Using ThemeData to create a Light and Dark Theme in Flutter
Yago Souza Oliveira
Posted on November 21, 2024
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(),
);
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,
);
}
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,
),
)
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,
),
);
}
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,
),
);
}
Foto de David Clode na Unsplash
Posted on November 21, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.