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.dartWe 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.dartIf you have followed then your project directory would look something like this.
project directoryYou 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.dartWhen 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.
terminalBasically 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.dartTheme 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.
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,
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 screenTo 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.
And the code for the home_viewmodel.dart …
home_viewmodel.dartInside 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!