title: Flutter BlocListener executed only once even after event gets re-fired
body: <p>I am implementing <a href="https://www.youtube.com/watch?v=dc3B_mMrZ-Q&amp;list=PLB6lc7nQ1n4iYGE_khpXRdJkJEp9WOech" rel="noreferrer">Reso Coder's clean architecture</a> in flutter. I followed his guides in dividing the project to layers and using dependency injection. In one of the cases I want to have the following scenario: An administrator user logs in, sees data on their home screen, edits it and by pressing a button, saves the data to the local db (sqflite). Upon saving the data I want to show a <code>Snackbar</code> with some sort of text "Settings saved!" for example. Here's my code (parts):</p>
  
  <pre><code>class AdministratorPage extends StatefulWidget {
    @override
    _AdministratorPageState createState() =&gt; _AdministratorPageState();
  }
  
  class _AdministratorPageState extends State&lt;AdministratorPage&gt; {
    Widget build(BuildContext context) {
      return Scaffold(
        appBar: AppBar(
          backgroundColor: Theme.of(context).backgroundColor,
          centerTitle: true,
          leading: Container(),
          title: Text(AppLocalizations.of(context).translate('adminHomeScreen')),
        ),
        body: SingleChildScrollView(
          child: buildBody(context),
        ),
      );
    }
  
    BlocProvider&lt;SettingsBloc&gt; buildBody(BuildContext context) {
      return BlocProvider(
        create: (_) =&gt; serviceLocator&lt;SettingsBloc&gt;(),
        child: BlocListener&lt;SettingsBloc, SettingsState&gt;(
          listener: (context, state) {
            if (state is SettingsUpdatedState) {
              Scaffold.of(context).showSnackBar(
                SnackBar(
                  content: Text(
                      AppLocalizations.of(context).translate('settingsUpdated')),
                  backgroundColor: Colors.blue,
                ),
              );
            }
          },
          child: Column(
            children: &lt;Widget&gt;[
              SizedBox(
                height: 20.0,
              ),
              AdministratorInput(),
              SizedBox(
                width: double.infinity,
                child: RaisedButton(
                  child: Text('LOG OUT'),
                  onPressed: () {
                    serviceLocator&lt;AuthenticationBloc&gt;().add(LoggedOutEvent());
                    Routes.sailor(Routes.loginScreen);
                  },
                ),
              ),
            ],
          ),
        ),
      );
    }
  }
  </code></pre>
  
  <p>Here's the <code>AdministratorInput</code> widget:</p>
  
  <pre><code>class AdministratorInput extends StatefulWidget {
    @override
    _AdministratorInputState createState() =&gt; _AdministratorInputState();
  }
  
  class _AdministratorInputState extends State&lt;AdministratorInput&gt; {
    String serverAddress;
    String daysBack;
    final serverAddressController = TextEditingController();
    final daysBackController = TextEditingController();
  
    @override
    Widget build(BuildContext context) {
      return Center(
        child: Padding(
          padding: const EdgeInsets.all(10.0),
          child: BlocBuilder&lt;SettingsBloc, SettingsState&gt;(
            builder: (context, state) {
              if (state is SettingsInitialState) {
                BlocProvider.of&lt;SettingsBloc&gt;(context)
                    .add(SettingsPageLoadedEvent());
              } else if (state is SettingsFetchedState) {
                serverAddressController.text =
                    serverAddress = state.settings.serverAddress;
                daysBackController.text =
                    daysBack = state.settings.daysBack.toString();
              }
  
              return Column(
                children: &lt;Widget&gt;[
                  Container(
                    child: Row(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: &lt;Widget&gt;[
                        Text(AppLocalizations.of(context)
                            .translate('serverAddress')),
                      ],
                    ),
                  ),
                  Container(
                    height: 40.0,
                    child: TextField(
                      controller: serverAddressController,
                      decoration: InputDecoration(
                        border: OutlineInputBorder(),
                      ),
                      onChanged: (value) {
                        serverAddress = value;
                      },
                    ),
                  ),
                  SizedBox(
                    height: 5.0,
                  ),
                  // Days Back Text Field
                  Container(
                    child: Row(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: &lt;Widget&gt;[
                        Text(AppLocalizations.of(context).translate('daysBack')),
                      ],
                    ),
                  ),
                  Container(
                    height: 40.0,
                    child: TextField(
                      controller: daysBackController,
                      decoration: InputDecoration(
                        border: OutlineInputBorder(),
                      ),
                      onChanged: (value) {
                        daysBack = value;
                      },
                    ),
                  ),
                  SizedBox(
                    width: double.infinity,
                    child: RaisedButton(
                      child: Text('SAVE CHANGES'),
                      onPressed: updatePressed,
                    ),
                  ),
                  SizedBox(
                    width: double.infinity,
                    child: RaisedButton(
                      child: Text('REFRESH'),
                      onPressed: refreshPressed,
                    ),
                  ),
                ],
              );
            },
          ),
        ),
      );
    }
  
    void updatePressed() {
      BlocProvider.of&lt;SettingsBloc&gt;(context).add(
        SettingsUpdateButtonPressedEvent(
          settings: SettingsAggregate(
            serverAddress: serverAddress,
            daysBack: int.parse(daysBack),
          ),
        ),
      );
    }
  
    void refreshPressed() {
      BlocProvider.of&lt;SettingsBloc&gt;(context).add(
        SettingsRefreshButtonPressedEvent(),
      );
    }
  }
  </code></pre>
  
  <p>The SettingsBloc is a standard bloc pattern with events and states and a mapper method. It is being injected using <code>get_it</code> package. Here's how is instantiated:</p>
  
  <pre><code>serviceLocator.registerFactory(
      () =&gt; SettingsBloc(
        pullUsersFromServerCommand: serviceLocator(),
        getSettingsQuery: serviceLocator(),
        updateSettingsCommand: serviceLocator(),
      ),
    );
  </code></pre>
  
  <p>All instances of the commands and query for the constructor of the bloc are instantiated properly the same way.</p>
  
  <p>Here's the bloc:</p>
  
  <pre><code>class SettingsBloc extends Bloc&lt;SettingsEvent, SettingsState&gt; {
    final PullUsersFromServerCommand pullUsersFromServerCommand;
    final UpdateSettingsCommand updateSettingsCommand;
    final GetSettingsQuery getSettingsQuery;
  
    SettingsBloc({
      @required PullUsersFromServerCommand pullUsersFromServerCommand,
      @required UpdateSettingsCommand updateSettingsCommand,
      @required GetSettingsQuery getSettingsQuery,
    })  : assert(pullUsersFromServerCommand != null),
          assert(updateSettingsCommand != null),
          assert(getSettingsQuery != null),
          pullUsersFromServerCommand = pullUsersFromServerCommand,
          updateSettingsCommand = updateSettingsCommand,
          getSettingsQuery = getSettingsQuery;
  
    @override
    SettingsState get initialState =&gt; SettingsInitialState();
  
    @override
    Stream&lt;SettingsState&gt; mapEventToState(SettingsEvent event) async* {
      if (event is SettingsPageLoadedEvent) {
        final getSettingsEither = await getSettingsQuery(NoQueryParams());
  
        yield* getSettingsEither.fold((failure) async* {
          yield SettingsFetchedFailureState(error: "settingsDatabaseError");
        }, (result) async* {
          if (result != null) {
            yield SettingsFetchedState(settings: result);
          } else {
            yield SettingsFetchedFailureState(
                error: "settingsFetchFromDatabaseError");
          }
        });
      } else if (event is SettingsUpdateButtonPressedEvent) {
        final updateSettingsEither = await updateSettingsCommand(
            UpdateSettingsParams(settingsAggregate: event.settings));
  
        yield* updateSettingsEither.fold((failure) async* {
          yield SettingsUpdatedFailureState(error: "settingsDatabaseError");
        }, (result) async* {
          if (result != null) {
            yield SettingsUpdatedState();
          } else {
            yield SettingsUpdatedFailureState(
                error: "settingsUpdateToDatabaseError");
          }
        });
      } else if (event is SettingsRefreshButtonPressedEvent) {
        final pullUsersFromServerEither =
            await pullUsersFromServerCommand(NoCommandParams());
  
        yield* pullUsersFromServerEither.fold((failure) async* {
          yield SettingsRefreshedFailureState(
              error: "settingsRefreshDatabaseError");
        }, (result) async* {
          if (result != null) {
            yield SettingsUpdatedState();
          } else {
            yield SettingsRefreshedFailureState(error: "settingsRefreshedError");
          }
        });
      }
    }
  }
  </code></pre>
  
  <p>The first time I enter this screen everything works perfect. The data is fetched from the database, loaded on screen and if I change it and press SAVE, it shows the <code>snackbar</code>. My problem is if I want to edit the data again while staying on that screen. I edit it again, therefore fire the changing event, the bloc gets it, calls the proper command below and the data is saved in the database. Then the state of the bloc is changed in attempt to tell the UI, "hey, I have a new state, get use of it". But the <code>BlocListener</code> never gets called again.</p>
  
  <p>How should I achieve the behavior I desire?</p>
  
  <p><strong>EDIT:</strong>
  I am adding another bloc I am using earlier in the App where I log in users. The Login Page utilizes that bloc and upon wrong username or password, I am showing a snackbar, clearing the input fields and leaving the page ready for more. If I try again with wrong credentials, I can see the snackbar again.</p>
  
  <p>Here is the LoginBloc:</p>
  
  <pre><code>class LoginBloc extends Bloc&lt;LoginEvent, LoginState&gt; {
    final AuthenticateUserCommand authenticateUserCommand;
    final AuthenticationBloc authenticationBloc;
  
    LoginBloc({
      @required AuthenticateUserCommand authenticateUserCommand,
      @required AuthenticationBloc authenticationBloc,
    })  : assert(authenticateUserCommand != null),
          assert(authenticationBloc != null),
          authenticateUserCommand = authenticateUserCommand,
          authenticationBloc = authenticationBloc;
  
    @override
    LoginState get initialState =&gt; LoginInitialState();
  
    @override
    Stream&lt;LoginState&gt; mapEventToState(LoginEvent event) async* {
      if (event is LoginButtonPressedEvent) {
        yield LoginLoadingState();
  
        final authenticateUserEither = await authenticateUserCommand(
            AuthenticateUserParams(
                username: event.username, password: event.password));
  
        yield* authenticateUserEither.fold((failure) async* {
          yield LoginFailureState(error: "loginDatabaseError");
        }, (result) async* {
          if (result != null) {
            authenticationBloc.add(LoggedInEvent(token: result));
            yield LoginLoggedInState(result);
          } else {
            yield LoginFailureState(error: "loginUsernamePasswordError");
          }
        });
      }
    }
  }
  </code></pre>
  
  <p>The <code>Event</code> and <code>State</code> classes here extend <code>Equatable</code>. And since it was working according to the expectations, I did it the same way in the Settings Page (where it failed). From the UI I raise the <code>LoginButtonPressedEvent</code> as many times as I want and the <code>BlocListener</code> gets called respectively.</p>