Flutter Build Apps using Bloc Pattern
By: Gianni Deplano
Tags: Software Architecture, Mobile Development, App Development, Flutter

Let’s start by explaining the App and the detail of the case implemented.

The HomePage is the entry point of the App and shows a list of news that comes from a web service. IT will be able to show different contents depending on the “state” of the loading. For instance, when is empty, loading, or loaded.

When the application is launched, the contents are empty and an image and a button will be displayed. On tap, the App invokes a Webservice that fetches the contents and displays a scrollable list. On click on each item, the App simply navigates on the detail page.

On this page, the user is able to share the content on social networks, depending on what is currently installed on the device.

So, very easy use case but very replicable in the real life to study and experiment with the BLoC architectural pattern.

Like other programming patterns like MVC, MVVM, MVP, BLoC has the aim to simplify and separate the logical parts of an application. The right separation of layers and the right architecture allows not only to create more complex and maintainable applications but also to be fast in the development and in the evolution of the codebase.

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; other widgets will respond. BLoC sits in the middle, managing the conversation. Dart even comes with the syntax for working with streams that are baked into the language. So the App is logically a final state machine that declares states and events during the lifecycle and the state change has the consequence to interact with the views (or part of the views) to show something different.

The App is divided into three main layers logically explained in the picture:

The Data Layer defined the networking part and the models, so in this part, we’ll implement the calls to web services instead of database interactions.

The BLoC layer defined the state machine in terms of the declaration of states and events. The communication with the frontend is implemented with a bidirectional stream that allows the communication with the different layers of the architecture.

On top, the frontend layer implements the experience and the widgets to be displayed.

This architectural contract is not too dissimilar from classical MVC. The Frontend layer can only talk to the BLoC layer. The BLoC layer sends events to the data and UI layers and processes business logic. This structure can scale nicely as the app grows.

The dart:async package provides Streams. The stream is like a Future, but instead of returning a single value asynchronously, streams can yield multiple values over time. If a Future is a value that will be provided eventually, a stream of a series of values that will be provided sporadically over time. This is a very important thing. A Future usually ends with a return keyword that ends the flow. When you’ll work with BLoC the return statement have to be replaced with the yield keyword. Think about yield like a return but that cannot end the function.

The dart:async package 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. If a stream yields output values over time, a sink accepts input values over time.

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

We’ll start with the web services. For this sample, we’ll implement a call to a public and free service exposed by The Guardian. You’ll need to register (free) on https://bonobo.capi.gutools.co.uk/register/developer to get the API-KEY needed to invoke the APIs.

To get the list of contents will invoke the search function that returns a list of contents by topic:

https://content.guardianapis.com/search?api-key={api-key}&q={query}

Full documentation at the following link https://open-platform.theguardian.com/documentation/search

Let’s start with Flutter. First, create an empty project and run it on your device or simulator to be sure that everything is working. Now we’ll start adding some dependencies in the pubspec.yaml useful to extend the framework for some features we are going to implements.

To implement the Use-case we’ll use these external libs found on pub.dev:

cupertino_icons:

Default icons asset for Cupertino widgets based on Apple styled icons

Default icons asset for Cupertino widgets based on Apple styled icons equatable:

An abstract class that helps to implement equality without needing to explicitly override == and hashCode.

An abstract class that helps to implement equality without needing to explicitly override == and hashCode. flutter_bloc:

Flutter Widgets that make it easy to implement the BLoC (Business Logic Component) design pattern. Built to be used with the bloc state management package.

Flutter Widgets that make it easy to implement the BLoC (Business Logic Component) design pattern. Built to be used with the bloc state management package. flutter_inappwebview:

A Flutter plugin that allows you to add an inline webview, to use an headless webview, and to open an in-app browser window.

A Flutter plugin that allows you to add an inline webview, to use an headless webview, and to open an in-app browser window. http :

A composable, multi-platform, Future-based API for HTTP requests.

: A composable, multi-platform, Future-based API for HTTP requests. intl :

Contains code to deal with internationalized/localized messages, date and number formatting and parsing, bi-directional text, and other internationalization issues.

: Contains code to deal with internationalized/localized messages, date and number formatting and parsing, bi-directional text, and other internationalization issues. share:

To manage the URL sharing using the Apps currently available on the Device

At this stage we’ll start implementing the networking-data layer. We create a data folder inside libs with two subfolders:

Models

Repository

The model is a dart class that contains all the fields needed to map the response received from the API. The repository is used to implement the web service client and the repository linked to the BLoC layer.

Let’s begin with the model. Looking to the JSON received from the Web service, we’ll map the fields inside:

Look to Equatable is used in the props method to check the hashcode or equality of the instance of the class when compared with another one.

fromJson is the static method used by the client to build the item from the JSON decoded as a dynamic instance type. So, this class will the instance that contains the information for each item browsable on the App, simply news returned from the API.

Next, we’ll implement the Webservice Client moving in the repository folder.

On top of the Class we’ll declare the common properties for the class:

The API-KEY has to be provided to the Client to invoke the service in the right way, according to The Guardian API documentation. Note the @required and assert directive in the constructor used to say the client isn’t instantiable if the API key is null.

Next, we’ll model the http GET call to the search API:

The call returns a Future because the method will be executed in async mode. If the status code of the http response is 200 OK, the JSON will be decoded using a method provided by dart:convert. Looking to the json structure, the total number of available elements is provided in response > total so we’ll check the value navigating the dynamic object as a map

int itemCount = jsonResponse[‘response’][‘total’]; print(‘Number of books about http: $itemCount.’);

If the list is empty we simply return an empty list

if (itemCount == 0) { print(‘No items found for this topic’); return []; }

Otherwise, for each allows building a list of “model instances” to be returned to the invoking layer:

List<SearchApiItemModel> output = []; for (var item in jsonResponse[‘response’][‘results’]) { output.add(SearchApiItemModel.fromJson(item)); } return output;

At this time the getItems returns the list of contents based on a topic. The topic is a string to be provided as a query of the search method.

The Repository class wraps the Client and provides, in the example, the API key for the web service:

class TheGuardianRepository { TheGuardianClient client; static const apikey = “ADD THE API KEY RECEIVED ON YOUT MAILBOX”;

TheGuardianRepository() { client = TheGuardianClient(apikey); }

Future<List<SearchApiItemModel>> getItems(String query) async { return await client.getItems(query); } }

We are finally ready to implement the Bloc layer. First, we’ll create a folder in lib named bloc. We’ll define four classes inside:

NewsBloc

NewsEvents

NewsStates

BlocUtils

As described before, Bloc models a final state machine based on events. When an event is triggered, an event transition can happen, depending on the current context. Before describing the implementation, following the image that describes the state machine:

In the beginning, the content is empty. When the FETCH event is received, the web service is invoked to get the contents but the http call is async so we have a mutual transition in a loading state during the processing time. The results of the call will be (as a simplification for this article) loaded or error. Error means that something goes wrong, for instance, the status code returned is not 200. Loaded means that the content is available in memory to be displayed. To update the contents and make a new call to the service, you have the ability to trigger the refresh event. Not let’s model this state machine using the BLoC pattern.

We’ll start defining the states:

abstract class NewsState extends Equatable { const NewsState(); @override

List<Object> get props => []; }

NewsState is the abstract class used as a base for each state in the application so each state inherits from NewsState.

All state states for this sample are without properties except for the loaded state that contains the list of news retrieved from the services:

class NewsEmptyState extends NewsState {} class NewsLoadingState extends NewsState {} class NewsErrorState extends NewsState {} class NewsLoadedState extends NewsState { final List<SearchApiItemModel> news;

NewsLoadedState({@required this.news}) : assert(news != null);

@override List<Object> get props => [news]; }

No particular complexity here, just writes down one class for each state in your workflow, defining properties if needed.

For Events we’ll follow the same approach:

abstract class NewsEvent extends Equatable { const NewsEvent(); }

NewsEvent is the abstract class as base for all the events.

class FetchNewsEvent extends NewsEvent { final String topic; FetchNewsEvent({@required this.topic}) : assert(topic != null); @override List<Object> get props => [topic]; }

class RefreshNewsEvent extends NewsEvent { final String topic; RefreshNewsEvent({@required this.topic}) : assert(topic != null); @override List<Object> get props => [topic]; }

class ResetNewsEvent extends NewsEvent { @override List<Object> get props => null; }

class ErrorNewsEvent extends NewsEvent { @override List<Object> get props => null; }

Each of the described events will be simplified as classes declaring arguments and properties when needed. For instance, as described when we implemented the client, the fetch event needs the topic to be passed to the search API as a query.

Well, we declared states and events but we’ll need to link each one to implement the designed workflow. The link between States and Events is the Bloc.

The class declaration links your states and events:

This is the core of the BLOC pattern. For each event received in the stream we can implement the logic needed for each particular event.

Note the particular keywords used in this class:

async*

yield*

Marking a function as async or async* allows it to use async/await keyword to use a Future.

The difference between both is that async* will always return a Stream and offer some syntax sugar to emit a value through yield keyword.

We already talk before about yield as a keyword similar to return but that cannot interrupt the function. The Stream is always opened during the application execution and yield allows to “return” a particular change consequence of the evaluation of an occurred event.

Why yield* and not yield? Because we separate the logic that checks each event in a separate method. This makes the code more readable and maintainable than put all your logic in one point.

So, looking at each method:

Stream<NewsState> _doReset(ResetNewsEvent event) async* { yield NewsEmptyState(); }

Reset just remove all the elements and move the state machine to the empty state (like what happens at the starting point).

Stream<NewsState> _doFetch(FetchNewsEvent event) async* { yield NewsLoadingState(); try { var items = await repository.getItems(event.topic); yield NewsLoadedState(news: items); } catch (error) { print(error); yield NewsErrorState(); } }

Stream<NewsState> _doRefresh(RefreshNewsEvent event) async* { yield NewsLoadingState(); try { var items = await repository.getItems(event.topic); yield NewsLoadedState(news: items); } catch (error) { print(error); yield NewsErrorState(); } }

Fetch and refresh are similar. The goal is to get the information to be displayed. When fetch is triggered, first the state is changed to loading:

yield NewsLoadingState();

Note yield works as a return but the process continues. Now the http call is made by the repository. If an error occurs (in the simple if the catch receives an exception), we’ll move to error state:

} catch (error) { print(error); yield NewsErrorState(); }

And if the result is returned simply goes to Loaded:

var items = await repository.getItems(event.topic); yield NewsLoadedState(news: items);

The remaining class, BlocUtils, is used only to store useful pieces of cose reusable in the frontend layer. We choose to design this as a singleton, so the instance, when created, is persistent for all the duration of the process, without create a new instance in memory when the constructor will be invoked:

The method event causes the trigger of a new event in the bloc architecture. For instance, if you are in the EMPTY state and you’ll press a button, invoking this method you can trigger the fetch event to move the application to the next stage.