DEV Community

Cover image for Give Your Flutter App the Power of Theme Switching (Light, Dark & System)
niranjantk
niranjantk

Posted on

Give Your Flutter App the Power of Theme Switching (Light, Dark & System)

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 lightTheme and darkTheme
  • A ThemeProvider using ChangeNotifier
  • A simple UI with a Switch and an ElevatedButton to 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,
      ),
    ),
  );
}
Enter fullscreen mode Exit fullscreen mode

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();
  }
}
Enter fullscreen mode Exit fullscreen mode

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(),
      );
    },
  ),
);
Enter fullscreen mode Exit fullscreen mode

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);
  },
),
Enter fullscreen mode Exit fullscreen mode

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(),
),
Enter fullscreen mode Exit fullscreen mode

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)