什么时候创建新的集团?
When to create new bloc?
我还在学习集团模式。我使用一个 bloc 创建了两个页面,ViewPage 和 DetailsPage。
这是我的集团:
getRecordEvent
deleteRecordEvent
LoadedState
LoadedErrorState
DeletedState
DeletedErrorState
查看页面只会构建一个包含 LoadedState
上的记录列表的小部件。当用户点击任何记录时,它会推送详细信息页面并显示带有删除按钮的详细记录。当用户按下删除按钮时,我会监听 DeletedState
并调用 getRecord
事件以使用更新后的记录再次填充视图页面。
它一切正常,但我的问题是当我在删除记录时遇到错误。当状态为 DeleteErrorState
时,我的视图页面变为空,因为我没有在那里调用 getRecord
因为错误可能是互联网连接并且将显示两个错误对话框。一个用于 DeletedErrorState
和 LoadedErrorState
.
我知道这是 bloc 的默认行为。我是否必须创建一个只有 deleteRecordEvent
的单独集团?还有,如果我创建一个新页面来添加记录,这也会是一个单独的集团吗?
更新:
这是 ViewPage 的示例。按下按钮后,DetailsPage 只会调用 deleteRecordEvent。
ViewPage.dart
void getRecord() {
BlocProvider.of<RecordBloc>(context).add(
getRecordEvent());
}
@override
Widget build(BuildContext context) {
return
Scaffold(
body: buildBody(),
),
);
}
buildBody() {
return Padding(
padding: const EdgeInsets.all(8.0),
child: BlocConsumer<RecordBloc, RecordState>(
listener: (context, state) {
if (state is LoadedErrorState) {
showDialog(
barrierDismissible: false,
context: context,
builder: (_) {
return (WillPopScope(
onWillPop: () async => false,
child: ErrorDialog(
failure: state.failure,
)));
});
} else if (state is DeletedState) {
Navigator.pop(context);
getRecord();
} else if (state is DeletedErrorState) {
Navigator.pop(context);
showDialog(
barrierDismissible: false,
context: context,
builder: (_) {
return (WillPopScope(
onWillPop: () async => false,
child: ErrorDialog(
failure: state.failure,
)));
});
}
},
builder: (context, state) {
if (state is LoadedState) {
return Expanded(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
state.records.length <= 0
? noRecordWidget()
: Expanded(
child: ListView.builder(
shrinkWrap: true,
itemCount: state.records.length,
itemBuilder: (context, index) {
return Card(
child: Padding(
padding: EdgeInsets.symmetric(
vertical: Sizes.s8),
child: ListTile(
title: Text(state.records[index].Name),
subtitle: state.records[index].date,
onTap: () {
showDialog(
barrierDismissible: false,
context: context,
builder: (_) {
return BlocProvider<RecordBloc>.value(
value: BlocProvider.of<RecordBloc>(context),
child: WillPopScope(
onWillPop: () async => false,
child:
DetailsPage(record:state.records[index]),
));
});
},
),
));
}),
),
],
),
);
}
return (Container());
},
),
),
);
}
关于集团
根据一般经验,每个 ui
需要一个 bloc
。当然,情况并非总是如此,因为这取决于几个因素,其中最重要的是您在 ui
中处理了多少事件。对于您的情况,如果有一个 ui
将项目列表保存到项目详细信息 ui
中,我将创建两个 blocs
。一个将只处理加载项目(例如 ItemsBloc
),另一个将处理对单个项目的操作(SingleItemBloc
)。我现在可能只使用 delete
事件,但随着应用程序的增长,我将添加更多事件。这一切都有助于 Separation of Concerns 概念。
将其应用于您的情况,SingleItemBloc
将处理删除、修改、订阅等单个项目,而 ItemsBloc
将处理从不同存储库加载项目(local/remote).
因为我没有你的代码 bloc
我不能提供任何修改。
针对您的情况的解决方案
每次发出新的 state
时,您似乎都会丢失项目列表的最新版本。您应该保留从存储库中获取的最后一个列表的本地副本。如果出现错误,您只需使用该列表即可;如果不只是将新列表保存为您拥有的最后一个列表。
class MyBloc extends Bloc<Event, State> {
.....
List<Item> _lastAcquiredList = [];
Stream<State> mapEventToState(Event event) async* {
try {
....
if(event is GetItemsEvent) {
var newList = _getItemsFromRepository();
yield LoadedState(newList);
_lastAcquiredList = newList;
}
....
} catch(err) {
yield ErrorState(items: _lastAcquiredItems);
}
}
}
我还在学习集团模式。我使用一个 bloc 创建了两个页面,ViewPage 和 DetailsPage。 这是我的集团:
getRecordEvent
deleteRecordEvent
LoadedState
LoadedErrorState
DeletedState
DeletedErrorState
查看页面只会构建一个包含 LoadedState
上的记录列表的小部件。当用户点击任何记录时,它会推送详细信息页面并显示带有删除按钮的详细记录。当用户按下删除按钮时,我会监听 DeletedState
并调用 getRecord
事件以使用更新后的记录再次填充视图页面。
它一切正常,但我的问题是当我在删除记录时遇到错误。当状态为 DeleteErrorState
时,我的视图页面变为空,因为我没有在那里调用 getRecord
因为错误可能是互联网连接并且将显示两个错误对话框。一个用于 DeletedErrorState
和 LoadedErrorState
.
我知道这是 bloc 的默认行为。我是否必须创建一个只有 deleteRecordEvent
的单独集团?还有,如果我创建一个新页面来添加记录,这也会是一个单独的集团吗?
更新:
这是 ViewPage 的示例。按下按钮后,DetailsPage 只会调用 deleteRecordEvent。
ViewPage.dart
void getRecord() {
BlocProvider.of<RecordBloc>(context).add(
getRecordEvent());
}
@override
Widget build(BuildContext context) {
return
Scaffold(
body: buildBody(),
),
);
}
buildBody() {
return Padding(
padding: const EdgeInsets.all(8.0),
child: BlocConsumer<RecordBloc, RecordState>(
listener: (context, state) {
if (state is LoadedErrorState) {
showDialog(
barrierDismissible: false,
context: context,
builder: (_) {
return (WillPopScope(
onWillPop: () async => false,
child: ErrorDialog(
failure: state.failure,
)));
});
} else if (state is DeletedState) {
Navigator.pop(context);
getRecord();
} else if (state is DeletedErrorState) {
Navigator.pop(context);
showDialog(
barrierDismissible: false,
context: context,
builder: (_) {
return (WillPopScope(
onWillPop: () async => false,
child: ErrorDialog(
failure: state.failure,
)));
});
}
},
builder: (context, state) {
if (state is LoadedState) {
return Expanded(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
state.records.length <= 0
? noRecordWidget()
: Expanded(
child: ListView.builder(
shrinkWrap: true,
itemCount: state.records.length,
itemBuilder: (context, index) {
return Card(
child: Padding(
padding: EdgeInsets.symmetric(
vertical: Sizes.s8),
child: ListTile(
title: Text(state.records[index].Name),
subtitle: state.records[index].date,
onTap: () {
showDialog(
barrierDismissible: false,
context: context,
builder: (_) {
return BlocProvider<RecordBloc>.value(
value: BlocProvider.of<RecordBloc>(context),
child: WillPopScope(
onWillPop: () async => false,
child:
DetailsPage(record:state.records[index]),
));
});
},
),
));
}),
),
],
),
);
}
return (Container());
},
),
),
);
}
关于集团
根据一般经验,每个 ui
需要一个 bloc
。当然,情况并非总是如此,因为这取决于几个因素,其中最重要的是您在 ui
中处理了多少事件。对于您的情况,如果有一个 ui
将项目列表保存到项目详细信息 ui
中,我将创建两个 blocs
。一个将只处理加载项目(例如 ItemsBloc
),另一个将处理对单个项目的操作(SingleItemBloc
)。我现在可能只使用 delete
事件,但随着应用程序的增长,我将添加更多事件。这一切都有助于 Separation of Concerns 概念。
将其应用于您的情况,SingleItemBloc
将处理删除、修改、订阅等单个项目,而 ItemsBloc
将处理从不同存储库加载项目(local/remote).
因为我没有你的代码 bloc
我不能提供任何修改。
针对您的情况的解决方案
每次发出新的 state
时,您似乎都会丢失项目列表的最新版本。您应该保留从存储库中获取的最后一个列表的本地副本。如果出现错误,您只需使用该列表即可;如果不只是将新列表保存为您拥有的最后一个列表。
class MyBloc extends Bloc<Event, State> {
.....
List<Item> _lastAcquiredList = [];
Stream<State> mapEventToState(Event event) async* {
try {
....
if(event is GetItemsEvent) {
var newList = _getItemsFromRepository();
yield LoadedState(newList);
_lastAcquiredList = newList;
}
....
} catch(err) {
yield ErrorState(items: _lastAcquiredItems);
}
}
}