When using the BLoC pattern to isolate business logic from the view (without the bloc library), how can we trigger a one-time UI call (like showing a snackbar or an alert dialog) when some asynchronous logic has finished in a BLoC? For example, if a network request fails, we want to show a dialog informing the user that some data cannot be fetched.
The options I've explored are:
Use a
StreamBuilder
in the widget's build method to listen for an event from the bloc, and show the dialog in the StreamBuilder's build method. The problem with this approach is that theStreamBuilder
'sbuild
method would not actually be building anything here. We'd just be asking Flutter to show a dialog. We could return some invisible "fake" widget just to make Flutter happy, but this is really hacky.Make the widget stateful. In
initState
, subscribe to a stream of errors from the BLoC, and show the dialog in the listener. Here we maintain the decoupling of logic/view, but as far as I can tell, this option is not even viable because we do not have theBuildContext
needed to show the dialog.When the widget invokes some logic on the BLoC which might result in a change that should trigger a dialog, pass in a callback which shows the dialog. Of the options listed here, I prefer this the one. Logic and view are still pretty separate, but it feels a little odd. The flow of data in the BLoC pattern is typically purely "widget -> BLoC via sinks or functions" and "BLoC -> widget via streams". It's a violation of this pattern to have the widget ask the BLoC to invoke a callback (passing data back with it) after some logic has executed.
I've been avoiding using the bloc library (to keep it simple, among other reasons). However, it does seem to offer a solution to this problem in the form of its BlocListener
. Is there a simple way to solve this problem while maintaining the core principles of BLoC?