How to implement Dark mode and Light Mode in flutter? - flutter

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 custom color to the fellow widget and I don't want to just config material theme.
how to detect the user device default theme?
the secend question is how to provide a theme to the whole app?
third is how change the theme with a simple switch in running time?

Using Material App
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(),
);
Using CupertinoApp
Detect the dark mode using, WidgetsBinding.instance?.window.platformBrightness
You may also have to listen for the brightness changes from the system in order to update in real-time using WidgetsBindingObserver, and overriding, didChangePlatformBrightness();
CupertinoApp Example:
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
Brightness? _brightness;
#override
void initState() {
WidgetsBinding.instance?.addObserver(this);
_brightness = WidgetsBinding.instance?.window.platformBrightness;
super.initState();
}
#override
void dispose() {
WidgetsBinding.instance?.removeObserver(this);
super.dispose();
}
#override
void didChangePlatformBrightness() {
if (mounted) {
setState(() {
_brightness = WidgetsBinding.instance?.window.platformBrightness;
});
}
super.didChangePlatformBrightness();
}
CupertinoThemeData get _lightTheme =>
CupertinoThemeData(brightness: Brightness.light, /* light theme settings */);
CupertinoThemeData get _darkTheme => CupertinoThemeData(
brightness: Brightness.dark, /* dark theme settings */,
);
#override
Widget build(BuildContext context) {
return CupertinoApp(
title: 'Demo App',
theme: _brightness == Brightness.dark ? _darkTheme : _lightTheme,
home: MyHomePage(title: 'Demo Home Page'),
);
}
}
You can use scoped_model, provider, bloc or get for seamless experience.

Below are three ways to implement Dark Mode:
always Dark mode
device/platform controlled dark mode
app controlled, runtime switchable dark mode
Always Dark Mode
To run your app only in Dark Mode:
in MaterialApp, replace ThemeData(...) with ThemeData.dark()
restart your app. It will now be running in Dark Mode using the colors defined in ThemeData.dark()
OLD
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
NEW
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData.dark(), // default dark theme replaces default light theme
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
Device Controlled Dark Mode
works only on Android 10+, iOS 13+ (when dark mode was introduced)
to let the device/platform set the theme, MaterialApp needs 3 args:
theme: ThemeData()
darkTheme: ThemeData().dark
themeMode: ThemeMode.system
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(),
darkTheme: ThemeData.dark(), // standard dark theme
themeMode: ThemeMode.system, // device controls theme
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
(you can use custom themes. Above are defaults for simplicity)
themeMode: ThemeMode.system tells Flutter to use the device/platform theme setting
with the above settings on Android 10+ or iOS 13+, toggling Dark mode via Device Settings will now switch your app between light and dark modes.
on Android: drag down from top of screen and click the Dark theme toggle button.
iOS physical device: Settings > Display & Brightness > Light or Dark.
iOS: add Dark mode switch to Control Center for ease of testing
iOS simulator: Settings > Developer > Dark Appearance.
any time the device theme changes, your app will immediately reflect the chosen device theme
to get the current device theme mode programmatically, we can check device brightness (Brightness.light or Brightness.dark) which corresponds to light mode and dark mode. Do this by querying platformBrightness with: MediaQuery.of(context).platformBrightness
App Controlled Dark Mode
our app can run in either light or dark mode, controlled by user and switched freely at runtime inside the app and completely ignore the device's theme setting
as before, supply all three theme arguments to MaterialApp: theme:, darkTheme: and themeMode:, but we'll adjust themeMode: to use a state field below
To switch between light / dark modes within the app, we'll swap the themeMode: argument between ThemeMode.light and ThemeMode.dark and rebuild the MaterialApp widget.
How to Rebuild MaterialApp widget
to switch our app theme from anywhere, we need to access MaterialApp from anywhere in our app
we can do this without any package using just StatefulWidget, or we can use a state management package
example of runtime theme switching anywhere in app using StatefulWidget below
Before - Stateless
we started with this, but we'll replace it with a StatefulWidget next
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(),
darkTheme: ThemeData.dark(), // standard dark theme
themeMode: ThemeMode.system, // device controls theme
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
After - Stateful
here we've replaced MyApp StatelessWidget with a StatefulWidget and its complementary State class, _MyAppState
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(),
darkTheme: ThemeData.dark(), // standard dark theme
themeMode: ThemeMode.system, // device controls theme
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
Add Static Accessor to StatefulWidget
adding this static of() method to our StatefulWidget makes its State object accessible for any descendant widget
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
/// ↓↓ ADDED
/// InheritedWidget style accessor to our State object.
static _MyAppState of(BuildContext context) =>
context.findAncestorStateOfType<_MyAppState>()!;
}
/// State object hidden ↓. Focusing on ↑ StatefulWidget here.
note the return Type of our of() method: _MyAppState
we're not getting the StatefulWidget, we're getting its State object: _MyAppState
_MyAppState will hold the "state" of our ThemeMode setting (in next step). This is what controls our app's current theme.
next in our _MyAppState class we'll add a ThemeMode "state" field and a method to change theme & rebuild our app
_MyAppState
below is our State class modified with:
a "state" field _themeMode
MaterialApp themeMode: arg using _themeMode state field value
changeTheme method
class _MyAppState extends State<MyApp> {
/// 1) our themeMode "state" field
ThemeMode _themeMode = ThemeMode.system;
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(),
darkTheme: ThemeData.dark(),
themeMode: _themeMode, // 2) ← ← ← use "state" field here //////////////
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
/// 3) Call this to change theme from any context using "of" accessor
/// e.g.:
/// MyApp.of(context).changeTheme(ThemeMode.dark);
void changeTheme(ThemeMode themeMode) {
setState(() {
_themeMode = themeMode;
});
}
}
next, we'll show how to access changeTheme() to change our theme & rebuild the app
Change Theme & Rebuild
below is an example of using the of() accessor method to find our State object and call its changeTheme method from the two buttons below which call:
MyApp.of(context).changeTheme(ThemeMode.light)
MyApp.of(context).changeTheme(ThemeMode.dark)
class MyHomePage extends StatelessWidget {
final String title;
MyHomePage({required this.title});
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Choose your theme:',
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
/// //////////////////////////////////////////////////////
/// Change theme & rebuild to show it using these buttons
ElevatedButton(
onPressed: () => MyApp.of(context).changeTheme(ThemeMode.light),
child: Text('Light')),
ElevatedButton(
onPressed: () => MyApp.of(context).changeTheme(ThemeMode.dark),
child: Text('Dark')),
/// //////////////////////////////////////////////////////
],
),
],
),
),
);
}
}
To return theme control back to the device's Dark mode setting, create a third button that makes a call to set themeMode: to ThemeMode.system:
MyApp.of(context).changeTheme(ThemeMode.system)
Running this method will delegate control of the app's theme back to whatever Dark mode setting the device is currently using.
Code: Complete copy-paste code available in this gist.

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.

Letting the system handle themes:
runApp(
MaterialApp(
theme: ThemeData.light(), // Provide light theme
darkTheme: ThemeData.dark(), // Provide dark theme
home: HomePage(),
),
);
Handling the themes yourself:
Use provider to set the theme programmatically. 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: ElevatedButton(
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.

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.

import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.light(), // Provide light theme.
darkTheme: ThemeData.dark(), // Provide dark theme.
themeMode: ThemeMode.system,
home: Scaffold(
appBar: AppBar(),
body: Container(),
),
);
}
}

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 ValueNotifier<ThemeMode> _notifier = ValueNotifier(ThemeMode.light);
#override
Widget build(BuildContext context) {
return ValueListenableBuilder<ThemeMode>(
valueListenable: _notifier,
builder: (_, mode, __) {
return MaterialApp(
theme: ThemeData.light(),
darkTheme: ThemeData.dark(),
themeMode: mode, // Decides which theme to show, light or dark.
home: Scaffold(
body: Center(
child: ElevatedButton(
onPressed: () => _notifier.value = mode == ThemeMode.light ? ThemeMode.dark : ThemeMode.light,
child: Text('Toggle Theme'),
),
),
),
);
},
);
}
}

Here is a code
In this code you i've made custom theme according to my requirements you can change it!!
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Theme',
debugShowCheckedModeBanner: false,
/* light theme settings */
theme: ThemeData(
primarySwatch: Colors.blue,
primaryColor: Colors.white,
brightness: Brightness.light,
accentColor: Colors.black,
accentIconTheme: IconThemeData(color: Colors.white),
dividerColor: Colors.white54,
scaffoldBackgroundColor: Colors.white,
),
/* Dark theme settings */
darkTheme: ThemeData(
primarySwatch: Colors.blue,
primaryColor: Colors.black,
brightness: Brightness.dark,
accentColor: Colors.white,
accentIconTheme: IconThemeData(color: Colors.black),
dividerColor: Colors.black12,
scaffoldBackgroundColor: Color(0xFF131313),
),
/* ThemeMode.system to follow system theme,
ThemeMode.light for light theme,
ThemeMode.dark for dark theme */
themeMode: ThemeMode.system,
home: MyHomePage(),
);
}
}

For Customize dark theme
as per your need use darkTheme: ThemeData( use theme properties you need in dark mode)
description:
if dark mode is selected in your system then flutter uses darkTheme property of MaterialApp and if light is selected then flutter uses theme property of MaterialApp, below code shows when you select (try it in your cellphone) dark option in your system then your app will show scaffoldBackgroundColor: Colors.red and if you select light then it will show scaffoldBackgroundColor: Colors.amber
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
darkTheme: ThemeData(brightness: Brightness.dark, scaffoldBackgroundColor: Colors.red),
theme: ThemeData(
scaffoldBackgroundColor: Colors.amber,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
Full code
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
darkTheme: ThemeData(brightness: Brightness.dark, scaffoldBackgroundColor: Colors.red),
// themeMode: ThemeMode.dark,
theme: ThemeData(
scaffoldBackgroundColor: Colors.amber,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
// print("brightness ${ColorScheme.}")
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'increment',
style: Theme.of(context).textTheme.headline4,
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}

theme: ThemeData.light(), // Provide light theme.
darkTheme: ThemeData.dark(), // Provide dark theme.
themeMode: ThemeMode.system,
//use only these three line for dynamic change theme respect to system theme.

Little late to the party you can implement it without any third party state management using the built-in ValueNotifier.This approach allows you to change the theme of your entire app from any part of the app.
Heres the dartpad demo
Complete code sample
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
final darkNotifier = ValueNotifier<bool>(false);
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ValueListenableBuilder<bool>(
valueListenable: darkNotifier,
builder: (BuildContext context, bool isDark, Widget? child) {
return MaterialApp(
title: 'Flutter Demo',
themeMode: isDark ? ThemeMode.dark : ThemeMode.light,
theme: ThemeData(primaryColor: Colors.blue),
darkTheme: ThemeData.dark(),
home: MyHomePage(
title: 'Homepage',
),
);
});
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
void dispose() {
// TODO: implement dispose
darkNotifier.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
bool isDark = darkNotifier.value;
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
_darkNotifier.value ? 'DarkMode' : 'LightMode',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
isDark = !isDark;
darkNotifier.value = isDark;
},
tooltip: 'Increment',
child: Icon(isDark ? Icons.wb_sunny_outlined : Icons.bubble_chart),
),
);
}
}

Below is the simple example for changing the theme light to dark
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:theme_mode_switch/notify.dart';
void main() {
runApp(
ChangeNotifierProvider(create: (context) => DarkMode(), child: MyApp()));
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
final thmode = Provider.of<DarkMode>(context); ///accessing the variable of provider class
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Dark Mode',
theme: ThemeData(
///here the value of darmode var is updationg by switching
brightness: thmode.darkMode ? Brightness.dark : Brightness.light,
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
final thmode = Provider.of<DarkMode>(context);
return Scaffold(
appBar: AppBar(
title: Text('Dark Mode'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(thmode.darkMode ? 'Dark' :'Light'),
CupertinoSwitch(
value: thmode.darkMode,
onChanged: (bool val) {
thmode.changemode();
},
),
],
),
));
}
}
class DarkMode with ChangeNotifier {
bool darkMode = true; ///by default it is true
///made a method which will execute while switching
changemode() {
darkMode = !darkMode;
notifyListeners(); ///notify the value or update the widget value
}
}

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?
Add the latest version of the package in your pubspec.yaml
Wrap the MaterialApp with DayNightTheme Widget.

I've found a very nice approach from ITnext where no third-party packages (except for either shared_preferences or hive) are necessary. Here a short summary (without the imports and with a switcher):
// this makes all variables available globally
library config.globals;
// initialize the theme model once
ThemeModel currentTheme = ThemeModel();
// also declare the box
Box? box;
config.dart
class ThemeModel with ChangeNotifier {
// initialize the standard theme here, possible with an elvis to check for the brightness
static bool _isDark = false;
// set a getter just for a more clean approach
bool get isDark => _isDark;
ThemeModel() {
// check for a stored value on initialization
if(box!.containsKey("currentTheme")) {
_isDark = box!.get("currentTheme");
} else {
// if there is no value, apply the standard theme
box!.put("currentTheme", _isDark);
}
}
ThemeMode currentTheme() {
return _isDark ? ThemeMode.dark : ThemeMode.light;
}
void switchTheme() {
// switches the theme by reversing the boolean
_isDark = !_isDark;
// storing the new value
box!.put("currentTheme", _isDark);
// notifies all listeners attached to the theme model
notifyListeners();
}
}
theme_model.dart
void main() async {
// waits for the hive init before running the app
box = await Hive.openBox("theme");
runApp(YourApp());
}
class YourApp extends StatefulWidget {
#override
_YourAppState createState() => _YourAppState();
}
class _YourAppState extends State<YourApp> {
#override
void initState() {
super.initState();
// we are setting a listener to the currentTheme,
// so it gets notified once we toggle it
currentTheme.addListener(() {
setState((){});
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Your title',
theme: ThemeData().light,
darkTheme: ThemeData().dark,
// it will always listen to changes made to currentTheme
themeMode: currentTheme.currentTheme(),
home: HomePage(),
);
}
}
main.dart
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Row(
children: <Widget>[
Switch(
// looking for the current value and sets the switch state
value: currentTheme.isDark,
onChanged: (value) {
setState(() {
// we then set the state to the new current theme
currentTheme.switchTheme();
});
},
),
// this is just a text next to the switch stating the current theme
Text("${currentTheme.currentTheme().toString().split(".")[1]} mode"),
],
);
);
}
}
homepage.dart
You can set the default value to ThemeData.system, if you want to get the users ui preferences. You have to adjust the code to look after the current brightness and then set the theme regarding to the state of it. After that it uses a switch to toggle between dark and light mode.
Example gif

Multiple Flutter themes example (Light and Dark theme)
Add provider in .yaml file
Declare runApp method like this
runApp(ChangeNotifierProvider(
create: (context) => ThemeState(),
child: MyApp(),
));
Create ThemeState class and extend it with ChangeNotitifer
import 'package:flutter/material.dart';
enum ThemeType { DARK, LIGHT }
class ThemeState extends ChangeNotifier {
bool _isDarkTheme = false;
ThemeState() {
getTheme().then((type) {
_isDarkTheme = type == ThemeType.DARK;
notifyListeners();
});
}
ThemeType get theme => _isDarkTheme ? ThemeType.DARK : ThemeType.LIGHT;
set theme(ThemeType type) => setTheme(type);
void setTheme(ThemeType type) async {
_isDarkTheme = type == ThemeType.DARK;
notifyListeners();
}
Future<ThemeType> getTheme() async {
return _isDarkTheme ? ThemeType.DARK : ThemeType.LIGHT;
}
}
In the MyApp class declare this in MaterialApp like this
theme: Provider.of<ThemeState>(context).theme == ThemeType.DARK
? ThemeData(
// Define the default brightness and colors.
brightness: Brightness.dark,
primaryColor: Colors.lightBlue[800],
// Define the default font family.
fontFamily: 'Georgia',
// Define the default `TextTheme`. Use this to specify the default
// text styling for headlines, titles, bodies of text, and more.
textTheme: const TextTheme(
headline1:
TextStyle(fontSize: 32.0, fontWeight: FontWeight.bold),
headline6:
TextStyle(fontSize: 16.0, fontStyle: FontStyle.italic),
bodyText2: TextStyle(fontSize: 10.0, fontFamily: 'Hind'),
),
)
: ThemeData(
// Define the default brightness and colors.
brightness: Brightness.light,
primaryColor: Colors.lightGreen[300],
// Define the default font family.
fontFamily: 'Georgia',
// Define the default `TextTheme`. Use this to specify the default
// text styling for headlines, titles, bodies of text, and more.
textTheme: const TextTheme(
headline1:
TextStyle(fontSize: 32.0, fontWeight: FontWeight.normal),
headline6:
TextStyle(fontSize: 16.0, fontStyle: FontStyle.italic),
bodyText2: TextStyle(fontSize: 10.0, fontFamily: 'Hind'),
),
),

much easier than you think with get package
return GetMaterialApp(
themeMode: lightOrDark?ThemeMode.light:ThemeMode.dark,
...
);

Related

Getx flutter change theme when i change theme dark and then return light mode its not loading my custom theme

Getx flutter when i change theme from light to dark mode and then return to light mode its not returning my custom theme its load old app bar background even theme changes
ThemeData lightTheme = ThemeData(
useMaterial3: true,
backgroundColor: Colors.white,
appBarTheme: AppBarTheme(
elevation: 1000,
backgroundColor: Colors.white,
)
);
ThemeData darkTheme = ThemeData.dark().copyWith(primaryColor: Colors.red);
Getx flutter when i change theme from light to dark mode and then return to light mode its not returning my custom theme its load old app bar background even theme changes
class _MyMaterialAppState extends State<MyMaterialApp> {
#override
Widget build(BuildContext context) {
return GetMaterialApp(
debugShowCheckedModeBanner: false,
title: appName,
theme: lightTheme,
darkTheme: darkTheme,
translations: Languages(),
locale: Get.deviceLocale,
fallbackLocale: const Locale('en', 'US'),
home: const MyHomePage(),
);
}
}
The documentation of GetX Itself says that you should not depend on any higher level widget than GetMaterialApp in order to update it. This can trigger duplicate keys.
I have seen your code and I tried to test this on physical devide and it works perfectly fine.
Here's the code:
import 'package:flutter/material.dart';
import 'package:get/route_manager.dart';
ThemeData lightTheme = ThemeData(
useMaterial3: true,
backgroundColor: Colors.white,
appBarTheme: AppBarTheme(
elevation: 1000,
backgroundColor: Colors.white,
));
ThemeData darkTheme = ThemeData.dark().copyWith(primaryColor: Colors.red);
void main(List<String> args) {
runApp(MyMaterialApp());
}
class MyMaterialApp extends StatelessWidget {
const MyMaterialApp({super.key});
#override
Widget build(BuildContext context) {
return GetMaterialApp(
debugShowCheckedModeBanner: false,
title: "appName",
theme: lightTheme,
darkTheme: darkTheme,
// translations: Languages(),
locale: Get.deviceLocale,
fallbackLocale: const Locale('en', 'US'),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("data"),
),
body: Column(
children: [
Center(
child: TextButton(
onPressed: () {
Get.changeTheme(ThemeMode.dark);
// Get.changeThemeMode(ThemeMode.dark);
},
child: Text("Chage"),
),
),
Center(
child: TextButton(
onPressed: () {
Get.changeThemeMode(ThemeMode.light);
},
child: Text("Chage light"),
),
),
],
),
);
}
}
Probably, this is because of the GetMaterialApp widget in the build method. Because the build method is called every time the widget is updated, this means that your custom theme will be overwritten. You can do something like this:
class _MyMaterialAppState extends State<MyMaterialApp> {
GetxController<ThemeData> _themeController = GetxController(lightTheme);
#override
Widget build(BuildContext context) {
return GetBuilder<GetxController<ThemeData>>(
init: _themeController,
builder: (_, theme) {
return GetMaterialApp(
debugShowCheckedModeBanner: false,
title: appName,
theme: theme.value,
darkTheme: darkTheme,
translations: Languages(),
locale: Get.deviceLocale,
fallbackLocale: const Locale('en', 'US'),
home: const MyHomePage(),
);
},
);
}
}
This code uses GetBuilder to listen for changes to the _themeController and update theme property.
To change the theme:
_themeController.updateValue(newTheme)

Flutter Basics: My app bar color is not changing

import 'package:flutter/material.dart';
void main() {
runApp(RecipeApp());
}
class RecipeApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
final ThemeData theme = ThemeData();
return MaterialApp(
title: 'Recipe Calculator',
theme: theme.copyWith(
colorScheme: theme.colorScheme.copyWith(
primary: Colors.grey,
secondary: Colors.black,
),
),
home: const MyHomePage(title: 'Recipe Calculator'),
);
}
}
This is the current code and above is the current output as per the code, the color remains blue and white instead of grey and black
running on my emulator using your code it works, try restarting your app completely.
import 'package:flutter/material.dart';
void main() {
runApp(RecipeApp());
}
class RecipeApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
final ThemeData theme = ThemeData();
return MaterialApp(
title: 'Recipe Calculator',
theme: theme.copyWith(
colorScheme: theme.colorScheme.copyWith(
primary: Colors.grey,
secondary: Colors.black,
),
),
home: const MyHomePage(title: 'Recipe Calculator'),
);
}
}
class MyHomePage extends StatelessWidget {
final title;
const MyHomePage({this.title});
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
);
}
}
Try below code hope its helpful to you ,I think you can used Scaffold Widget refer AppBar class here and refer Scaffold class here
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.grey,//change color on your need
title: Text(
'BottomNavigationBar Sample',
),
),
body:Container(),//or your widget
);
Your result screen->

I don't understand the point of the Theme class in Flutter

So I was working with Flutter and I needed a custom Widget for a ListView where every widget has its own theme based on some data.
The proper way to do it seems to be something like this:
class CustomWidget extends StatefulWidget {
CustomWidget({Key key, this.data}) : super(key: key);
final Color data;
#override
_CustomWidgetState createState() => _CustomWidgetState();
}
class _CustomWidgetState extends State<CustomWidget> {
#override
Widget build(BuildContext context) {
return Theme(
data: ThemeData(primaryColor: widget.data),
child: Builder(builder: (context) {
return Container(
color: Theme.of(context).primaryColor,
);
}),
);
}
}
But if I do it like this, what exactly is the advantage of this?
Specifically applying the color in the container? Why can't I just do color: widget.data?
Wouldn't it make more sense if things like TextTheme automatically applied to every Text() decadence of Theme()?
The ThemeData is used to configure a Theme or MaterialApp widget:
https://api.flutter.dev/flutter/material/ThemeData-class.html
The primaryColor value does not affect the color of the Text widget, from the docs:
The background color for major parts of the app (toolbars, tab bars, etc)
If you want to change the color of the text then you can use the textTheme property:
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
accentColor : Colors.black,
textTheme: TextTheme(bodyText2: TextStyle(color: Colors.purple)),
primaryColor : Colors.black
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
primaryColor: is used for the appbar
accentColor: is used to color the foreground
textTheme: will change the color of the text and style it
working example:
https://dartpad.dartlang.org/bba537def9dbfa771400309a4e6415ed

How to turn other screens to Dark Mode

I have managed to implement a Darkmode into my app, however I don't know how to implement dark mode into other screens, so that when I press a button in settings the whole app goes into dark mode. I know it has something to do with notifying the listeners but I don't know where to begin.
Currently I am using shared preferences to store the dark mode and I have a separate file for the theme.
My Question is effectively how do I implement dark mode into other screens?
Here is my main controller code
import 'themes.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() => runApp(Darkmode());
class Darkmode extends StatelessWidget {
#override
Widget build(BuildContext context) {
/// Here we are asynchronously passing an instance of SharedPreferences
/// to our Settings ChangeNotifier class and that in turn helps us
/// determine the basic app settings to be applied whenever the app is
/// launched.
return FutureBuilder<SharedPreferences>(
future: SharedPreferences.getInstance(),
builder:
(BuildContext context, AsyncSnapshot<SharedPreferences> snapshot) {
return ChangeNotifierProvider<Settings>.value(
value: Settings(snapshot.data),
child: _Darkmode(),
);
},
);
}
}
class _Darkmode extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: Provider.of<Settings>(context).isDarkMode
? setDarkTheme
: setLightTheme,
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
widget.title,
style: TextStyle(color: Theme.of(context).accentColor),
),
actions: <Widget>[
IconButton(
icon: Icon(Provider.of<Settings>(context).isDarkMode
? Icons.brightness_high
: Icons.brightness_low),
onPressed: () {
changeTheme(
Provider.of<Settings>(context, listen: false).isDarkMode
? false
: true,
context);
},
),
],
),
body: Center(),
);
}
void changeTheme(bool set, BuildContext context) {
///Call setDarkMode method inside our Settings ChangeNotifier class to
///Notify all the listeners of the change.
Provider.of<Settings>(context, listen: false).setDarkMode(set);
}
}
Here is the Shared Preferences file:
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
/// Class that contains all your app settings
/// Consists only of Theme setting as of now
class Settings extends ChangeNotifier {
final SharedPreferences sharedPreferences;
Settings(this.sharedPreferences);
bool get isDarkMode => sharedPreferences?.getBool("isDarkMode") ?? false;
void setDarkMode(bool val) {
sharedPreferences?.setBool("isDarkMode", val);
notifyListeners();
}
}```
Use the ThemeData() to apply the theme to whole app!
bool isDarkMode = Provider.of<Settings>(context).isDarkMode
return MaterialApp(
theme: ThemeData(
primaryColor: isDarkMode ? Colors.blueGrey[900] : Colors.white,
accentColor: isDarkMode ? null : Colors.pink,
scaffoldBackgroundColor: isDarkMode ? Colors.blueGrey[900] : Colors.white,
brightness: isDarkMode ? Brightness.dark : Brightness.light,
backgroundColor: isDarkMode ? Colors.white : Colors.blueGrey[900],
dividerTheme: DividerThemeData(endIndent: 10, indent: 10),
iconTheme: IconThemeData(
color: isDarkMode ? Colors.white : Colors.grey[900]),
textTheme: Theme.of(context)
.textTheme
.apply(
displayColor: model.drakModeState
? Colors.white
: Colors.grey[900],
bodyColor: model.drakModeState
? Colors.white
: Colors.grey[900]),
),
);
This how you can ahieve this.
use Consumer. this widget will let you listen to changes in Settings
pass the new theme to your MaterialApp.
Then combine them.
class _Darkmode extends StatelessWidget {
#override
Widget build(BuildContext context) {
//settingProvider is the current instance of Settings (up to date)
return Consumer<Settings>(builder: (ctx, settingProvider, child)=> MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: settingProvider.isDarkMode
? setDarkTheme
: setLightTheme,
home: MyHomePage(title: 'Flutter Demo Home Page'),
));
}
}
This is some kind of realtime detection. Any time you'll change your theme, this code will update it for the whole app

Declaring one or more custom Themes

I'm trying to create a custom theme inside a flutter project.
I've created a separate file (mycolors.dart) where i defined some colors (const myPrimaryColor = const Color(0xFFFF3900); etc etc)
Then, in main.dart i'm referring to these colors and a custom font but inside the Widget build...
How can I isolate the theme data (colors and font/text styles), let's say "separately", and to refer to it inside the class?
Can I also define 2 different themes and use them later in the project?
Many thanks.
import 'package:flutter/material.dart';
import 'package:my_repository/mycolors.dart';
import 'package:flutter_statusbarcolor/flutter_statusbarcolor.dart';
void main() {
runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
FlutterStatusbarcolor.setStatusBarColor(myPrimaryColor);
return MaterialApp(
theme: ThemeData(
fontFamily: 'Raleway',
primaryColor: myPrimaryColor,
accentColor: myAccentColor,
scaffoldBackgroundColor: myBackgroundColor,
cardColor: mySurfaceColor,
textSelectionColor: myPrimaryColor,
errorColor: myErrorColor,
),
home: Scaffold( ....
You can define your themes in a class and then call ThemeName().theme.
here is how I have a theme file in my app:
class MuskTheme {
...
ThemeData get theme => ThemeData(
brightness: Brightness.dark,
primarySwatch: musk,
accentColor: accentColor,
fontFamily: fontFamily,
backgroundColor: musk,
canvasColor:canvasColor,
textTheme: _textTheme,
iconTheme: _iconTheme,
cardColor: Color(0xff313A49),
appBarTheme: AppBarTheme(color: canvasColor,elevation: 0),
dialogBackgroundColor: canvasColor,
snackBarTheme: SnackBarThemeData(
backgroundColor: musk[800],
actionTextColor: accentColor,
),
);
...
}
for changing your theme during runtime you need to rebuild the MaterialApp widget by implementing a stateful widget that is higher than MaterialApp and can rebuild it upon request.
example implementation:
class ThemeChanger extends StatefulWidget {
final ThemeData initialTheme;
final MaterialApp Function(BuildContext context, ThemeData theme)
materialAppBuilder;
const ThemeChanger({Key key, this.initialTheme, this.materialAppBuilder})
: super(key: key);
#override
_ThemeChangerState createState() => _ThemeChangerState();
static void setTheme(BuildContext context, ThemeData theme) {
var state = context.ancestorStateOfType(TypeMatcher<_ThemeChangerState>())
as _ThemeChangerState;
state.setState(() {
state.theme = theme;
});
}
}
class _ThemeChangerState extends State<ThemeChanger> {
ThemeData theme;
#override
void initState() {
super.initState();
theme = widget.initialTheme;
}
#override
Widget build(BuildContext context) {
return widget.materialAppBuilder(context, theme);
}
}
then you'll need to build your MaterialApp with it:
class ThemeChangingApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ThemeChanger(
initialTheme: ThemeData(
primarySwatch: Colors.blue,
),
materialAppBuilder: (context, theme) {
return MaterialApp(
theme: theme,
home: StartingPage(),
);
},
);
}
}
and in your app you can change the theme like this:
class StartingPage extends StatefulWidget {
#override
_StartingPageState createState() => _StartingPageState();
}
class _StartingPageState extends State<StartingPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Container(
child: Center(
child: FlatButton(
child: Text('change theme to orange'),
onPressed: () {
ThemeChanger.setTheme(
context,
ThemeData(
primarySwatch: Colors.orange,
));
},
),
),
),
);
}
}
this package does a similar thing.
You can build a scaffold with a different theme just by warpping it with a Theme widget:
class StartingPage extends StatefulWidget {
#override
_StartingPageState createState() => _StartingPageState();
}
class _StartingPageState extends State<StartingPage> {
#override
Widget build(BuildContext context) {
return Theme(
data: ThemeData.dark(),
child: Scaffold(
appBar: AppBar(),
body: Container(
child: Center(
child: Text('test'),
),
),
),
);
}
}
final ThemeData customTheme = _buildcustomTheme();
ThemeData _buildcustomTheme() {
return customThemeFoundation;
}
ThemeData customThemeFoundation =ThemeData(
brightness: Brightness.dark,
primaryColor: Color.fromRGBO(40, 204, 86, 1),
accentColor: Colors.cyan[600],
fontFamily: 'Georgia',
textTheme: TextTheme(
headline1: TextStyle(fontSize: 72.0, fontWeight: FontWeight.bold),
headline6: TextStyle(fontSize: 36.0, fontStyle: FontStyle.italic),
bodyText2: TextStyle(fontSize: 14.0, fontFamily: 'Hind'),
),
and in main.dart just call it by
import 'theme.dart';
and just relpace theme:{.....} with theme: customTheme,