Niraj Prakash
Posted on July 30, 2023
Greetings, fellow developers! Step into the captivating realm of Flutter 3.10, where theming takes the center stage in creating immersive and user-friendly apps. In this blog, we'll embark on an exciting journey to implement dynamic theme customization. Embrace the powerful capabilities of ThemeExtension as we gain unparalleled control over colors.
Let's dive in and integrate dynamic theming with custom colors extension into Flutter App! Example Github Repo you can find here
Default Theme
By default, Flutter provides a light and dark theme. These themes come with predefined color schemes for elements like the app bar, background, text, buttons, and more. Before we explore dynamic theming, let's take a brief look at the default color palette for both light and dark modes.
Step 1: Create theme for the app.
// Theme
class AppTheme {
static ThemeData themeData(
{Color seedColor = Colors.green, bool isDark = false}) {
ColorScheme colorScheme = isDark
? ColorScheme.fromSeed(seedColor: seedColor).copyWith(brightness: Brightness.dark)
: ColorScheme.fromSeed(seedColor: seedColor).copyWith( brightness: Brightness.light);
return ThemeData(colorScheme: colorScheme);
}
}
Step 2: In your main.dart file, use your light and dark themes.
// ..
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: "'Flutter Theme Example',"
theme: AppTheme.themeData(isDark: false),
darkTheme: AppTheme.themeData(isDark: true),
home: MyHomePage(),
);
}
}
Making it Dynamic
In this section, we'll explore how to dynamically change the theme color in your Flutter app. With this users can personalize their app's appearance. To achieve this, we'll leverage Flutter's ChangeNotifier to update the theme throughout the app.
Step 1. We'll begin by creating a AppThemeProvider class that extends the ChangeNotifier class which notify listeners whenever the theme changes.
class AppThemeProvider extends ChangeNotifier {
late Color _seedColor;
Color get seedColor => _seedColor;
set seedColor(value) {
_seedColor = value;
notifyListeners();
}
AppThemeProvider({required Color defaultColor}) {
_seedColor = Color.lerp(defaultColor, Colors.black, 0.1) ?? defaultColor;
}
void updateTheme(Color currentColor) {
seedColor = currentColor;
}
}
Step 2. Now change you main.dart file add ChangeNotifierProvider.
void main() {
runApp(
ChangeNotifierProvider<AppThemeProvider>(
create: (context) => AppThemeProvider(),
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
final appThemeProvider = Provider.of<AppThemeProvider>(context);
return MaterialApp(
title: 'Flutter Theme Example',
theme: AppTheme.themeData(seedColor: appThemeProvider.seedColor),
darkTheme: AppTheme.themeData(seedColor: appThemeProvider.seedColor, isDark: true),
home: MyHomePage(),
);
}
}
Step 3: In your any page.dart file, use the Consumer widget to update the theme.
class MyHomePage extends StatelessWidget {
// ..
// call this for color change
void _toggleTheme(BuildContext context, Color seedColor) {
context.read<AppThemeProvider>().updateTheme(currentColor);
}
}
For picking the color you can use flutter_colorpicker.
Custom Colors using ThemeExtension
Suppose you want more control over your app's color palette. For example different grey colors for text and background.
Flutter provide ThemeExtensions, a powerful way to add custom colors to your app's theme. With ThemeExtensions, you can define colors like "surface," "onSurface," "primaryHigh," and many more. These custom colors give you granular control over the appearance of various UI elements, resulting in a truly personalized and cohesive app design. Let's do this:
Step 1: Create a new Dart file called app_theme_colors.dart.
@immutable
class AppThemeColors extends ThemeExtension<AppThemeColors> {
final Color onSurfaceHigh;
const AppThemeColors({
required this.onSurfaceHigh,
});
@override
ThemeExtension<AppThemeColors> copyWith({
Color? onSurfaceHigh,
}) {
return AppThemeColors(
onSurfaceHigh: onSurfaceHigh ?? this.onSurfaceHigh,
);
}
@override
ThemeExtension<AppThemeColors> lerp(
ThemeExtension<AppThemeColors>? other, double t) {
if (other is! AppThemeColors) {
return this;
}
return AppThemeColors(
onSurfaceHigh: Color.lerp(onSurfaceHigh, other.onSurfaceHigh, t) ??
other.onSurfaceHigh,
// isDark: true,
);
}
@override
String toString() {
return 'AppThemeColors(seed: $onSurfaceHigh)';
}
factory AppThemeColors.seedColor({
Color seedColor = Colors.red,
bool isDark = false,
}) {
print("isDark: $isDark $seedColor");
var surfaceBasedColor = isDark
? Color.alphaBlend(Colors.white.withOpacity(0.1), Colors.black)
: Colors.white;
var onSurfaceBasedColor = isDark ? Colors.white : Colors.black;
var onSurfaceHigh = Color.alphaBlend(
onSurfaceBasedColor.withOpacity(0.7), surfaceBasedColor);
return AppThemeColors(onSurfaceHigh: onSurfaceHigh);
}
}
Step 2: Update your main.dart to use the theme extension.
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
final appThemeProvider = Provider.of<AppThemeProvider>(context);
return MaterialApp(
title: 'Flutter Theme Example',
theme: AppTheme.themeData(seedColor: appThemeProvider.seedColor).copyWith(
extensions: <ThemeExtension<dynamic>>[
AppThemeColors.create(
isDark: false)
]),
darkTheme: AppTheme.themeData(seedColor: appThemeProvider.seedColor, isDark: true).copyWith(
extensions: <ThemeExtension<dynamic>>[
AppThemeColors.create(
isDark: true)
]),
home: MyHomePage(),
);
}
}
Step 3: Update your Page to use the theme extension
class MyPage extends StatelessWidget {
late AppThemeColors themeColors;
@override
Widget build(BuildContext context) {
themeColors = Theme.of(context).extension<AppThemeColors>() ??
AppThemeColors.create(); // if null
// ..
return Scaffold(
body: Container(
child: Text(
"By continuing, you agree to our",
style: TextStyle(color: themeColors.onSurfaceHigh),
),
),
);
}
}
By setting up the app_theme_colors.dart, you can now easily customize the light and dark themes in one place, making your code cleaner and more maintainable.
Conclusion:
Congratulations! You've done the setup of dynamic theme and ThemeExtensions.
Don't forget to try out our example app
Unleash the full potential of Flutter themes and ThemeExtensions in your future projects.
We have implemented this in one of our app Hundi: Record Book.
Posted on July 30, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.