Implementing Dark Mode in Flutter using Provider

Dark mode has become a popular feature in many mobile and web applications. It provides a visually appealing alternative to the default light mode, reduces eye strain, and saves battery life on devices with OLED screens. In this tutorial, we will explore how to implement dark mode in a Flutter app using the Provider package for state management.

Dark mode - Provider

Folder Structure

Before we dive into the code, let's set up the folder structure for our Flutter project. We will follow the standard Flutter project structure, which looks like this:

- lib/
  - main.dart
  - models/
    - dark_mode_provider.dart
  - themes/
    - theme_config.dart
  - screens/
    - dark_mode_page.dart

In the `models` folder, we will create a file called `dark_mode_provider.dart` to define our `DarkModeProvider` class. This class will manage the state of the dark mode feature. The `themes` folder will contain a file called `theme_config.dart`, where we define our light and dark themes. Finally, in the `screens` folder, we will create a file called `dark_mode_page.dart`, which will display the UI for toggling between light and dark modes.

Code Explanation

Now, let's dive into the code. We start by importing the necessary packages:

Next, we define the main entry point for our Flutter app:

Inside the `MyApp` widget, we define a `BuildContext` variable called `currentContext` that will be used later to access the current context:

We also define a `textTheme` variable to apply the display color from the current context's theme:

late BuildContext currentContext;

final textTheme = Theme.of(currentContext)
    .apply(displayColor: Theme.of(currentContext).colorScheme.onSurface);

Note that we use the `late` keyword to indicate that the variable will be initialized later.

Moving on to the `DarkModeProvider` class, which extends `ChangeNotifier`, we define it in the `dark_mode_provider.dart` file:

class DarkModeProvider with ChangeNotifier {
  bool _isDarkMode = false;

  bool get isDarkMode => _isDarkMode;

  set isDarkMode(bool value) {
    _isDarkMode = value;

In this class, we have a private `_isDarkMode` variable that represents the current state of the dark mode. We provide a getter `isDarkMode` to access this value and a setter `isDarkMode` to update it. When the value changes, we call `notifyListeners()` to notify any listeners (such as UI elements) that depend on this state.

Back in the `MyApp` widget, we wrap it with a `MultiProvider` widget to provide the `DarkModeProvider` to its descendants:

class MyApp extends StatefulWidget {
  State<MyApp> createState() => _MyAppState();

class _MyAppState extends State<MyApp> {
  void initState() {

  // This widget is the root of your application.
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
          create: (_) => DarkModeProvider(),
      child: Builder(
        builder: (context) {
          // Initialize the variables after the MaterialApp widget
          currentContext = context;
          textTheme.apply(displayColor: ThemeConfig.getTextColor(context));

          return Consumer<DarkModeProvider>(builder: (context, appMode, _) {
            return MaterialApp(
              debugShowCheckedModeBanner: false,
              title: 'API Call',
              theme: appMode.isDarkMode
                  ? ThemeConfig.darkTheme
                  : ThemeConfig.lightTheme,
              home: darkModePage(),
              routes: const {
                // Navigating the page using Named Routes
                //ProfileDetailedScreen.routeName: (ctx) => ProfileDetailedScreen(),

Inside the `darkModePage` widget, which is a stateless widget, we build the UI for the dark mode page:

class darkModePage extends StatelessWidget {
  const darkModePage({Key? key});

  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Dark mode - Provider'),
      body: Padding(
        padding: const EdgeInsets.all(8.0),
        child: Center(
          child: Column(
            children: [
                    ? "Enable light mode"
                    : "Enable dark mode",
                onChanged: (value) {
        <DarkModeProvider>().isDarkMode = value;

In this widget, we use the `` method to listen to changes in the `DarkModeProvider` and update the UI accordingly. The `Switch` widget allows the user to toggle between light and dark modes by modifying the `isDarkMode` property of the `DarkModeProvider`.

Finally, let's take a look at the `ThemeConfig` class. This class contains the configurations for our light and dark themes. In the `theme_config.dart` file, define the `ThemeConfig` class as follows:

class ThemeConfig {
  static ThemeData lightTheme = ThemeData.light().copyWith(
    appBarTheme: const AppBarTheme(
      titleTextStyle: TextStyle(color:,
    cardColor: Colors.white,
    // Add more theme configurations for other elements

  static ThemeData darkTheme = ThemeData.dark().copyWith(
    appBarTheme: const AppBarTheme(
      backgroundColor: Colors.orangeAccent,
      titleTextStyle: TextStyle(color: Colors.white),
    cardColor: Colors.grey[900],
    // Add more theme configurations for other elements

  static Color getTextColor(BuildContext context) {
    return Theme.of(context).brightness == Brightness.dark
        ? Colors.white

In this class, we define `lightTheme` and `darkTheme` properties, which are instances of `ThemeData` that customize various aspects of the app's appearance. The `getTextColor` method returns the appropriate text color based on the current theme's brightness.


In this tutorial, we learned how to implement dark mode in a Flutter app using the Provider package for state management. We explored the folder structure of the project, explained the code in detail, and saw how to create a dark mode page with a toggle switch. Dark mode not only enhances the user experience but also adds a modern touch to your Flutter applications. Happy coding!


Popular posts from this blog

Error Handling in Flutter - Gradle issue

How to Make a Dynamic and Trending ListView with Flutter Widgets?

Understanding API integration with Getx State management