'how to reuse previous state fields in flutter bloc pattern?

have the following. using sliding_up_panel that has body with messages and with a different view in panel content that pops up on click action from bottom bar.

  @override
  Stream<AppState> mapEventToState(
    AppEvent event,
  ) async* {
    if (event is LoadChat) {
      List<Msg> msgs = await Api.getMessages();
      yield LoadedChat(messages: msgs);
    } else if (event is OrderPanelOpen) {
      yield OpenedPanelState();
    } else if (event is OrderPanelClose) {
      yield ClosedPanelState();
    }
  }

Goal is to hide the appBar when panel is opened. appBar is present in AppLayout which is parent holding the SlidingUpPanel widget itself in a Scaffold.

class _AppLayoutState extends State<AppLayout> {
  @override
  Widget build(BuildContext context) {
    var bloc = BlocProvider.of<AppBloc>(context);
    return Container(
      child: Scaffold(
        appBar: widget.showAppBar
            ? AppBar(...)
            : null,
        bottomNavigationBar: BottomAppBar(...),
        body: SlidingUpPanel(...),
      ),
    );
  }
}

following is the action that adds panel events to bloc

 IconButton(
   icon: Icon(Icons.description),
   onPressed: () {
     if (widget.pannelCtrl.isPanelClosed()) {
       widget.pannelCtrl.open();
       bloc.add(OrderPanelOpen());
     } else {
       widget.pannelCtrl.close();
       bloc.add(OrderPanelClose());
     }
 })

problem here is SlidingUpPanel has a body that needs to show messages regardless of panel open or close. If panel open and close events are mapped to states with bloc, these open and close events has to be separate states but messages from current state has to be passed to new state by either as constructor params to new state or other ways. is that right approach to achieve this or is there anything else cleaner that I'm missing here.

class ClosedPanelState implements LoadedChat {
  final messagesArg;
  ClosedPanelState({this.messagesArg});

  @override
  Widget get currentView => Chat(messages: this.messagesArg);

  @override
  List<Object> get props => [];

  @override
  bool get showAppBar => true;

  @override
  String get title => 'Order Food';

  @override
  List<Msg> get messages => messages;
}


Solution 1:[1]

According to your question's title there's a thing you can do.

For your bloc class you can create a stack-like variable (you can use your own implementation of a LIFO stack or use a List, as you wish). Your bloc class must be Singleton in order to save the Events.

static final List<AppEvent> _events = new List<AppEvent>();

And on your _mapEventToState implementation add the events called to the variable:

@override
  Stream<AppState> mapEventToState(
    AppEvent event,
  ) async* {
    _events.add(event); // New Line
    if (event is LoadChat) {
      List<Msg> msgs = await Api.getMessages();
      yield LoadedChat(messages: msgs);
    } else if (event is OrderPanelOpen) {
      yield OpenedPanelState();
    } else if (event is OrderPanelClose) {
      yield ClosedPanelState();
    }
  }

Later you write a method for calling the last Event on the Stack/List.

dispatchPreviousState() {
    this.add(_events.removeLast());
  }

This worked for me to get the latest Event called and dispatching it again.

Solution 2:[2]

You can use reversible_bloc to add this behaviour to your bloc / cubit.

Ex.:

class MyReversibleCubit extends Cubit<int> with ReversibleBlocMixin {
  MyReversibleCubit(int initialState) : super(initialState);

  void changeValue(int newValue) => emit(newValue);
}

Then, when you need to rollback, use revert()

final myReversibleCubit = context.read<MyReversibleCubit>();
myReversibleCubit.changeValue(2);
myReversibleCubit.changeValue(4);
// Current state is 4
myReversibleCubit.revert();
// Current state back to 2

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Andrés Escobar Pérez
Solution 2 Guilherme Gabanelli