Working with Flutter Bloc

ref – https://pub.dev/packages/flutter_bloc

Events

Events are triggered due to some user action from the UI layer. Events can be represented via hierarchy by using abstract classes.

For example, in the app we are going to build today, there are three events, FetchWeatherEvent, ResetWeatherEvent, RefreshWeatherEvent. They all extend from WeatherEvents.

They are triggered in WeatherView. In our UI, say we have a handler called onRefresh. When the refresh button is clicked, it will execute it like so:

Notice that it uses BlocProvider to add our Event to the event stream’s sink. This is essentially what’s happening when we call BlockProvider.of(context).add(…).

RefreshWeather extends from WeatherEvents, as are other classes.
Thus, on the UI layer, we put events on the event stream in such ways.
When the event arrives on the event stream, we have BlocObserver.

The idea is that we pass the Bloc and Event into our BlocObserver.
BlocObserver will then call weatherBloc’s mapEventToState with this particular event.

mapEventToState is an abstract function in our weatherBloc, which yield states to this particular event. Yielding state means we are sending the state in the counter stream back to the UI layer.

Notice that we await a weather repo to get the weather from a web API. Then we have received it, we just yield the state class WeatherLoaded with the weather data. Of course we can yield instances of other state classes that implements the State abstract class, which is used to put up say an indicator or stop an indicator.

weatherState.dart

Notice WeatherLoaded has a weather property that holds on to this new data.
We also override props because our abstract class WeatherStates extends Equatable.
In order to see if this instance is equal to another, we need to put our weather object in the props array
so Equatable can do the comparison

Okay, so now from the WeatherBloc, we have yielded the WeatherLoaded instance along with the weather data, now what?

We come back to simpleBlocObserver and hit the onTransition.

SimpleBlocObserver.dart

So this means that we’re currently on the WeatherEmpty state
The event passed in is fetchWeather
Because Event FetchWeather, weatherBloc yields WeatherLoading…that is why our nextState is WeatherLoading.

When the data arrives the state will be WeatherLoaded. But first! Let’s see what happens to the UI when we our state is on WeatherLoading.

In our weather.dart, our BlocConsumer’s listener will be activated.

BlocConsumer

BlocConsumer exposes a builder and listener in order react to new states.

BlocConsumer is analogous to a nested BlocListener and BlocBuilder but reduces the amount of boilerplate needed.

BlocConsumer should only be used when it is necessary to both rebuild UI and execute other reactions to state changes in the bloc.

BlocConsumer takes a required BlocWidgetBuilder and BlocWidgetListener and an optional bloc, BlocBuilderCondition, and BlocListenerCondition.

If the bloc parameter is omitted, BlocConsumer will automatically perform a lookup using BlocProvider and the current BuildContext.

in our case, look aa builder property. In it, its if-else has a condition where if state is WeatherLoading, then it returns an instance of a Center Widget. In it, it has a CircularProgressIndicator which will then run on the UI.

weather.dart

So now our circular indicator is running, we’re also fetching data from the web api.

weatherBloc.dart

Once the data comes back, we yield WeatherLoaded with the data. The BlocConsumer’s state changed to WeatherLoaded and it simply updates the UI.

In our particular case, we update our refresh control, and give it a ListView where it calls DisplayWeather to put all the text and new data onto the screen.