Flutter Bloc – AUTOMATIC LOOKUP – v0.20 (and Up), Updated Tutorial

The Bloc library for Flutter is a life-saver for implementing the BLoC pattern in a simple and streamlined way without all the boilerplate. With over 2000 stars on GitHub, I think it's fair to say that this is the most widely used state management package for Flutter.

It's amazing to see that it is in constant development from its author. As new features are added though, the old ones are either deprecated or they stop working all together. In this tutorial, you are going to learn the newest best practices and ways of managing state with this awesome library.

Starter Project

Not so long ago I've made a tutorial on version 0.15 which was subsequently updated to work with 0.17. If you're just starting out with the library, learn from that tutorial first. To follow along in this tutorial, clone this GitHub repo containing the code from the tutorial below.

Works with Version 0.20 (+)

The version 0.20.0 brought with it a few sweet features which reduce the boilerplate even more than before. Prior to that, in version 0.19.0, flutter_bloc got integrated with the provider package! We're going to take a look at 

BlocProvider Accessibility

It is usually the case that an InheritedWidget can be accessed only inside a child Widget's build method. That's simply how Flutter works. Since BlocProvider is in essence only a glorified InheritedWidget, the same restrictions apply... until the version 0.20.0!

The Bloc instance provided by the BlocProvider is now accessible from within the same build method. I know this probably sounds too theoretical, so let's look at some actual code. You can now go from this:

main.dart

class WeatherPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Fake Weather App"),
      ),
      body: BlocProvider(
        builder: (context) => WeatherBloc(),
        // Child has to be a new widget
        child: WeatherPageChild(),
      ),
    );
  }
}

class WeatherPageChild extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      padding: EdgeInsets.symmetric(vertical: 16),
      alignment: Alignment.center,
      // BlocListener invokes the listener when new state is emitted.
      child: BlocListener(
        bloc: BlocProvider.of<WeatherBloc>(context),
        listener: (BuildContext context, WeatherState state) {
          ...
        },
        // BlocBuilder invokes the builder when new state is emitted.
        child: BlocBuilder(
          bloc: BlocProvider.of<WeatherBloc>(context),
          builder: (BuildContext context, WeatherState state) {
            ...
          },
        ),
      ),
    );
  }
  ...
}

To only a single widget without any intermediaries!

main.dart

class WeatherPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Fake Weather App"),
      ),
      body: BlocProvider(
        builder: (context) => WeatherBloc(),
        // No intermediary widget is needed now
        child: Container(
          padding: EdgeInsets.symmetric(vertical: 16),
          alignment: Alignment.center,
          child: BlocListener(
            bloc: BlocProvider.of<WeatherBloc>(context),
            listener: (BuildContext context, WeatherState state) {
              ...
            },
            child: BlocBuilder(
              bloc: BlocProvider.of<WeatherBloc>(context),
              builder: (BuildContext context, WeatherState state) {
                ...
              },
            ),
          ),
        ),
      ),
    );
  }
  ...
}

But there is one problem - the app doesn't work anymore. We get an exception saying "BlocProvider.of() called with a context that does not contain a Bloc of type WeatherBloc." Hmm... isn't this precisely the exception you get when you try to access any kind of an InheritedWidget from the same build method's BuildContext? Am I a liar with what I told you just a couple of paragraphs above?

You see, no matter how awesome the Bloc library is, it still cannot break the laws of Flutter. Accessing a Bloc instance using a BlocProvider is still not possible from within the same level on the widget tree. Yes, the BlocProvider still needs an ancestor widget.

Automatic Bloc Lookup

The thing is, with the version 0.20.0 and upwards, we can omit the Bloc instance from both the BlocListener and the BlocBuilder. This way we will no longer call BlocProvider.of() ourselves.

Instead, it will be called by the BlocListener or BlocBuilder internally and since they are separate widgets which are "one level downwards" in the widget tree, they will be able to access the Bloc instance from their BuildContext.

main.dart

class WeatherPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Fake Weather App"),
      ),
      body: BlocProvider(
        builder: (context) => WeatherBloc(),
        // No intermediary widget is needed now
        child: Container(
          padding: EdgeInsets.symmetric(vertical: 16),
          alignment: Alignment.center,
          // Bloc instance isn't directly specified, add type parameters instead
          child: BlocListener<WeatherBloc, WeatherState>(
            listener: (BuildContext context, WeatherState state) {
              ...
            },
            child: BlocBuilder<WeatherBloc, WeatherState>(
              builder: (BuildContext context, WeatherState state) {
                ...
              },
            ),
          ),
        ),
      ),
    );
  }
  ...
}

Provider Package Integration

While the provider package was integrated into flutter_bloc already in version 0.19.0, I feel like this is still not well known among most developers. There are some pretty cool implications stemming from this integration.

  1. You don't have to add another dependency to the pubspec.yaml file, since flutter_bloc now brings in provider automatically.
  2. Unified nomenclature is another benefit. The previously named BlocProviderTree is now called MultiBlocProvider. This goes well with the naming scheme of the provider package, which already has a MultiProvider widget.
  3. This cross-package integration will hopefully also show that provider is not a substitute for Bloc. Provider is really more geared to remove the boilerplate of InheritedWidget and it is perfect to use in conjunction with Bloc.

Conclusion

You've learned how to use the newest features of the flutter_bloc package. They will surely reduce the amount of boilerplate you write even more.

Matej Rešetár
 

Matej is an app developer with a knack for teaching others. If he's not programming, making tutorials or doing other business, he's mostly working out, listening to audiobooks and taking cold showers.

>