I want to create a flutter app that has 2 light and dark mode themes that change by a switch in-app and the default theme is default android theme.
I need to pass some custo
Screenshot:
If you don't want to use any third party packages or plugins, you can use ValueListenableBuilder
which comes out of the box with Flutter.
Full code:
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
final _notifier = ValueNotifier<ThemeModel>(ThemeModel(ThemeMode.light));
@override
Widget build(BuildContext context) {
return ValueListenableBuilder<ThemeModel>(
valueListenable: _notifier,
builder: (_, model, __) {
final mode = model.mode;
return MaterialApp(
theme: ThemeData.light(), // Provide light theme.
darkTheme: ThemeData.dark(), // Provide dark theme.
themeMode: mode, // Decides which theme to show.
home: Scaffold(
appBar: AppBar(title: Text('Light/Dark Theme')),
body: RaisedButton(
onPressed: () => _notifier.value = ThemeModel(mode == ThemeMode.light ? ThemeMode.dark : ThemeMode.light),
child: Text('Toggle Theme'),
),
),
);
},
);
}
}
class ThemeModel with ChangeNotifier {
final ThemeMode _mode;
ThemeMode get mode => _mode;
ThemeModel(this._mode);
}
Screenshot:
You can use provider to set the theme. Here's full code:
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider<ThemeModel>(
create: (_) => ThemeModel(),
child: Consumer<ThemeModel>(
builder: (_, model, __) {
return MaterialApp(
theme: ThemeData.light(), // Provide light theme.
darkTheme: ThemeData.dark(), // Provide dark theme.
themeMode: model.mode, // Decides which theme to show.
home: Scaffold(
appBar: AppBar(title: Text('Light/Dark Theme')),
body: RaisedButton(
onPressed: () => model.toggleMode(),
child: Text('Toggle Theme'),
),
),
);
},
),
);
}
}
class ThemeModel with ChangeNotifier {
ThemeMode _mode;
ThemeMode get mode => _mode;
ThemeModel({ThemeMode mode = ThemeMode.light}) : _mode = mode;
void toggleMode() {
_mode = _mode == ThemeMode.light ? ThemeMode.dark : ThemeMode.light;
notifyListeners();
}
}
Answering OP questions:
Current theme can be found using:
bool isDarkMode = MediaQuery.of(context).platformBrightness == Brightness.dark;
or
bool isDarkMode = SchedulerBinding.instance.window.platformBrightness == Brightness.dark;
You can provide theme to your whole app using theme
for default themes, darkTheme
for Dark themes (if dark mode is enabled by the system or by you using themeMode
)
You can make use of provider package as shown in the code above.
The easiest way in my opinion is by using provider to manage the state of your app and shared_preferences to save your theme preference on file system. By following this procedure you can save your theme so the user doesn't have to switch theme every time.
Output
You can easily store your theme preference in form of a string and then at the start of your app check if there is value stored on file system, if so apply that theme as shown below.
StorageManager.dart
import 'package:shared_preferences/shared_preferences.dart';
class StorageManager {
static void saveData(String key, dynamic value) async {
final prefs = await SharedPreferences.getInstance();
if (value is int) {
prefs.setInt(key, value);
} else if (value is String) {
prefs.setString(key, value);
} else if (value is bool) {
prefs.setBool(key, value);
} else {
print("Invalid Type");
}
}
static Future<dynamic> readData(String key) async {
final prefs = await SharedPreferences.getInstance();
dynamic obj = prefs.get(key);
return obj;
}
static Future<bool> deleteData(String key) async {
final prefs = await SharedPreferences.getInstance();
return prefs.remove(key);
}
}
Define your theme properties in a theme variable like below and initialize your _themedata variable on the basis of value inside storage.
ThemeManager.dart
import 'package:flutter/material.dart';
import '../services/storage_manager.dart';
class ThemeNotifier with ChangeNotifier {
final darkTheme = ThemeData(
primarySwatch: Colors.grey,
primaryColor: Colors.black,
brightness: Brightness.dark,
backgroundColor: const Color(0xFF212121),
accentColor: Colors.white,
accentIconTheme: IconThemeData(color: Colors.black),
dividerColor: Colors.black12,
);
final lightTheme = ThemeData(
primarySwatch: Colors.grey,
primaryColor: Colors.white,
brightness: Brightness.light,
backgroundColor: const Color(0xFFE5E5E5),
accentColor: Colors.black,
accentIconTheme: IconThemeData(color: Colors.white),
dividerColor: Colors.white54,
);
ThemeData _themeData;
ThemeData getTheme() => _themeData;
ThemeNotifier() {
StorageManager.readData('themeMode').then((value) {
print('value read from storage: ' + value.toString());
var themeMode = value ?? 'light';
if (themeMode == 'light') {
_themeData = lightTheme;
} else {
print('setting dark theme');
_themeData = darkTheme;
}
notifyListeners();
});
}
void setDarkMode() async {
_themeData = darkTheme;
StorageManager.saveData('themeMode', 'dark');
notifyListeners();
}
void setLightMode() async {
_themeData = lightTheme;
StorageManager.saveData('themeMode', 'light');
notifyListeners();
}
}
Wrap your app with themeProvider and then apply theme using consumer. By doing so whenever you change the value of theme and call notify listeners widgets rebuild to sync changes.
Main.dart
void main() {
return runApp(ChangeNotifierProvider<ThemeNotifier>(
create: (_) => new ThemeNotifier(),
child: MyApp(),
));
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Consumer<ThemeNotifier>(
builder: (context, theme, _) => MaterialApp(
theme: theme.getTheme(),
home: Scaffold(
appBar: AppBar(
title: Text('Hybrid Theme'),
),
body: Row(
children: [
Container(
child: FlatButton(
onPressed: () => {
print('Set Light Theme'),
theme.setLightMode(),
},
child: Text('Set Light Theme'),
),
),
Container(
child: FlatButton(
onPressed: () => {
print('Set Dark theme'),
theme.setDarkMode(),
},
child: Text('Set Dark theme'),
),
),
],
),
),
),
);
}
}
Here is the link to github repository.
You can also use the available plugin day_night_theme_flutter
A Flutter plugin that helps you to automatically change the theme of the app with sunrise and sunset. Just specify the light and dark theme to use, and you are all set. You can use your custom sunrise and sunset time too.
How to use it?
MaterialApp(
theme: ThemeData.light(),
/// theme: ThemeData.dark(),
)
Down the widget tree, you can access ThemeData simply by writing Theme.of(context)
. If you want to access the current ThemeData and provide your own styling for certain field, you can do for an instance:
Widget build(BuildContext context) {
var themeData = Theme.of(context).copyWith(scaffoldBackgroundColor: darkBlue)
return Scaffold(
backgroundColor = themeData.scaffoldBackgroundColor,
);
}
But to handle the ThemeData state (changing its value), you need to implement proper state management.
MaterialApp(
title: 'App Title',
theme: ThemeData(
brightness: Brightness.light,
/* light theme settings */
),
darkTheme: ThemeData(
brightness: Brightness.dark,
/* dark theme settings */
),
themeMode: ThemeMode.dark,
/* ThemeMode.system to follow system theme,
ThemeMode.light for light theme,
ThemeMode.dark for dark theme
*/
debugShowCheckedModeBanner: false,
home: YourAppHomepage(),
);
You can use scoped_model or provider for seamless experience.