Reputation: 2842
I'm still learning bloc patter. I created two pages, ViewPage and DetailsPage using a single bloc. This is my bloc:
getRecordEvent
deleteRecordEvent
LoadedState
LoadedErrorState
DeletedState
DeletedErrorState
The view page will only build a widget with list of records on a LoadedState
. When the user taps any record, It will push the Details page and displays detailed record with a delete button. When user press the delete button, I listen to the DeletedState
and call the getRecord
event to populate the view page again with the updated record.
Its all working but my problem is when I encountered an error while deleting record. When the state is DeleteErrorState
, my view page becomes empty since I don't call getRecord
there because the error could be internet connection and two error dialog will be shown. One for the DeletedErrorState
and LoadedErrorState
.
I know this is the default behavior of bloc. Do I have to create a separate bloc with only deleteRecordEvent
? And also if I create a new page for adding record, will this also be a separate bloc?
UPDATE:
This is a sample of ViewPage. The DetailsPage will only call the deleteRecordEvent once the button was pressed.
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());
},
),
),
);
}
Upvotes: 2
Views: 1184
Reputation: 888
About bloc
As a general rule of thumb, you need one bloc
per ui
. Of course, this is not always the case, as it depends on a few factors, the most important of which is how many events are you handling in your ui
. For your case, where there is a ui
that holds a list of items into an item-details ui
, I would create two blocs
. One will only handle loading items (ItemsBloc
for instance), the other will handle actions to a single item (SingleItemBloc
). I might only use the delete
event for now, but as the app grows, I will be adding more events. This all facilitates the Separation of Concerns concept.
Applying that to your case, the SingleItemBloc
will handle deleting, modifying, subscribing, etc to a single item, while ItemsBloc
will handle loading the items from the different repositories (local/remote).
Since I don't have the code for your bloc
I can't offer any modifications.
Solution specific to your case
It seems that you're losing the last version of your list of items every time a new state
is emitted. You should keep a local copy of the last list you acquired from your repositories. In case there is an error, you just use that list; if not just save the new list as the last list you had.
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);
}
}
}
Upvotes: 2