What is the best way to go about dynamically changing the theme of a Flutter app? For example, if the user changes the color to red, I want the theme to instantly be changed to red. I can't find anything very helpful online except one guy said to use the BLOC pattern, which I am not familiar with it. I'd like to hear your guys thoughts on the issue. Thanks!
My current code structure:
var themeData = ThemeData(
fontFamily: 'Raleway',
primaryColor: Colors.blue,
brightness: Brightness.light,
backgroundColor: Colors.white,
accentColor: Colors.blue);
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: Constants.appName,
theme: themeData,
home: CheckAuth(), //CheckAuth returns MyHomePage usually
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title, @required this.uid}) : super(key: key);
final String title;
final String uid;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
...build and stuff
}
You can use InhertedWidget if you like (instead of BLOC) - Basically it is used to access parent widget anywhere from the tree.
So what you should do is
InheritedWidget
, somewhere in top of tree [from where you want the effect of theme to take place]Theme
widgetHere is some code:
import 'package:flutter/material.dart';
var themeData = ThemeData(
fontFamily: 'Raleway',
primaryColor: Colors.blue,
brightness: Brightness.light,
backgroundColor: Colors.white,
accentColor: Colors.blue
);
void main() {
runApp(
ThemeSwitcherWidget(
initialTheme: themeData,
child: MyApp(),
),
);
}
class ThemeSwitcher extends InheritedWidget {
final _ThemeSwitcherWidgetState data;
const ThemeSwitcher({
Key key,
@required this.data,
@required Widget child,
}) : assert(child != null),
super(key: key, child: child);
static _ThemeSwitcherWidgetState of(BuildContext context) {
return (context. dependOnInheritedWidgetOfExactType(ThemeSwitcher)
as ThemeSwitcher)
.data;
}
@override
bool updateShouldNotify(ThemeSwitcher old) {
return this != old;
}
}
class ThemeSwitcherWidget extends StatefulWidget {
final ThemeData initialTheme;
final Widget child;
ThemeSwitcherWidget({Key key, this.initialTheme, this.child})
: assert(initialTheme != null),
assert(child != null),
super(key: key);
@override
_ThemeSwitcherWidgetState createState() => _ThemeSwitcherWidgetState();
}
class _ThemeSwitcherWidgetState extends State<ThemeSwitcherWidget> {
ThemeData themeData;
void switchTheme(ThemeData theme) {
setState(() {
themeData = theme;
});
}
@override
Widget build(BuildContext context) {
themeData = themeData ?? widget.initialTheme;
return ThemeSwitcher(
data: this,
child: widget.child,
);
}
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeSwitcher.of(context).themeData,
home: CheckAuth(),
);
}
}
I have wrapped ThemeSwitcherWidget around MaterialApp so the effect is throughout the app (even when you push new route with Navigator).
Use ThemeSwitcher.of(context).switchTheme(themeData)
anywhere below ThemeSwithcerWidget to change the theme.
In question's case it should call ThemeSwitcher.of(context).switchTheme(Theme.of(context).copyWith(primaryColor: Colors.red))
to switch primary color to red throught out the app, for eg. on some button click
EDIT: replaced inheritFromWidgetOfExactType
-> dependOnInheritedWidgetOfExactType
, since it is deprecated - as pointed by Phoca in comments.
Using provider package: theme_changer.dart
var darkTheme = ThemeData.dark();
var lightTheme= ThemeData.light();
class ThemeChanger extends ChangeNotifier {
ThemeData _themeData;
ThemeChanger(this._themeData);
get getTheme => _themeData;
void setTheme(ThemeData theme) {
_themeData = theme;
notifyListeners();
}
}
main.dart
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => ThemeChanger(lightTheme)),
],
child: MaterialAppWithTheme(),
);
}
}
class MaterialAppWithTheme extends StatelessWidget {
@override
Widget build(BuildContext context) {
final theme = Provider.of<ThemeChanger>(context);
return MaterialApp(
theme: theme.getTheme,
home: FirstScreen(),
);
}
first_screen.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import './theme_changer.dart'
class FirstScreen extends StatelessWidget{
@override
Widget build(BuildContext context){
var _themeProvider=Provider.of<ThemeChanger>(context);
return Scaffold(
appBar: AppBar(title:Text("First Screen"),),
body:Container(width:MediaQuery.of(context).size.width,
height:MediaQuery.of(context).size.height,
child:Center(
child:FlatButton(child:Text("Press me"). onPressed:(){
_themeProvider.setTheme(_themeProvider.getTheme==lightTheme?darkTheme:lightTheme);
})
),
),
);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With