DEV Community

Cover image for Using ThemeData to create a Light and Dark Theme in Flutter
Yago Souza Oliveira
Yago Souza Oliveira

Posted on

Using ThemeData to create a Light and Dark Theme in Flutter

Dark and Light themes are fundamental concepts in interface design and user experience. While the 'Light Theme' offers a bright and illuminated aesthetic, the 'Dark Theme' provides a softer, low-luminance atmosphere.

The choice between these two themes plays a important role in the usability of applications and websites, impacting not only aesthetics but also visual comfort and efficiency, adapting to user preferences.

In Flutter, ThemeData is the main tool for managing colors in your application, allowing complete interface customization and the implementation of themes such as light and dark mode. We have most of ways to apply that concept in Flutter Apps, this one is a simplify and is especially useful in small apps.

When started a new App Flutter, we have a MaterialApp to start:

  return MaterialApp(
      title: 'My App Name',
      themeMode: ThemeMode.system,
      darkTheme: MyThemeDataDark.themeData,
      theme: MyThemeDataLight.themeData,
      home: const FooBarScreen(),
    );
Enter fullscreen mode Exit fullscreen mode

If you see, we have two themes: the theme, where we input a ThemeData with Light, and the darkTheme.

ThemeData is the best way to share colors and fontstyles. You can see in official article with Flutter here

In this example, we put this in Light Theme:

abstract class MyAppThemeLight {
  static ThemeData themeData = ThemeData(
    primaryColor: const Color(0xFFF2F2F2),
    scaffoldBackgroundColor: const Color(0xFFF2F2F2),
    appBarTheme: const AppBarTheme(
      backgroundColor: Color(0xFFF2F2F2),
      elevation: 0.0,
    ),
    switchTheme: SwitchThemeData(
      trackColor: MaterialStateProperty.all(
        const Color(0xFFB6B6D4),
      ),
    ),
    elevatedButtonTheme: ElevatedButtonThemeData(
      style: ElevatedButton.styleFrom(
        backgroundColor: const Color(0xFF000027),
      ),
    ),
    textButtonTheme: TextButtonThemeData(
      style: TextButton.styleFrom(
        foregroundColor: const Color(0xFF000027),
      ),
    ),
    colorScheme: const ColorScheme(
      brightness: Brightness.light,
      primary: Colors.white,
      onPrimary: Color(0xFF000027),
      secondary: Color(0xFF000027),
      onSecondary: Color(0xFFF5F5F8),
      error: Color(0xFF721C24),
      onError: Color(0xFFF5C6CB),
      background: Color(0xFFF5F5F8),
      onBackground: Color(0xFFF5F5F8),
      surface: Colors.white,
      onSurface: Colors.black,
    ),
    textTheme: GoogleFonts.montserratTextTheme(
      const TextTheme(
        titleMedium: TextStyle(
          color: Colors.white,
        ),
      ),
    ),
    fontFamily: GoogleFonts.montserrat().fontFamily,
    useMaterial3: true,
  );
}
Enter fullscreen mode Exit fullscreen mode

Please note that we have some specific themes for widgets, like ElevatedButton and Switch. It is important to have these themes minimally defined as it reduces the redundancy of colors and attributes.

The ColorScheme is your best friend. The Primary Color and Secondary of ThemeData is good, but not cover all situations. The ColorScheme get others options and is easly to access this in componentes:

SizedBox(
   height: 25.0,
   width: 25.0,
   child: CircularProgressIndicator(
            color: Theme.of(context).colorScheme.primary,
  ),
)
Enter fullscreen mode Exit fullscreen mode

When you build the two themes, choice the colors with de Dark theme and input this in MaterialApp.

Put themeMode: ThemeMode.system we look for the Operational System and apply the theme selected by the user in there. If the user set you SO in Light Mode, Flutter get this information and apply the theme data.

And that's it! We have a Dark and Light mode in our app. But if we need to switch in the app the theme, not depending for the selection in OS? Well, in this case, we need a more steps.

First, we need to save the choice of the user and see if has selected every time the app is initializated. We can use the package Shared Preferences to do that. In the main method, before to called a Material app we do:

void main() async {
  ThemeMode themeMode = ThemeMode.system;
  final Future<SharedPreferences> prefsInstance =
      SharedPreferences.getInstance();
  final SharedPreferences prefs = await prefsInstance;
  final bool? isDarkTheme = prefs.getBool('is_dark_theme');
  if (isDarkTheme != null) {
    themeMode = isDarkTheme ? ThemeMode.dark : ThemeMode.light;
  }
  runApp(
    MyApp(
      themeMode: themeMode,
    ),
  );
}
Enter fullscreen mode Exit fullscreen mode

In this example, we initialize the ThemeMode in the main, preventing us from passing null values and using this in the MaterialApp below.

void main() async {
  ThemeMode themeMode = ThemeMode.system;
  final Future<SharedPreferences> prefsInstance =
      SharedPreferences.getInstance();
  final SharedPreferences prefs = await prefsInstance;
  final bool? isDarkTheme = prefs.getBool('is_dark_theme');
  if (isDarkTheme != null) {
    themeMode = isDarkTheme ? ThemeMode.dark : ThemeMode.light;
  }
  runApp(
    MyApp(
      themeMode: themeMode,
    ),
  );
}
Enter fullscreen mode Exit fullscreen mode

Foto de David Clode na Unsplash

Top comments (0)