How to toggle between dark mode and light mode using the Stacked architecture in Flutter

Setup

To start things off, make sure you install a fresh Flutter project to follow along.

// you can name the project anyway you want, 
// in this case I will just use this name below
flutter create stacked_theme_switcher

If you aren’t familiar about the stacked architecture, then I highly recommend you to watch and learn how to setup the stacked architecture from FilledStacks before following through this tutorial since it requires an understanding of the stacked architecture.

I have learned about stacked architecture from there and the content and quality of his tutorials are great so you should check it out.

And if you are familiar about the stacked architecture, then let’s install the dependencies that we need. Head over to your pubspec.yaml file in your project directory and put these dependencies.

dependencies:
 # state
 stacked: ^1.7.3+2
 # inversion of control
 injectable:
 observable_ish:
 get_it: ^4.0.2
dev_dependencies:
 build_runner:
 injectable_generator:

Under dependencies, we need the stacked since it is what we are using for our state management solution. The other 3 below are the ones that handles dependency injection in our Flutter app. For observable_ish package handles the reactive values inside services of the stacked architecture. You might say it is an old package but there is a reason why Dane from FilledStacks have chosen this package. But I am now going to talk about that in here.

And lastly, under the dev_dependencies, these are what we need so we can avoid boilerplate code in our Flutter app since we would be using injectable annotations as how they are recognized for the builders.

Okay so make sure you run pub get to install the dependencies. flutter pub get

Dependency Injection

After setting up for the project, we can then start writing code for our theme switcher. For the main.dart file, clean it up and leave what we only need.

main.dart

We create a folder inside /lib directory and name it as “app”, in this directory is where we put our locator, this handles the dependency injection of our Flutter application. And then create a file locator.dart in it.

locator.dart

If you have followed then your project directory would look something like this.

project directory

You may see that VSCode have been indicating errors in the code and that’s fine because the auto-generated boilerplate code are not there yet. Hang on for a little bit as we need to create our theme switcher class or theme service.

In the /lib directory, create a new directory and name it as “services” since inside here we put all of our services that will be responsible of serving some sort of data into our viewmodels. Then create a file called as theme_service.dart and inside the file, write the code and put a lazySingleton annotation from the injectable package, so it will auto register this service and make it accessible globally in our Flutter app.

theme_service.dart

When you have created it already then it’s about time we run the builder and let it generate boilerplate code for us! Open up a terminal or use your existing terminal, then execute the command below

flutter pub run build_runner build

It would take a moment to auto generate the code for us. So please wait. :) It should look something like this if you have done things correctly.

terminal

Basically it created a locator.config.dart file and it registered the ThemeService class as a singleton, this we can now access our ThemeService globally in our application if you might need theming in other parts of the code.

This is what the auto-generated code looks like…

locator.config.dart

Theme Definition

Right after we have set up our ThemeService class and registered it as a singleton, we then define our theme data inside the ThemeService class, our ThemeService class is using a mixin ReactiveServiceMixin since this will be handling the reactive values in the service.

We then import observable_ish inside this ThemeService class since we will be using it inside the class as we would be toggling over dark theme and light theme during run time. Then we also import material package since we will be using classes like ThemeData, ThemeMode and other widgets related to create our theme. Then we also instantiate reactive values from observable_ish and give it a type of ThemeMode since we will be toggling over dark and light mode.

theme_service.dart (Part 1)

theme_service.dart (Part 2)

After defining our properties for light theme and dark theme, we can then make our application toggle between themes.

Toggle Theme

Now back into our main.dart, put setupLocator() before runApp() since we want to instantiate those services before the app starts. It should look something like this,

main.dart

And then wrap our MaterialApp() in a ViewModelBuilder.reactive() named constructor, create a MainViewModel class under and give the ViewModelBuilder a type of MainViewModel.

Here we added reference to our ThemeService that was registered as a singleton as we were setting it up. This means we have access to the methods and properties inside our ThemeService and since our MainViewModel extends ReactiveViewModel, it has a required override annotation, in it we put all of our dependencies in an array, in our case we only have _themeService in it because it is the only service we only want to listen to for changes.

If you have noticed, we have a property added in our MaterialApp widget, there we put our home screen as it’s the first view that gets rendered when our app starts.

home screen

To create this view, as convention being followed from the stack architecture, let us create a directory inside /lib and name it as “ui” and inside /ui directory we create another directory called as “views”, inside this directory is where we put all of our user interface code along with its viewmodel of each views. And lastly, we want the home screen, so we create another folder called as “home” and inside this folder we create 2 files, home_view.dart & home_viewmodel.dart Inside home_view.dart is where we put our UI related code and all logic inside home_viewmodel.dart

Inside our home_view.dart we create a Scaffold that is wrapped inside a ViewModelBuilder of type HomeViewModel so we have access to methods from the ViewModel that this view is coupling with. Then as the body of our Scaffold we put a RaisedButton widget, and on tap we toggle the theme between light and dark.

home_view.dart

And the code for the home_viewmodel.dart …

home_viewmodel.dart

Inside the view model is we will be using the ThemeService again as dependency so we can make use of the toggleTheme() method from the ThemeService class to be able to switch between light and dark theme.

And from there, we can now toggle between dark and light theme! Thanks for following through and I hope you have liked it. But if you have a better way of implementing this one then let me know I would appreciate it :)

Cheers!