In this post, we’ll learn how to add theme switching in Flutter — letting users toggle between Light Mode, Dark Mode, and System Default Theme.
We’ll use the Provider package to manage the state and a simple Switch widget to control the theme dynamically.
What We’ll Build
We’ll create:
- A custom theme file with
lightThemeanddarkTheme - A ThemeProvider using
ChangeNotifier - A simple UI with a
Switchand anElevatedButtonto toggle themes
Step 1: Create the AppTheme File
Create a new file named app_theme.dart.
This file will hold both the light and dark theme configurations.
import 'package:flutter/material.dart';
class AppTheme {
// Light Theme
static final ThemeData lightTheme = ThemeData(
brightness: Brightness.light,
colorScheme: const ColorScheme.light(
primary: Color(0xFF0066FF),
secondary: Color(0xFF00C853),
surface: Colors.white,
background: Color(0xFFF5F5F5),
onPrimary: Colors.white,
onSecondary: Colors.white,
onSurface: Colors.black,
onBackground: Colors.black,
error: Colors.redAccent,
),
scaffoldBackgroundColor: Colors.white,
appBarTheme: const AppBarTheme(
backgroundColor: Color(0xFF0066FF),
foregroundColor: Colors.white,
elevation: 0,
),
floatingActionButtonTheme: const FloatingActionButtonThemeData(
backgroundColor: Colors.lightBlueAccent,
),
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF0066FF),
foregroundColor: Colors.white,
textStyle: const TextStyle(fontWeight: FontWeight.bold),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
),
),
textTheme: const TextTheme(
bodyLarge: TextStyle(color: Colors.red, fontSize: 16),
bodyMedium: TextStyle(color: Colors.black, fontSize: 14),
titleLarge: TextStyle(color: Colors.blue, fontWeight: FontWeight.bold),
),
inputDecorationTheme: InputDecorationTheme(
filled: true,
fillColor: Colors.greyAccent,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide.none,
),
),
);
// 🌚 Dark Theme
static final ThemeData darkTheme = ThemeData(
brightness: Brightness.dark,
colorScheme: const ColorScheme.dark(
primary: Color(0xFF90CAF9),
secondary: Color(0xFF00E676),
surface: Color(0xFF121212),
background: Color(0xFF1E1E1E),
onPrimary: Colors.black,
onSecondary: Colors.black,
onSurface: Colors.white,
onBackground: Colors.white,
error: Colors.redAccent,
),
scaffoldBackgroundColor: Colors.black,
appBarTheme: const AppBarTheme(
backgroundColor: Color(0xFF121212),
foregroundColor: Colors.white,
elevation: 0,
),
floatingActionButtonTheme: const FloatingActionButtonThemeData(
backgroundColor: Colors.blue,
),
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF90CAF9),
foregroundColor: Colors.black,
textStyle: const TextStyle(fontWeight: FontWeight.bold),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
),
),
textTheme: const TextTheme(
bodyLarge: TextStyle(color: Colors.yellow, fontSize: 16),
bodyMedium: TextStyle(color: Colors.white, fontSize: 14),
titleLarge: TextStyle(color: Colors.pink, fontWeight: FontWeight.bold),
),
inputDecorationTheme: InputDecorationTheme(
filled: true,
fillColor: Colors.grey,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide.none,
),
),
);
}
Note: You don’t have to copy everything — use what your app needs!
Step 2: Create the ThemeProvider
Now, let’s manage our theme state using the Provider package.
Create a new file named theme_provider.dart:
import 'package:flutter/material.dart';
class ThemeProvider extends ChangeNotifier {
ThemeMode _themeMode = ThemeMode.system;
ThemeMode get themeMode => _themeMode;
// Toggle between Light and Dark
void toggleTheme(bool isDark) {
_themeMode = isDark ? ThemeMode.dark : ThemeMode.light;
notifyListeners();
}
// Set back to System Default
void setSystemTheme() {
_themeMode = ThemeMode.system;
notifyListeners();
}
}
Step 3: Connect the Provider to Your App
Wrap your MaterialApp with a ChangeNotifierProvider and set your theme values like this:
return ChangeNotifierProvider(
create: (_) => ThemeProvider(),
child: Consumer<ThemeProvider>(
builder: (context, themeProvider, child) {
return MaterialApp(
debugShowCheckedModeBanner: false,
themeMode: themeProvider.themeMode,
theme: AppTheme.lightTheme,
darkTheme: AppTheme.darkTheme,
home: const HomePage(),
);
},
),
);
Step 4: Add the Switch and Button in Your UI
Inside your HomePage widget, add a Switch to toggle between light and dark themes.
bool isDarkMode = themeProvider.themeMode == ThemeMode.dark;
Switch(
value: isDarkMode,
onChanged: (value) {
themeProvider.toggleTheme(value);
},
),
And for switching back to the system theme, add this ElevatedButton:
ElevatedButton.icon(
icon: const Icon(Icons.settings_brightness_outlined),
label: const Text("Follow System Theme"),
style: ElevatedButton.styleFrom(
backgroundColor: theme.colorScheme.primary,
foregroundColor: theme.colorScheme.onPrimary,
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 14),
),
onPressed: () => themeProvider.setSystemTheme(),
),
Done!
You now have:
- A light and dark theme setup
- A Provider controlling theme changes
- A UI switch and button to toggle and reset themes
Wrapping Up
That’s it! You’ve just given your Flutter app the power of dynamic theming
If you found this helpful, drop a or leave a comment below —
this is my first post, and I’d love your feedback!
I’ll be happy to answer any questions you have — within my current knowledge
Top comments (0)