Pull to refresh

Dark Mode in Flutter Apps

With the introduction of Android 10, the Dark mode option in Android phones is becoming more popular. The latest Android versions running on mobile devices are readily supporting the dark mode feature. Dark mode themes not only provide a distinct visual impact but also aids human vision and device energy. It is proven that Dark themes on mobile UI reduce eye strain on low-lighting conditions. These types of themes also offer brightness scope reduction which conserves the battery of the device.

This proves that dark mode in mobile applications is not just for show but also useful for the visual health aspect of the user.

In this post, we are going to learn how to implement dark mode in an Android app built by using Flutter/dart programming.

Create a new Flutter project

First, we need to create a new Flutter project. For that, make sure that the Flutter SDK and other flutter app development-related requirements are properly installed. If everything is properly set up, then in order to create a project, we can simply run the following command in the desired local directory:

flutter create flutterDarkMode 

After the project has been set up, we can navigate inside the project directory and execute the following command in the terminal to run the project in either an available emulator or an actual device:

flutter run

After successfully build, we will get the following result in the emulator screen:

Scaffolding the Home Page

Now, we are going to scaffold the main screen template in order to capture the influence of dark mode on different UI widgets of Flutter.

For that, we need to remove the default MyApp stateless widget as well as the HomePageState stateful widget from the main.dart file.

Instead, we are going to add a new App class extending to the Stateful widget which is to be called in the main() function inside the runApp method. The basic scaffolding is provided in the code snippet below:


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

class App extends StatefulWidget {
  @override
  _AppState createState() => _AppState();
}

class _AppState extends State<App> {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        appBar: AppBar(
          title : Text("Flutter Dark Mode"),
        ),
        body: Container(),
      ),
    );
  }
}

Here, we have returned the MaterialApp widget as the primary parent widget from the AppState class which contains the Scaffold widget as a child. In the Scaffold widget, we have added a basic AppBar and Container widget as a body.

Now once we re-run the app, we will get the following result on the emulator screen:

Now, we are going to work on creating the Scaffold body. Here, we are going to add some Text widgets, a button, and an input field so that, we can see what becomes of them when we switch the app themes. The overall coding implementation is provided in the code snippet below:

body: Container(
  padding: EdgeInsets.all(10),
  child: SingleChildScrollView(
    child: Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Text("Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?"),
        SizedBox(height: 10,),
        Text("Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?"),
        SizedBox(height : 20),
        TextField(
          controller: controller,
          decoration: InputDecoration(
            hintText: "Demo Text",
          ),
        ),
        SizedBox(height : 20),
        RaisedButton(
          onPressed: () {  },
          child: Text("I Understand and Accept", style: TextStyle(fontSize: 16, wordSpacing: 2),),
        ), 
        SizedBox(height : 50)
      ],
    ),
  ),
),
),

Here, we have some Text widgets, a RaisedButton, and a TextField. Moreover, we have used the Column widget to structure other widgets vertically on the screen.

Hence, the result is shown in the emulator screenshot below:

Adding Theme changer Switch to App Bar

Now, we are going to add a Switch widget through which we will control the theme changes in the app. But first, we need to define a Boolean state variable that controls the value of the switch whether the switch is ON or OFF. Here, we are going to define it as darkModeActive which is initialized to false as shown in the code snippet below:

bool darkModeActive = false;

Next, we are going to add the Switch widget at the right end of the App bar using the actions property provided by the AppBar widget:

Scaffold(
        appBar: AppBar(
          title : Text("Flutter Dark Mode"),
          actions: [
            Switch(
              value: darkModeActive, 
              onChanged: (toggle){
              setState(() {
                darkModeActive= toggle;
              });
            }),
          ],
        ),
        body: Container(

Here, flicking the switch On/Off will trigger the onChanged event which will assign a toggle value to our darkModeActive state. The setState() method is used to re-render the whole widget tree so that the changes to the app display reflect on the screen. The value property in the Switch widget reflects the value of the darkModeActive state.

Thus, the idea is to change the theme to dark when darkModeActive state is true and to light mode when darkModeActive is false.

Initializing Theme Properties

This step is the major step in which we are going to define our theme properties using the Themedata class object.

The ThemeData class enables us to define the configuration of the overall visual Theme for a MaterialApp or a widget subtree within the app. It lets us define the color properties as well as widget themes for other widgets as well. Once assigned to the theme property of the MaterialApp widget, the properties in the ThemeData class will reflect on the overall app. The light and dark mode theme configuration properties that we are going to use on this app is provided in the code snippet below:

ThemeData darkThemeProperties = ThemeData(
  accentColor: Colors.red,
  brightness: Brightness.dark,
  primaryColor: Colors.black,
  textTheme: TextTheme(bodyText1: TextStyle(color: Colors.white)),
  buttonTheme: ButtonThemeData(
    buttonColor: Colors.black,
  )
);

ThemeData lightThemeProperties = ThemeData(
  accentColor: Colors.pink,
  brightness: Brightness.light,
  primaryColor: Colors.blue,
  textTheme: TextTheme(bodyText1: TextStyle(color: Colors.black)),
);

Here, we define the accentColor, brightness, and primaryColor properties for both themes. For the dark theme, we define a custom theme for buttons as well using ButtonThemeData class on buttonTheme property. Moreover, we apply custom text themes for both theme modes as well using TextTheme class.

Most Importantly, the major theme property that provides the dark and light theme appearance is the brightness property.

Applying the Theme properties to the App

Finally, the last thing left is to add these theme properties to our MaterialApp. The MaterialApp widget provides a theme property. This theme property allows us to assign the custom theme properties that reflect on the entire app widget tree. Here, we are going the assign the theme properties based on darkModeActive conditional status as shown in the code snippet below:

Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      **theme: !darkModeActive ? lightThemeProperties : darkThemeProperties,**
      home: Scaffold(
        appBar: AppBar(

If the darkModeActive status is false, then we display the light theme else the dark mode.

Now if we re-run the app, we can use the switch to change the theme as demonstrated in the demo below:

As we can notice, the theme for text, button, and the Text field also changes along with the entire appearance of the app.

Finally, we have successfully implemented the dark mode in the Flutter environment without using any third-party plugins.

Conclusion

The main goal of this article post was to demonstrated how to implement a dark mode theme to the Flutter application. Apart from adding a dark theme, we can also add our own customized theme based on the need of the app. We can customize the theme using ThemeData class which makes things much easier and efficient.

Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.