Getting Started With the BLoC Pattern

See how to use the popular BLoC pattern to build your Flutter app architecture and manage the flow of data through your widgets using Dart streams. By Sardor Islomov.

5 (5) · 3 Reviews

Download materials
Save for later
Share
Update note: Sardor Islomov updated this tutorial for Flutter 2.10. Brian Kayfitz wrote the original.

How to design the structure of an app is among the most heavily debated topics that arise in app development. Everyone seems to have their favorite architectural pattern with a fancy acronym.

iOS and Android developers are well versed in Model-View-Controller (MVC) and have used this pattern as a default choice when building apps. The model and view are separated, with the controller sending signals between them.

But Flutter brings a new, reactive style that’s not entirely compatible with MVC. A variation of this classical pattern has emerged from the Flutter community: BLoC.

BLoC stands for Business Logic Components. The gist of BLoC is that everything in the app should be represented as a stream of events: Widgets submit events, and other widgets will respond. BLoC sits in the middle, managing the conversation. Dart even comes with syntax for working with streams baked into the language!

The best part about this pattern is you won’t need to import any plugins or learn any custom syntax. Flutter already comes with everything you need.

In this tutorial, you’ll create an app to find articles using an API provided by raywenderlich.com. At the tutorial’s end, the app will do the following:

  1. Wrap API calls with the BLoC pattern
  2. Search for articles and show the results asynchronously
  3. Display detail of article

Getting Started

Download the starter project by clicking the Download Materials button and open it up with your favorite IDE. This tutorial will use Android Studio, but you can also use Visual Studio Code. Make sure to run flutter packages get, either at the command line or when prompted by your IDE, to pull down the latest version of the HTTP package.

Note: Android Studio can show you the error message Dart SDK is not configured. To solve this, click Open Dart settings, switch to the Flutter section in the menu and choose Flutter SDK path

The starter project contains some basic models and networking files. When you open up the project it should look like this.

Starter project Structure

As you can see, rw_client.dart handles communicating with Raywenderlich servers. article.dart contains the Article model class, which represents article objects from the API response.

You’ll learn more about the Raywenderlich API platform and network communication in the next section.

Raywenderlich API

The tutorial uses the Raywenderlich (RW) API to display a list of articles. You can check the data structure of the article model at raywenderlich.docs.apiary.io.

In this tutorial, you don’t need any setup to integrate RW API into the app. The tutorial already includes all necessary classes to work with RW API. The RWClient class in rw_client.dart uses the http library to make network requests. That is nothing complex.

Building the Application Architecture

When writing apps, whether using Flutter or another framework, it’s important to organize classes into layers. This is an informal convention; it’s not something concrete you can see in the code.

Each layer, or group of classes, handles one general task. The starter project comes with a folder called data. The data layer manages the app’s models and the connections to the back end; it knows nothing about the UI.

Every project is slightly different. But in general, you’ll want to build something like this:

BLoC Layers

This architectural contract is not too unlike classical MVC. The UI/Flutter layer can only talk to the BLoC layer. The BLoC layer receives input events, processes business logic using the data layer and responds with output events to the UI layers or other listeners. This structure can scale nicely as the app grows.

Anatomy of a BLoC

The BLoC Pattern is just an interface around Dart streams:

BLoC Diagram

dart:async provides an object called StreamController. StreamControllers are manager objects that instantiate both a stream and a sink. A sink is the opposite of a stream. You add data to the StreamController using Sink and listen with the Stream.

In the Bloc architecture, you use Stream with Future most of the time. dart:async provides both of them. A Future represents a potential value, or error, that will be available sometime in the future. Future uses async and await keywords to perform asynchronous operations in Dart.

Future tells you when the result is ready. A Stream is a sequence of asynchronous events.

To summarize, BLoCs are objects that process and store business logic, use sinks to accept input and provide output via streams.

Enough theory. It’s time to build some screens with the appropriate BLoC.

Article List Screen

To search articles, you need to display a list of articles. In this section, you’ll create a simple screen with a search field at the top and a list view to show the results.

In the lib/ui folder of the project, create a file called article_list_screen.dart. Add a StatelessWidget named ArticleListScreen to the file:

class ArticleListScreen extends StatelessWidget {

  const ArticleListScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    // 1
    return Scaffold(
      appBar: AppBar(
          title: const Text(
              'Articles'
          )
      ),
      body: Column(
        children: [
          Padding(
            padding: const EdgeInsets.all(16),
            child: TextField(
              decoration: const InputDecoration(
                  border: OutlineInputBorder(), hintText: 'Search ...'),
              onChanged: (query) {
                   // 2
              },
            ),
          ),
          Expanded(
            // 3
            child:_buildResults(),
          )
        ],
      ),
    );
  }

  Widget _buildResults() {
    return const Center(child: Text('No Results'));
  }
}
Note: Your IDE will show errors when classes that aren’t imported are typed. To fix this, move your cursor over any symbol with a red underline and press Option+Return on macOS (Alt+Enter on Windows/Linux) or click the red light bulb. This brings up a menu where you can select the correct file to import.

The code above does the following:

  1. You create a screen with a Scaffold widget, which contains standard widgets such as: AppBar, Column and Expanded.
  2. The TextField widget triggers the onChange{} method when the user types keywords. In the next sections of this tutorial, you’ll update the onChange{} method to submit keywords to BLoC.
  3. _buildResults() builds the list with Article objects. Right now, it shows only the message “No Results”. You’ll change it later in the tutorial.

If you build and run the app, it displays an empty, black screen. This is because main.dart doesn’t have a reference to ArticleListScreen. Go to the ArticleFinder class in main.dart and change the home: property to return the new screen:

...
return MaterialApp(
      title: 'RW Finder',
      home: const ArticleListScreen(),
...

Build and run the app. It should look like this:

Article list screen

That’s better, but it still doesn’t do anything. It’s time to create some BLoCs!