How to dynamically change the theme in Flutter
Changing the theme at runtime and persisting those changes across restarts? No problem!

TLDR: I made a package which takes care of all of this, if you are interested on how it’s done keep reading.
How to do it.
Usually your entry point to the app looks like this:
If you want to change the theme of your app at compile time you can modify ThemeData
. There is an attribute called Brightness
which changes a bunch of colors from light to dark if set to Brightness.dark
.
We want it to dynamically change that variable during runtime. We therefore introduce state to the top level widget.
Now, we have to access this state. Either by passing down a callback, writing a custom InheritedWidget
or using the BuildContext
to walk up the tree.
Passing down a callback is the most straight forward thing to do. But it also is the most annoying one. If the tree gets deep a lot of classes just pass the callback to its children. The logic is pretty simple though all that happens is:
- Top widget implements a method which changes the brightness and calls
setState()
- Any child who has a reference to that method can invoke it.
An InheritedWidget
would solve this problem by giving all the children access to the callback. You would still have to manage a callback, but this time there is no need to pass it all the way down.
The last viable option is searching the tree for the state. This one is pretty straight forward. We need a context to start the search, all we do is walk up the tree a search for a State
which is of our type.
context.ancestorStateOfType(const TypeMatcher<YourState>());
Holding a static reference or global variable is a big no go, I won’t cover why in this post though.
Now we are be able to access and modify the Brightness
on the go. The last thing is to persist the Brightness
across app restarts.
Using shared preferences we can fetch the brightness before we run the app. When the brightness changes we will also have to save it there.
Because the shared preferences are asynchronous we need to await the result. The call is very fast and therefore this is not a big deal.
And we’re done! Now we can change the Theme
of the app anywhere in our code.
The package
Because we as developers shouldn’t waste time re-implementing features, I made this a package!
Basically it’s exactly doing all the things I talked about above but encapsulated in one handy class.
Here is how it works:
You wrap your MaterialApp
in the DynamicTheme
widget. There you can specify a default brightness.
Because the brightness value is changing the colors when constructing the ThemeData
you’re able to either just pass the brightness on to the ThemeData
constructor and let it take care of it, or you can customize it by passing your own colors depending on the Brightness
.
Last but not least, the ThemedWidgetBuilder
. This is the place you usually just return the MaterialApp
and pass it the theme.
And that’s it.
Now you can modify your Theme from anywhere in your code with:
DynamicTheme.of(context).setBrightness(Brightness.dark);
It will take care of saving it to the shared preferences and updating the app.
Bonus
Because I had way to much fun coding this, here is a dialog you can use for the theme switching:
