App Theming in Flutter – Dark Mode/ Light Mode

By | December 31, 2019

In this article, we will see how to theme our apps in Flutter.
As you know Google and Apple has released Dark Mode and Light Modes in their latest OS releases in Android 10 and iOS 13. So we have to make sure that our apps works perfectly when the user switch modes in their devices.

Watch Video Tutorial


We have a simple screen here with just a ListView. So we will be trying to theme this screen based on the Device theme.

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'AppNotifier.dart';

class ThemeDemo extends StatefulWidget {
  @override
  ThemeDemoState createState() => ThemeDemoState();
}

class ThemeDemoState extends State<ThemeDemo> {
  //

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        elevation: 0,
        title: Text('Flutter Themes'),
        leading: Icon(Icons.menu),
      ),
      body: Container(
        child: ListView.builder(
          itemCount: 10,
          itemBuilder: (context, pos) {
            return Card(
              elevation: 0,
              child: ListTile(
                title: Text(
                  "Title $pos",
                ),
                subtitle: Text(
                  "Subtitle $pos",
                ),
                leading: Icon(
                  Icons.alarm,
                ),
                trailing: Icon(
                  Icons.chevron_right,
                ),
              ),
            );
          },
        ),
      ),
    );
  }
}

Now we will add this screen in the main.dart as the ‘home’.


void main() {
  runApp(
    HomeApp(),
  );
}

class HomeApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Tutorials',
      debugShowCheckedModeBanner: false,
      home: ThemeDemo(),
    );
  }
}

If you run the application now, you will see the default theme, which is the Light Theme.
Now let’s add a theme to our parent ‘MaterialApp’ widget.

MaterialApp(
      title: 'Flutter Tutorials',
      debugShowCheckedModeBanner: false,
      theme: ThemeData.light(),
      home: ThemeDemo(),
);

Even if you specify the ThemeData.light() theme, the UI will not change because the default theme applied is the light theme.

Now let’s apply the Dark Theme.

MaterialApp(
      title: 'Flutter Tutorials',
      debugShowCheckedModeBanner: false,
      theme: ThemeData.light(),
      darkTheme: ThemeData.dark(),
      home: ThemeDemo(),
);

Here we have applied the Dark Theme, so if we switch the Device theme from light to Dark in the Settings, our UI will update. Be default, all whites will turn black and all blacks will turn whites, this is provided by the Flutter framework.


Customize Themes

So now we know that the ‘theme’ property is accepting the ThemeData Object. So lets go ahead a create a custom ThemeData.

For that, I am creating a new file named “AppTheme.dart” and create two themes for Light and Dark Themes.

import 'package:flutter/material.dart';

class AppTheme {
  //
  AppTheme._();

  static final ThemeData lightTheme = ThemeData(
    scaffoldBackgroundColor: Colors.teal,
    appBarTheme: AppBarTheme(
      color: Colors.teal,
      iconTheme: IconThemeData(
        color: Colors.white,
      ),
    ),
    colorScheme: ColorScheme.light(
      primary: Colors.white,
      onPrimary: Colors.white,
      primaryVariant: Colors.white38,
      secondary: Colors.red,
    ),
    cardTheme: CardTheme(
      color: Colors.teal,
    ),
    iconTheme: IconThemeData(
      color: Colors.white54,
    ),
    textTheme: TextTheme(
      title: TextStyle(
        color: Colors.white,
        fontSize: 20.0,
      ),
      subtitle: TextStyle(
        color: Colors.white70,
        fontSize: 18.0,
      ),
    ),
  );

  static final ThemeData darkTheme = ThemeData(
    scaffoldBackgroundColor: Colors.black,
    appBarTheme: AppBarTheme(
      color: Colors.black,
      iconTheme: IconThemeData(
        color: Colors.white,
      ),
    ),
    colorScheme: ColorScheme.light(
      primary: Colors.black,
      onPrimary: Colors.black,
      primaryVariant: Colors.black,
      secondary: Colors.red,
    ),
    cardTheme: CardTheme(
      color: Colors.black,
    ),
    iconTheme: IconThemeData(
      color: Colors.white54,
    ),
    textTheme: TextTheme(
      title: TextStyle(
        color: Colors.white,
        fontSize: 20.0,
      ),
      subtitle: TextStyle(
        color: Colors.white70,
        fontSize: 18.0,
      ),
    ),
  );
}

In the above code, as you can see we have two variables for Light and Dark Themes where we are customizing some of its properties such as ‘scaffoldBackgroundColor’, ‘appBarTheme’ etc.

scaffoldBackgroundColor will change the background color of the screen, appBarTheme will change the color of you AppBar and similarly for other styles.


Manually Switching Themes

Here the idea is to broadcast the theme change when the user changes. Now we will need a package which can do that.

So open your pubspec.yaml file and add the ‘provider’ package.

Run the flutter packages get in the terminal inside your project and install the package.

Now we have to write a class and a method to broadcast the Theme changes. So lets create a new class named “AppStateNotifier’

import 'package:flutter/material.dart';

class AppStateNotifier extends ChangeNotifier {
  //
  bool isDarkMode = false;

  void updateTheme(bool isDarkMode) {
    this.isDarkMode = isDarkMode;
    notifyListeners();
  }
}

Now we will add a Switch Widget in the UI to switch between the Themes.

Switch(
    value: Provider.of<AppStateNotifier>(context).isDarkMode,
    onChanged: (boolVal) {
        Provider.of<AppStateNotifier>(context).updateTheme(boolVal);
    },
)

Now we need to add a Listener to listen to the changes and update the theme. We have to go to our Top most widget and update the code like this.

void main() {
  runApp(
    ChangeNotifierProvider<AppStateNotifier>(
      builder: (context) => AppStateNotifier(),
      child: HomeApp(),
    ),
  );
}

class HomeApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Consumer<AppStateNotifier>(
      builder: (context, appState, child) {
        return MaterialApp(
          title: 'Flutter Tutorials',
          debugShowCheckedModeBanner: false,
          theme: AppTheme.lightTheme, // ThemeData(primarySwatch: Colors.blue),
          darkTheme:
              AppTheme.darkTheme, // ThemeData(primarySwatch: Colors.blue),
          home: ThemeDemo(),
          themeMode: appState.isDarkMode ? ThemeMode.dark : ThemeMode.light,
        );
      },
    );
  }
}

Here the HomeApp is surrounded by ChangeNotifierProvider Widget which receives the changes and sends it to the ‘Consumer’ widget to Consume it and use it to make the changes. In the above code you can see the ‘themeMode’ property which is updated based on the appState variable. When the Switch widget turns on, it will send the boolean value true to the Listeners and finally to the Consumer Widget.

themeMode: appState.isDarkMode ? ThemeMode.dark : ThemeMode.light,

Update the Text Themes

Now we know the theme which is set in the application with the help of Theme.of(context) inherited widget.

So to update the text themes or any other theme which is declared in our Custom Themes, you can get access to it with the help of Theme.of(context).

For example. the text widgets can be applied styles like this.

Text(
    "Title $pos",
    style: Theme.of(context).textTheme.title,
),

and Icons can be set like this.

Icon(
    Icons.alarm,
    color: Theme.of(context).iconTheme.color,
),

Complete Source Code

The complete source code is available in the below location

https://bitbucket.org/vipinvijayan1987/tutorialprojects/src/master/FlutterTutorialProjects/flutter_demos/lib/widgets/ThemeDemo/

Make sure to watch the Youtube Tutorial to see everything in action and a Better Understanding.

Please leave your valuable comments in the comment section below.

One thought on “App Theming in Flutter – Dark Mode/ Light Mode

  1. Archie

    Thanks for the article but how can it be implemented when there is a bottomnavigationbar. i. e. The main.dart goes to a page with the bottomnavigationbar

    Reply

Leave a Reply to Archie Cancel reply

Your email address will not be published. Required fields are marked *