Dark-mode changer

Dark-mode changer #

Have you ever wanted to add a way to change between light and dark mode in your application?

Here is a simple way to let users change the brightness of your app.

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  static final ValueNotifier<ThemeMode> themeNotifier =
      ValueNotifier(ThemeMode.system);

  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return ValueListenableBuilder<ThemeMode>(
      valueListenable: themeNotifier,
      builder: (context, themeMode, child) => MaterialApp(
        title: 'Charts Examples',
        theme: ThemeData.light(),
        darkTheme: ThemeData.dark(),
        themeMode: themeMode,
        home: Scaffold(
          body: Center(
            child: Padding(
              padding: EdgeInsets.all(50),
              child: ThemeChanger(),
            ),
          ),
        ),
      ),
    );
  }
}

class ThemeChanger extends StatelessWidget {
  const ThemeChanger({super.key});

  @override
  Widget build(BuildContext context) {
    final modes = [
      (ThemeMode.system, Icons.invert_colors),
      (ThemeMode.dark, Icons.dark_mode),
      (ThemeMode.light, Icons.light_mode),
    ];
    return DropdownButton(
      value: MyApp.themeNotifier.value,
      items: [
        for (final (mode, icon) in modes)
          DropdownMenuItem(
            value: mode,
            child: Row(
              children: [
                Icon(icon),
                const SizedBox(width: 8),
                Text(mode.name),
              ],
            ),
          ),
      ],
      onChanged: (value) {
        MyApp.themeNotifier.value = value as ThemeMode;
      },
    );
  }
}

The ThemeChanger widget can anywhere in the application.

A ValueNotifier is used. It is a wrapper for a single immutable type, allowing observers to get notified when the wrapped value is changed.

Those changes are listened to by a ValueListenableBuilder widget. It rebuilds using the builder function each time the value listened to changes.

You can also have a Switch toggle between light and dark mode.

import 'package:flutter/material.dart';

class ThemeChanger extends StatefulWidget {
  const ThemeChanger({super.key});

  @override
  State<ThemeChanger> createState() => _ThemeChangerState();
}

class _ThemeChangerState extends State<ThemeChanger> {
  @override
  Widget build(BuildContext context) {
    return Switch(
      value: MyApp.themeNotifier.value == ThemeMode.dark,
      onChanged: (darkMode) {
        setState(() {
          MyApp.themeNotifier.value =
              darkMode ? ThemeMode.dark : ThemeMode.light;
        });
      },
    );
  }
}

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  static final ValueNotifier<ThemeMode> themeNotifier =
      ValueNotifier(ThemeMode.dark);

  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return ValueListenableBuilder<ThemeMode>(
      valueListenable: themeNotifier,
      builder: (context, themeMode, child) => MaterialApp(
        title: 'Charts Examples',
        theme: ThemeData.light(),
        darkTheme: ThemeData.dark(),
        themeMode: themeMode,
        home: Scaffold(
          body: Center(
            child: Padding(
              padding: EdgeInsets.all(50),
              child: ThemeChanger(),
            ),
          ),
        ),
      ),
    );
  }
}