Building a Dark Mode in Flutter: A Complete Tutorial

Building a Dark Mode in Flutter: A Complete Tutorial

Master the art of dark mode implementation for stunning Flutter applications.

Introduction

Dark mode is no longer just a trend—it's an essential feature that enhances user experience, reduces eye strain, and improves battery efficiency on OLED devices. In Flutter, implementing dark mode is seamless thanks to its flexible theming capabilities. This tutorial provides a complete step-by-step guide on how to add dark mode to your Flutter applications, making them more appealing and functional.

By the end of this article, you’ll have the knowledge to implement dark mode and even customize it to fit your app’s brand.


Why Dark Mode?

Before diving into implementation, it’s important to understand why dark mode is so popular:

  1. User Experience: Dark mode reduces eye strain, especially in low-light environments.

  2. Battery Efficiency: Devices with OLED screens consume less power in dark mode.

  3. Modern Aesthetic: It provides a sleek and modern look to apps, making them more appealing.

Let’s now move on to the technical part—creating dark mode in Flutter.


Step 1: Setting Up Your Flutter Project

If you’re starting from scratch, create a new Flutter project:

flutter create dark_mode_example
cd dark_mode_example

For existing projects, ensure your pubspec.yaml file has the latest Flutter SDK dependencies.

Run the following command to fetch any missing dependencies:

flutter pub get

Step 2: Understanding Flutter Themes

Flutter uses ThemeData to define the look and feel of an application. Themes allow you to control colors, typography, and other visual aspects. Flutter’s MaterialApp widget makes it easy to apply themes.

You’ll need two themes: one for light mode and one for dark mode.

Here’s a basic example of defining light and dark themes:

import 'package:flutter/material.dart';

final ThemeData lightTheme = ThemeData(
  brightness: Brightness.light,
  primaryColor: Colors.blue,
  scaffoldBackgroundColor: Colors.white,
  textTheme: TextTheme(
    bodyText1: TextStyle(color: Colors.black),
  ),
);

final ThemeData darkTheme = ThemeData(
  brightness: Brightness.dark,
  primaryColor: Colors.teal,
  scaffoldBackgroundColor: Colors.black,
  textTheme: TextTheme(
    bodyText1: TextStyle(color: Colors.white),
  ),
);

Step 3: Integrating Dark Mode

To enable dark mode, you need to dynamically switch between the light and dark themes. This can be achieved using the ThemeMode property of MaterialApp.

Update your main.dart file as follows:

import 'package:flutter/material.dart';

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

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  ThemeMode _themeMode = ThemeMode.light;

  void _toggleThemeMode() {
    setState(() {
      _themeMode = _themeMode == ThemeMode.light ? ThemeMode.dark : ThemeMode.light;
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Dark Mode Example',
      theme: lightTheme,
      darkTheme: darkTheme,
      themeMode: _themeMode,
      home: HomeScreen(toggleThemeMode: _toggleThemeMode),
    );
  }
}

class HomeScreen extends StatelessWidget {
  final VoidCallback toggleThemeMode;

  HomeScreen({required this.toggleThemeMode});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Dark Mode Example')),
      body: Center(
        child: ElevatedButton(
          onPressed: toggleThemeMode,
          child: Text('Toggle Theme'),
        ),
      ),
    );
  }
}

Step 4: Persisting Theme Preferences

To save the user’s theme preference, you can use the shared_preferences package. Add it to your pubspec.yaml file:

dependencies:
  shared_preferences: ^2.0.0

Update your _toggleThemeMode method to persist the selected theme:

import 'package:shared_preferences/shared_preferences.dart';

void _toggleThemeMode() async {
  final prefs = await SharedPreferences.getInstance();
  setState(() {
    _themeMode = _themeMode == ThemeMode.light ? ThemeMode.dark : ThemeMode.light;
    prefs.setBool('isDarkMode', _themeMode == ThemeMode.dark);
  });
}

@override
void initState() {
  super.initState();
  _loadThemeMode();
}

void _loadThemeMode() async {
  final prefs = await SharedPreferences.getInstance();
  setState(() {
    _themeMode = prefs.getBool('isDarkMode') ?? false ? ThemeMode.dark : ThemeMode.light;
  });
}

Step 5: Customizing Dark Mode

To make your dark mode more appealing, consider customizing the theme colors and widgets. For instance, you can add animations or apply different accent colors for light and dark modes.

Example of custom widget styling:

class CustomButton extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      style: ElevatedButton.styleFrom(
        primary: Theme.of(context).primaryColor,
      ),
      onPressed: () {},
      child: Text('Custom Button'),
    );
  }
}

Best Practices for Dark Mode

  1. Contrast and Readability: Ensure sufficient contrast for text and UI elements.

  2. Avoid Pure Black: Use dark gray instead of pure black for better aesthetics.

  3. Test Thoroughly: Check the app on different devices to ensure a consistent experience.

  4. User Choice: Always give users the option to switch between modes.


Conclusion

Adding dark mode to your Flutter app is a valuable feature that improves user satisfaction and aligns with modern design trends. With Flutter’s robust theming capabilities, implementing dark mode is straightforward and highly customizable.

By following this tutorial, you can enhance your app’s appeal and provide a better user experience.