How to change the app’s theme at runtime using the BLoC pattern in Flutter.

There are many cases where you might want to support multiple themes in your application, and let the user change them dynamically. Let me explain you how to do it in this tutorial using the “business logic component” pattern, also known as BLoC.

TL;DR; For those just interested in the code, here’s the code covered in this article: https://github.com/jorgecoca/theme_switcher/tree/theme-switcher-tutorial-1

We are going to build a Flutter application that consists of:

  • A MaterialApp at the root of our app
  • Two pages: the HomePage and theThemeSelectorPage
  • A BLoC component that will receive a selected theme, and will emit a ThemeData object

Let’s start by creating an app named theme_switcher, replacing the contents of main.dart and creating home_page.dart file that will represent the “initial” screen of our app.

We have just created a simple app with, based on MaterialApp, with an AppBar, a Text centered and a FloatingButton. Should look like this:

Our next step will be to open the ThemeSelectorPage from the icon that we added on the AppBar. To do that, let’s create a theme_selector_page.dart, and let’s modify our HomePage to open it when pressing on the IconButton

Now our application looks like this:

As you can see, our buttons still do nothing. So the next step is to add functionality to those buttons. In order to that, we are going to create a new file that we will name themes.dart. This file will have two missions:

  • Listen to the button events, so when we receive an event we can emit a ThemeData object containing the specs for the selected theme.

To accomplish this, we will make use of Stream and Sink. You can think of Sink as an asynchronous input to your BLoC, and Stream as the same thing, but being an output instead.

Using a little bit of RxDart, our main mission here is to transform the selected theme name input, and transform it into a ThemeData object. It looks something like this:

selectedTheme.distinct().map((theme) => theme.data);

We use distinct() here to avoid repainting the same theme again.

In order to use RxDart, we will need to add new package to our pubspec and update our dependencies with rxdart: ^0.18.1.

With this information, our themes.dart file will look like this:

We have defined a DemoTheme model that will be our BLoC input, and our ThemeBloc class has defined a Sink to process the input, an Stream output to emit our themes, and our initial theme.

Now we can update our ThemeSelectorPage to link our button events with the Sink in the BLoC:

We have added a constructor to mark our ThemeBloc as a dependency.

Next step here is to modify our main.dart and home_page.dart files to react to the new themes emitted:

Let’s break down these changes:

  • Our ThemeSwitcherApp now returns a StreamBuilder, that will help us to setup the initial state of the widget, plus also it is responsible for handling the communication with the Stream of the ThemeBloc.
  • But why do we need to modify the HomePage? Well, since we navigate to the ThemeSelectorPage from here, it is just a mere pass through for the ThemeBloc object. If we were creating two instances of ThemeBloc, we might be emitting events in one object and listening for themes on a different one. There are better ways to do this, as I will explain on following posts, but this works for now.

With all these pieces together, we can test our theme switcher:

Hurray!!! Our application works!!! However, there are still a few details that we can improve:

  • In order to use the same instance of ThemeBloc, we modified HomePage because it was the nexus between our ThemeSwitcherApp and our ThemeSelectorPage
  • We did not write any tests! And this, “mi amigo”, it is unacceptable!

We will improve these two cases in following articles ;)

I hope you enjoyed this tutorial as much as I did!