Nefarious
Nefarious

Reputation: 516

Flutter Bloc state management

I am attempting to build a Flutter based tablet application. Because there is way more screen real estate on a tablet, each page is made up of a set of widgets built each using their own bloc implementation.

I am fairly new to flutter and bloc and the current design simply regenerates the widget tree for each state change that changes how the widget tree is generated. Maybe I don't understand, more then likely true, when the state change occurs, the bloc builder is to return a widget. I haven't seen an example where the page widget is saved and new states simply modify the saved widget.

Here is an example of a problem I am having. I am using a bloc observer to monitor the state of the blocks.

The current widget tree looks like:

LocaleTile [LocaleTileBloc, LocaleTileInit] 
    NeighborColumn [NeighborBloc, NeighborInit] 
    LocaleColumn [RegionBloc, RegionInit] 
    FamilyColumn [FamilyBloc, FamilyInit] 

The event to state mapping for the NewLocaleEvent looks like:

Stream<LocaleTileState> _mapNewLocaleToState(NewLocaleEvent event) async* {
    yield LocaleTileLaughing();
    yield NewLocaleState(event.locale);
  }

I send an event to the LocaleTileBloc -> NewLocaleEvent when the user presses on a locale button. The observer log is:

// Create NewLocaleEvent
: onEvent -- b: LocaleTileBloc, e: NewLocaleEvent: {Bellevue}

// Consume the NewLocaleEvent
: onTrans -- b: LocaleTileBloc, t: Transition { currentState: LocaleTileInit, 
                                                event: NewLocaleEvent: {Bellevue}, 
                                                nextState: NewLocalState: {Bellevue} }
: onChange -- c: LocaleTileBloc, c: Change { currentState: LocaleTileInit, 
                                             nextState: NewLocalState: {Bellevue} }
// The widget that implements the LocalTileBloc receives the new state
: RegionLocalTile[LocaleTileBloc] State: NewLocalState: {Bellevue}

If the user presses on a new Locale to show it, I get the following in the observer log:

// Create NewLocaleEvent Kirkland
: onEvent -- b: LocaleTileBloc, e: NewLocaleEvent: {Kirkland}

// Consume NewLocalState
// Where did this come from
: RegionLocalTile[LocaleTileBloc] State: NewLocalState: {Bellevue}

When the page widget is regenerated, it seems that the bloc tells the new RegionLocalTile widget that it has to go back to the old state, so the application does all of the api calls to regenerate the page, then:

: onTrans -- b: LocaleTileBloc, t: Transition { currentState: NewLocalState: {Bellevue}, 
                                                event: NewLocaleEvent: {Kirkland}, 
                                                nextState: LocaleTileLaughing }
: onChange -- c: LocaleTileBloc, c: Change { currentState: NewLocalState: {Bellevue}, 
                                             nextState: LocaleTileLaughing }

: onTrans -- b: LocaleTileBloc, t: Transition { currentState: LocaleTileLaughing, 
                                                event: NewLocaleEvent: {Kirkland}, 
                                                nextState: NewLocalState: {Kirkland} }
: onChange -- c: LocaleTileBloc, c: Change { currentState: LocaleTileLaughing, 
                                             nextState: NewLocalState: {Kirkland} }
: RegionLocalTile[LocaleTileBloc] State: NewLocalState: {Kirkland}

The widget page then gets the correct state.

I think my question is: When the widget that implements a bloc responds to a state change, it has to return a widget. There are two choices, either regenerate the widget or save it in a statefull widget and create keys to modify specific sub widgets.

If I regenerate it each state change, then each time the widget comes up, it is reminded of its old state, and has to redo all of the work it just left. I wish I could just reset the bloc to go back to the initial state.

Upvotes: 0

Views: 800

Answers (1)

Nefarious
Nefarious

Reputation: 516

How I decided to solve this problem is to save the widgets that are pass through, the ones that will not change ui based on state change, and just reinsert them back into widget tree. The widgets that do change ui generate new widget and the new parts are inserted into tree. This change saves multiple duplicate api calls as the blocs don't have to tell the widget to reset it's state to the last state, then go to the new state:

Generally the parent has state looking like:

class _RegionState extends State<RegionPage> {

    LocaleTile localeTile;

    @override
    void initState() {
      super.initState();
      localeTile = null;
    }

    Widget getWidget(BlocState state) {
      if (localeTile == null) {
        localeTile = new LocaleTile(state);
      }
      return Center( child: localeTile);
    }

    @override
    Widget build(BuildContext context) {
      return BlocBuilder<EntityBloc, EntityState>(builder: (context, state) {
          if (state is EntityLoaded) {
            ...
            // computeSomething();
            // Send events to the localTileBloc
          }
          return getWidget(state);
      });
    }

    

Upvotes: 1

Related Questions