We will go over the three questions to start using the Bloc, —
1. How to make Bloc available to the Widget tree (UI)?
The Widget which gives this functionality is the BlocProvider widget, BlocProvider makes the bloc available to the entire Widget tree, which is passed in the child
parameter.
In the above code, we create a new instance of the LoginBloc
and as a child, we pass the instance of our LoginScreen
so Bloc Provider makes the LoginBloc
available to the Widget tree of LoginScreen
Bloc Provider automatically handles the closing of the Bloc.
2. How to trigger an Event from UI on some action?
Now that we have Bloc available on the Widget tree we can access it from anywhere, there are two ways to do that, —
a. Using context,
context.read<BlocA>();
b. Using Bloc Provider,
BlocProvider.of<BlocA>(context)
But what is the difference? When should we use which one?
There is actually not much difference both are almost the same, but the first one is defined as an extension on the BuildContext
, so it’s easier to use. Proceed with whichever you like.
Let’s add a button tap event on the Bloc, —
Note: — It’s important to pass the correct type of the Bloc, because it tries to lookup the tree to find the instance of that type, you can read more on this here.
Coming back to the event, see how easy its to trigger an event from UI, we just call the add
method on the Bloc, and pass an instance of an event.
Now we need to go back to the Bloc and check if we have registered this event which is sent from the UI, on receiving this we will emit a State with a String to show on the UI, you can refer to the Bloc code attached above , just uncomment all the code with the comment “will be used later”. Great that’s it.
3. But how will UI update itself when a new state is Emitted?
Yes, you guessed that right, there is a widget for this too. It’s called Bloc Consumer.
Let’s go over the BlocConsumer
though the above example contains the entire LoginScreen
but for now, you need to focus only on _buildScaffoldBody
function.
If you notice the BlocConsumer
have four arguments let’s go over them one by one,
builder
— It rebuilds the widget tree in response to the state changes, which means whenever you emit a new state it will rebuild the widgets so that you can use the value sent in the state that is emitted from the Bloc. So in this example, we have passed a text from the bloc in the UpdateTextState
and used it to so on the UI, this state is emitted whenever the “Tap me!!” button is tapped.
listener
— So whenever a state is emitted the listener also gets invoked, but unlike builder, it doesn’t return any widget. It gets called once per state change, that’s why we generally use a listener that happens once whenever the state changes, like showing a snack bar, dialog, bottom sheet, or navigating to the next screen.
buildWhen
— This is an optional parameter that provides previous and current states and returns a boolean, so if we return true it will call the builder, or else it won’t if you don’t use this parameter the builder will be called for each state change.
listenWhen
— This is similar to buildWhen
but used to control the Listener, if it returns true then the listener gets called.
So in our example, we are using Listener for ShowSnackBar
state, which shows a snack bar with a text to display when the button is tapped. and we are using builder for LoginInitial
, UpdateTextState
which updates the text on the screen when a button is tapped.
Another interesting point to note is that BlocConsumer
takes in an optional parameter bloc, if you don’t provide it, it will try to lookup using BlocProvider
and the current BuildContext
. So if you missed injecting the Bloc (Question 1) it will throw an exception.
There will be cases where you either want to use builder
or listener
for such cases there are different BlocBuilder
and BlocListener
widgets, feel free to explore them, also Flutter blocs provide some other helpful widgets but that’s for some other day.