Supporting dark mode in your application can improve user readability by reducing the light emitted by device screens. Both Android and iPhone devices offer system-wide dark modes. However, you will still need to set up dark mode on individual apps.
In this tutorial I will show you how to implement dark mode in your Flutter application:
To complete this tutorial you will need to:
Download and install Visual Studio Code or Android Studio.
Download and install Flutter.
Set up your editor as described here.
Once you have your environment set up for Flutter you can run the following command to create a new application.
$ flutter create dark\_mode\_example
Open the pubspec.yaml for the newly created project and add the Riverpod package like this:
dependencies:
flutter:
sdk: flutter
flutter_riverpod: ^0.14.0+3
Run the command “flutter pub get” in your terminal to include the package in your project.
The Material App
The Material app has three properties that are of interest to us, namely:
theme
darkTheme
themeMode
The “theme” property is used to provide the light theme for the app, while the “darkTheme” property is used to provide the dark theme for the app. The “themeMode” property is an enum to select which of the themes to use for the app.
1 2 3 4 5 6 7 8 9 10 11
enum ThemeMode { // Use either the light or dark theme based on what the user has selected in // the system settings. system, // Regardless of system settings, always use the light mode. light, // Regardless of the system settings, always use the dark mode. dark, }
Add the following code to the Material app.
1 2 3 4 5 6
theme: ThemeData( brightness: Brightness.light, primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, // Add any other property here )
Add the following code to the Material app.
1 2 3 4 5
darkTheme: ThemeData( brightness: Brightness.dark, visualDensity: VisualDensity.adaptivePlatformDensity, // Add any other property here )
To be able to implement dynamic theming we will use the Riverpod package to provide the theme mode.
Create a state provider to provide the theme mode as follows.
final themeModeProvider = StateProvider<ThemeMode>((ref)=>ThemeMode.system);
The OS theme setting will be used by default based on what the user has selected in the system settings.
In order to use the Riverpod package you will have to wrap the whole application widget in a provider scope, your main method should look like this:
1 2 3
void main() { runApp(ProviderScope(child: DarkModeExample())); }
The DarkModeExample widget should extend the consumer widget from Riverpod to access the theme mode provider.
1
2
3
4
5
6
class DarkModeExample extends ConsumerWidget {
@override
Widget build(BuildContext context, ScopedReader watch) {
final themeMode = watch(themeModeProvider)
.state;
The watch method from the Riverpod package notifies us when there is a new themeMode and the DarkModeExample widget gets rebuilt as a result. Now that we have our themeMode, we can safely provide it for the Material app as follow:
1 2 3
return MaterialApp( title: 'Dark Mode Demo', themeMode: themeMode, …
This tutorial will build a simple home page consisting of a text widget and a Cupertino switch to toggle between the light mode and the dark mode.
In order to manage the state of the Cupertino switch, we need a new provider as follows:
1 2 3 4 5
final isDarkModeProvider = Provider < bool > ((ref) { final themeMode = ref.watch(themeModeProvider) .state; return themeMode == ThemeMode.dark; });
Notice how this provider watches themeModeProvider by calling ref.watch(themeModeProvider).state, and then returns true if the themeMode is ThemeMode.dark or false if otherwise.
Create the home page widget and extend the stateless widget as follows.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("Dark Mode", style: TextStyle(fontSize: 28), ),
CupertinoSwitch(
value: ,
onChanged: (value) {
if (value) {
context.read(themeModeProvider)
.state = ThemeMode.dark;
} else {
context.read(themeModeProvider)
.state = ThemeMode.light;
}
})
],
),
),
);
}
}
The on changed method in the Cupertino switch is used to toggle the theme by calling context.read(themeModeProvider).state and setting it to the appropriate theme mode, notice that the value property is empty. Finally, we will use the consumer widget from Riverpod to listen to the state of isDarkModeProvider, and then provide it to the Cupertino switch as follows:
1 2 3 4 5
Consumer( builder: (BuildContext context, T Function < T > (ProviderBase < Object, T > ) watch, Widget child) { final isDarkMode = watch(isDarkModeProvider); return CupertinoSwitch( value: isDarkMode,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
void main() {
runApp(ProviderScope(child: DarkModeExample()));
}
class DarkModeExample extends ConsumerWidget {
@override
Widget build(BuildContext context, ScopedReader watch) {
final themeMode = watch(themeModeProvider)
.state;
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Dark Mode Demo',
themeMode: themeMode,
theme: ThemeData(
brightness: Brightness.light,
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
// Add any other property here
),
darkTheme: ThemeData(
brightness: Brightness.dark,
visualDensity: VisualDensity.adaptivePlatformDensity,
// Add any other property here
),
home: HomePage(),
);
}
}
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Dark Mode",
style: TextStyle(fontSize: 28),
),
Consumer(
builder: (BuildContext context,
T Function < T > (ProviderBase < Object, T > ) watch, Widget child) {
final isDarkMode = watch(isDarkModeProvider);
return CupertinoSwitch(
value: isDarkMode,
onChanged: (value) {
if (value) {
context.read(themeModeProvider)
.state = ThemeMode.dark;
} else {
context.read(themeModeProvider)
.state = ThemeMode.light;
}
});
},
)
],
),
),
);
}
}
final themeModeProvider = StateProvider < ThemeMode > ((ref) => ThemeMode.system);
final isDarkModeProvider = Provider < bool > ((ref) {
final themeMode = ref.watch(themeModeProvider)
.state;
return themeMode == ThemeMode.dark;
});
Github gist: https://gist.github.com/De-Morgan/f5508fd4dbe2daeaa492e589c7c8f539
Dart pad link: https://dartpad.dev/?id=f5508fd4dbe2daeaa492e589c7c8f539
CHECK OUT TOPCODER FLUTTER FREELANCE GIGS