Implementing Dark Mode
1. Using ThemeMode
| Property | Values | Description |
|---|---|---|
| ThemeMode.system | ThemeMode.system |
Uses the system's dark/light mode setting. |
| ThemeMode.light | ThemeMode.light |
Forces the app to use light mode. |
| ThemeMode.dark | ThemeMode.dark |
Forces the app to use dark mode. |
Example: Setting ThemeMode in MaterialApp
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
themeMode: ThemeMode.system, // or ThemeMode.light, ThemeMode.dark
theme: ThemeData.light(),
darkTheme: ThemeData.dark(),
home: Scaffold(
appBar: AppBar(title: Text("Dark Mode Demo")),
body: Center(child: Text("Hello, World!")),
),
);
}
}
2. Defining Dark Theme
| Property | Value | Description |
|---|---|---|
| brightness | Brightness.dark |
Defines the theme's brightness level |
| primaryColor | Colors.blue |
Primary color used for app bars, buttons, etc. |
| scaffoldBackgroundColor | Colors.grey[900] |
Background color for the main screen |
| cardColor | Colors.grey[800] |
Color for cards and containers |
| textTheme | TextTheme.dark() |
Defines text styles for dark mode |
Example: Defining a Dark Theme
final ThemeData darkTheme = ThemeData(
brightness: Brightness.dark,
primaryColor: Colors.blue,
scaffoldBackgroundColor: Colors.grey[900],
cardColor: Colors.grey[800],
textTheme: TextTheme.dark(),
);
3. Detecting System Theme
| Property | Description | Values |
|---|---|---|
| ThemeMode.system | Uses the system's theme setting | ThemeMode.system |
| ThemeMode.light | Forces light theme | ThemeMode.light |
| ThemeMode.dark | Forces dark theme | ThemeMode.dark |
Example: Detecting System Theme
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.light(),
darkTheme: ThemeData.dark(),
themeMode: ThemeMode.system, // Detects system theme
home: Scaffold(
appBar: AppBar(title: Text("Theme Demo")),
body: Center(
child: Text("System theme detected"),
),
),
);
}
}
Note:
ThemeMode.system will automatically switch between light and dark themes based on the user's system settings.
4. Switching Themes Dynamically
| Property | Description | Example |
|---|---|---|
| ThemeMode | Defines the current theme mode (light, dark, system) | ThemeMode.dark |
| ChangeNotifier | Used to notify UI when theme changes | ChangeNotifier() |
Example: Theme Switcher Widget
class ThemeChanger extends ChangeNotifier {
ThemeMode _themeMode = ThemeMode.system;
ThemeMode get themeMode => _themeMode;
void setTheme(ThemeMode theme) {
_themeMode = theme;
notifyListeners();
}
}
class ThemeSwitcher extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Consumer<ThemeChanger>(
builder: (context, themeChanger, child) {
return MaterialApp(
themeMode: themeChanger.themeMode,
theme: ThemeData.light(),
darkTheme: ThemeData.dark(),
);
},
);
}
}
Example: Toggle Theme Button
IconButton(
icon: Icon(Icons.brightness_4),
onPressed: () {
final themeChanger = context.read<ThemeChanger>();
if (themeChanger.themeMode == ThemeMode.light) {
themeChanger.setTheme(ThemeMode.dark);
} else {
themeChanger.setTheme(ThemeMode.light);
}
},
)
Note: The
ChangeNotifier pattern ensures that UI updates when the theme changes dynamically.
5. Using Brightness Enum
| Property | Description | Values |
|---|---|---|
| Brightness.light | Represents a light theme | Brightness.light |
| Brightness.dark | Represents a dark theme | Brightness.dark |
Example: Checking Brightness
final brightness = WidgetsBinding.instance.window.platformBrightness;
if (brightness == Brightness.dark) {
// Apply dark theme
} else {
// Apply light theme
}
6. Creating Dark Theme ColorScheme
| Property | Values | Description |
|---|---|---|
| primary | Color(0xFF6200EE) |
Primary color for the theme |
| onPrimary | Color(0xFFFFFFFF) |
Color for content on primary color |
| secondary | Color(0xFF03DAC6) |
Secondary color for the theme |
| onSecondary | Color(0xFF000000) |
Color for content on secondary color |
| surface | Color(0xFF121212) |
Background color for surfaces |
| onSurface | Color(0xFFFFFFFF) |
Color for content on surface |
| background | Color(0xFF121212) |
Background color for the app |
| onBackground | Color(0xFFFFFFFF) |
Color for content on background |
| error | Color(0xFFB00020) |
Error color for the theme |
| onError | Color(0xFFFFFFFF) |
Color for content on error color |
Example: Defining a Dark ColorScheme
final darkColorScheme = ColorScheme(
primary: Color(0xFF6200EE),
onPrimary: Color(0xFFFFFFFF),
secondary: Color(0xFF03DAC6),
onSecondary: Color(0xFF000000),
surface: Color(0xFF121212),
onSurface: Color(0xFFFFFFFF),
background: Color(0xFF121212),
onBackground: Color(0xFFFFFFFF),
error: Color(0xFFB00020),
onError: Color(0xFFFFFFFF),
);
7. Customizing Widgets for Dark Mode
| Widget | Dark Mode Properties | Notes |
|---|---|---|
| Text | style: TextStyle(color: Colors.white) |
Use Colors.white for better contrast in dark mode |
| Container | decoration: BoxDecoration(color: Colors.grey[800]) |
Set background color to a dark shade like Colors.grey[800] |
| Card | color: Colors.grey[900] |
Use Colors.grey[900] for a dark card background |
| AppBar | backgroundColor: Colors.grey[900] |
Set backgroundColor to a dark color for contrast |
Example: Customizing Text for Dark Mode
<Text
text "Hello, Dark Mode!"
style="TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.bold
)"
/>
Example: Customizing Container for Dark Mode
<Container
decoration: BoxDecoration(
color: Colors.grey[800],
borderRadius: BorderRadius.circular(8)
)
child: Text(
"Dark Background",
style: TextStyle(color: Colors.white)
)
/>
8. Using Theme Extensions
| Property | Description | Values | Notes |
|---|---|---|---|
| ThemeExtension<T> | Base class for creating custom theme extensions | ThemeExtension<T> |
Used to extend Flutter's built-in themes with custom properties |
| copyWith | Creates a new instance with specified properties overridden | ThemeExtension<T> copyWith({T? property}) |
Used to create a modified version of the extension |
| merge | Merges this extension with another of the same type | ThemeExtension<T> merge(ThemeExtension<T> other) |
Used to combine two theme extensions |
| maybeResolve | Resolves a theme extension in the context of a given theme | ThemeExtension<T>? maybeResolve(ThemeData theme) |
Used to resolve a theme extension in the context of an existing theme |
Example: Creating a Custom Theme Extension
class CustomThemeExtension extends ThemeExtension<CustomThemeExtension> {
final Color primaryColor;
CustomThemeExtension({
required this.primaryColor,
});
@override
ThemeExtension<CustomThemeExtension> copyWith({
Color? primaryColor,
}) {
return CustomThemeExtension(
primaryColor: primaryColor ?? this.primaryColor,
);
}
@override
ThemeExtension<CustomThemeExtension> merge(
ThemeExtension<CustomThemeExtension> other) {
if (other is CustomThemeExtension) {
return CustomThemeExtension(
primaryColor: other.primaryColor,
);
}
return this;
}
@override
ThemeExtension<CustomThemeExtension>? maybeResolve(
ThemeData theme) {
return this;
}
}
Example: Applying a Custom Theme Extension
final customTheme = ThemeData(
extensions: [
CustomThemeExtension(primaryColor: Colors.blue),
],
);
MaterialApp(
theme: customTheme,
home: Scaffold(
appBar: AppBar(
title: Text('Custom Theme Example'),
),
body: Center(
child: Text('Hello, World!'),
),
),
);
Note: Theme extensions are a powerful way to customize Flutter themes beyond the built-in properties. They allow for more granular control over theme elements and can be used to create complex, reusable themes.
9. Storing Theme Preferences
Example: Saving theme preference to SharedPreferences
import 'package:shared_preferences/shared_preferences.dart';
Future<void> saveThemeMode(ThemeMode themeMode) async {
final prefs = await SharedPreferences.getInstance();
prefs.setString('theme_mode', themeMode.toString().split('.').last);
}
ThemeMode loadThemeMode() {
final prefs = SharedPreferences.getInstance();
final themeString = prefs.getString('theme_mode') ?? 'system';
switch (themeString) {
case 'light':
return ThemeMode.light;
case 'dark':
return ThemeMode.dark;
default:
return ThemeMode.system;
}
}
| Preference Key | Value Type | Description |
|---|---|---|
| theme_mode | string |
Stores the selected theme mode: 'light', 'dark', or 'system' |
Note: The
shared_preferences package is commonly used for simple key-value storage in Flutter apps.
Warning: Avoid storing large objects or complex data structures in shared preferences. Use it only for simple settings like theme mode.
10. Using Material 3
| Property | Values | Description |
|---|---|---|
| themeMode | ThemeMode.system, ThemeMode.light, ThemeMode.dark |
Controls the theme mode of the app. Defaults to ThemeMode.system. |
| useMaterial3 | true, false |
Enables or disables Material 3 (Material Design 3) for the app. Defaults to true. |
Example: Configuring Material 3 Theme
MaterialApp(
themeMode: ThemeMode.system,
useMaterial3: true,
theme: ThemeData.light(),
darkTheme: ThemeData.dark(),
)
Note: Material 3 is the latest design language from Google, offering improved accessibility and a more consistent look across platforms.