unice
unice

Reputation: 2842

When to create new bloc?

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

Answers (1)

hman_codes
hman_codes

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

Related Questions