Theme Switching & Persisting in Flutter using cubits and Stream

Biplab Dutta
4 min readJun 8, 2022
Image Source: raywenderlich.com

Every mobile app user prefers having an option to choose between multiple themes. Having decent themes available is also very crucial in enhancing the user experience. So, how can we do it effectively in Flutter? How can we have different configs set for each theme? This article ensures that you get a proper understanding of it and I’ll also talk a bit regarding Stream in Dart.

Demo

Let’s take a look at our final app.

Final app demo demonstrating theme switching and persisting

As we can see in the GIF, our app allows us to switch between dark theme and light theme. Also, the icon on the floating action button changes dynamically. And the chosen theme is persisted which can be witnessed in every app launch.

Dependencies

Before we begin working on the code, let’s first include some external packages that we will need. Include flutter_bloc, shared_preferences, and equatable as your dependencies in pubspec.yaml file.

Let’s Code 👨‍💻

Since this is a very simple app, I won’t be concerned about app architecture in this article. You can check my other articles if you want to learn about app architecture.

Theme Configurations

First, start a new flutter project and get rid of the default counter app. Then inside the lib folder, create a file app_theme.dart.

Data Layer/Theme Repository

Then create a theme_repository.dart file inside the lib directory and paste the following code.

As you can see, we created an abstract class ThemePersistence, and another class ThemeRepository that implements the abstract class. Also, we created an enum CustomTheme that has two values — light and dark because these are the themes that our app will have. The ThemeRepository class depends on the SharedPreferences instance that we will use for theme persistence.

Also, we can see that getTheme() returns a Stream and not a Future. The main reason behind using Stream is that listeners of this Stream can immediately be notified of the theme change and we needn’t call the getTheme() method again and again.

If we had a Future implementation for getTheme(), after every theme update, we’d have to invoke the getTheme() method which is not ideal.

getTheme() is a one-time delivery of data meaning that it should be called only once and any changes should be yielded in the form of a stream which the listeners will listen to.

Now, we will add code to our getTheme() and saveTheme() methods.

We initialized a StreamController which will act as a manager for our Stream<CustomTheme>. The saveTheme() method is straightforward. First, it adds the theme that we want to save to the stream and then calls _setValue() which will persist the theme. The _setValue() method uses API from SharedPreferences to persist the chosen theme.

Everything looks fine. But if you see it carefully, the method getTheme() yields the stream from the controller. But initially, as the app is launched, there would be no stream in the controller. Then how do we deal with it?

The solution is simple. We can add a constructor body that would be executed as soon as the ThemeRepository class is instantiated. Also, getTheme() is a method that would be called in the very early stage. So, we need to make sure that before the getTheme() method is called, there is some stream value in the controller. So, add a _init() method in the constructor body and instantiate the ThemeRepository in the main.dart file later. Then, the final version of our theme_repository.dart file would look like this:

Also, you may have noticed I used a CustomTheme enum that I created in this file instead of using ThemeMode which is available in Flutter. The reason is simply to avoid including the material package in the data layer of our project as ThemeMode comes from the material package. In my opinion, the components of the material package are associated with the presentation layer and not the data layer.

Cubits/ViewModel/State Management

Now, we will create a ThemeCubit and ThemeClass class that will be responsible for our state handling. Create a folder in the lib directory and name it them_cubit. Inside theme_cubit, create two dart files — theme_cubit.dart and theme_state.dart.

The code is self-explanatory and I have added all the necessary explanations through comments in the above code. Be sure to go through them.

main.dart

Let’s add some code to the main.dart file.

App Widget

Next, create a app.dart file in the lib directory and paste the following code.

HomePage

Create a file home_page.dart and add the following code.

Other solutions

There are many other ways of doing the same thing that I showed in this article. You could also use hydrated_bloc instead of cubits to manage the state and persist it. However, I wanted to show you how you could have done the persisting if you were working with other state management solutions other than flutter_bloc.

Conclusion

This article showed how we can include theme switching and persisting feature in our Flutter app using the best practices. I hope you all got to learn from my article and if there’s any feedback for me, drop a comment. I’ll be sure to upload another article in a few days again.

If you wish to see some Flutter projects with proper architecture, follow me on GitHub. I am also active on Twitter @b_plab.

Source Code for the project in this article

My Socials:

Until next time, happy coding!!! 👨‍💻

— Biplab Dutta

--

--