- Published on
 
System Design: Add Dynamic Themes & Light/Dark Mode in Flutter using Provider & Notify Listeners
- Authors
 - Name
 - Loi Tran
 
Introduction

Light & Dark mode's are a common requirement in modern web & mobile apps.
We'll use the following packages to implement this requirement:
We'll also add the ability to dynamically change the theme so that we're prepared for any updates a designer might send our way.
- Define Theme Provider.
- This will hold helper methods to change brightness modes between 
lightanddark - It'll notify listeners in the event that a change is detected.
 
 - This will hold helper methods to change brightness modes between 
 - Add 
AppThemeclass which stores theme data.- Store configuration for colors and 
enumsfor maintainability. 
 - Store configuration for colors and 
 - Utilize 
themeProviderinmain.dart- Listen for notifications using 
ChangeNotifierProviderand passing it it's required params,create&builder. - Inject the theme into the app by leveraging 
MaterialApp'stheme,darkTheme, &themeModeparams. 
 - Listen for notifications using 
 
  import 'package:flutter/material.dart';
  import 'package:theme_demo/theme.dart';
  class ThemeProvider extends ChangeNotifier {
    ThemeMode themeMode = ThemeMode.system;
    ThemeData theme = AppTheme.lightBlue;
    ThemeData darkTheme = AppTheme.darkBlue;
    void changeTheme(ThemeType themeType) {
      theme = AppTheme.getTheme(themeType);
      darkTheme = AppTheme.getDarkTheme(themeType);
      notifyListeners();
    }
    void toggleTheme() {
      themeMode = themeMode == ThemeMode.light ? ThemeMode.dark : ThemeMode.light;
      notifyListeners();
    }
  }
  import 'package:flutter/material.dart';
  class AppTheme {
    static ThemeData lightBlue = ThemeData(
        brightness: Brightness.light,
        colorScheme: ColorScheme.fromSeed(seedColor: ColorConstants.blue));
    static ThemeData darkBlue = ThemeData(
        brightness: Brightness.dark,
        colorScheme: ColorScheme.fromSeed(
          seedColor: ColorConstants.blue,
          brightness: Brightness.dark,
        ));
    static ThemeData lightRed = ThemeData.from(
        colorScheme: ColorScheme.fromSeed(seedColor: ColorConstants.red));
    static ThemeData darkRed = ThemeData.from(
        colorScheme: ColorScheme.fromSeed(
      seedColor: ColorConstants.red,
      brightness: Brightness.dark,
    ));
    static ThemeData lightGreen = ThemeData.from(
        colorScheme: ColorScheme.fromSeed(seedColor: ColorConstants.green));
    static ThemeData darkGreen = ThemeData.from(
        colorScheme: ColorScheme.fromSeed(
      seedColor: ColorConstants.green,
      brightness: Brightness.dark,
    ));
    AppTheme._();
    static ThemeData getDarkTheme(ThemeType themeType) {
      switch (themeType) {
        case ThemeType.red:
          return darkRed;
        case ThemeType.blue:
          return darkBlue;
        case ThemeType.green:
          return darkGreen;
      }
    }
    static ThemeData getTheme(ThemeType themeType) {
      switch (themeType) {
        case ThemeType.red:
          return lightRed;
        case ThemeType.blue:
          return lightBlue;
        case ThemeType.green:
          return lightGreen;
      }
    }
  }
  class ColorConstants {
    static const blue = Color(0xFF0000FF);
    static const red = Color(0xFFFF0000);
    static const green = Color.fromARGB(255, 15, 147, 66);
    static const orange = Color.fromARGB(255, 238, 119, 0);
    ColorConstants._();
  }
  enum ThemeType { red, blue, green }
  import 'package:flutter/material.dart';
  import 'package:provider/provider.dart';
  import 'package:theme_demo/menu_icon.dart';
  import 'package:theme_demo/theme.dart';
  import 'package:theme_demo/theme_provider.dart';
  void main() {
    runApp(const MyApp());
  }
  class MyApp extends StatelessWidget {
    const MyApp({super.key});
    
    Widget build(BuildContext context) {
      return ChangeNotifierProvider(
        create: (_) => ThemeProvider(),
        builder: (context, _) {
          final themeProvider = Provider.of<ThemeProvider>(context);
          return MaterialApp(
            title: 'Theme Demo',
            theme: themeProvider.theme,
            darkTheme: themeProvider.darkTheme,
            themeMode: themeProvider.themeMode,
            home: const MyHomePage(title: 'Theme Demo'),
          );
        },
      );
    }
  }
  class MyHomePage extends StatefulWidget {
    final String title;
    const MyHomePage({super.key, required this.title});
    
    State<MyHomePage> createState() => _MyHomePageState();
  }
  class _MyHomePageState extends State<MyHomePage> {
    int _counter = 0;
    late ThemeProvider themeProvider;
    
    Widget build(BuildContext context) {
      return Scaffold(
        appBar: AppBar(
          backgroundColor: Theme.of(context).colorScheme.inversePrimary,
          title: Text(widget.title),
          actions: const [
            MenuIcon(),
          ],
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              const Text(
                'You have pushed the button this many times:',
              ),
              Text(
                '$_counter',
                style: Theme.of(context).textTheme.headlineMedium,
              ),
              const SizedBox(height: 60),
              Column(
                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                children: ThemeType.values.map((e) {
                  return ElevatedButton(
                    onPressed: () {
                      themeProvider.changeTheme(e);
                    },
                    child: Text(e.toString()),
                  );
                }).toList(),
              ),
            ],
          ),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: _incrementCounter,
          tooltip: 'Increment',
          child: const Icon(Icons.add),
        ),
      );
    }
    
    void didChangeDependencies() {
      super.didChangeDependencies();
      themeProvider = Provider.of<ThemeProvider>(context);
    }
    void _incrementCounter() {
      setState(() {
        _counter++;
      });
      themeProvider.toggleTheme();
    }
  }
Conclusion
By using a few SDKs and packages we're able to quickly & easily add a modern feature making our app user friendly & flexible.