A theme is a generic styling element that represents the overall style, look, and feel of your application. When you wish to modify your Flutter app theme, say for eg. from Flutter Dark mode to Flutter Light mode or vice-versa, it is known as Flutter theming.
There are many articles available on Flutter theming, showing how to implement Flutter theme Dark Light in projects using the default App Theme and their default Colors attribute. But in the live project, we are required to add our custom Color for our app branding.
In this blog, we will be sharing how to create a flutter custom theme step by step
To successfully implement Flutter theming, you will have to take care of the following prerequisites:
Once you are ready to go ahead, execute the following steps consecutively for enabling custom Flutter theming.
Want to Implement a custom option to change the theme dynamically in your app?
Hire Flutter Developers from Bacancy to add or customize theme Color for your app branding
For creating a theme for light and dark mode, we use ThemeData class and customize colors and other properties based on needs. We have created one method for getting ThemeDate based on selected light/dark themes.
We give different values for scaffoldBackgroundColor, bodyColor, thumbColor, listTileTheme, and appBarTheme based on the selected theme.
ThemeData getAppTheme(BuildContext context, bool isDarkTheme) { return ThemeData( scaffoldBackgroundColor: isDarkTheme ? Colors.black : Colors.white, textTheme: Theme.of(context) .textTheme .copyWith( titleSmall: Theme.of(context).textTheme.titleSmall?.copyWith(fontSize: 11), ) .apply( bodyColor: isDarkTheme ? Colors.white : Colors.black, displayColor: Colors.grey, ), switchTheme: SwitchThemeData( thumbColor: MaterialStateProperty.all( isDarkTheme ? Colors.orange : Colors.purple), ), listTileTheme: ListTileThemeData( iconColor: isDarkTheme ? Colors.orange : Colors.purple), appBarTheme: AppBarTheme( backgroundColor: isDarkTheme ? Colors.black : Colors.white, iconTheme: IconThemeData(color: isDarkTheme ? Colors.white : Colors.black54)), ); }
We will use river-pod to manage the app theme state. We only want to store bool value to manage light or dark themes so we will use StateProvider.
We use river-pod in the project so we have to wrap MyApp with ProviderScope to access all providers though-out app. MyApp extends ConsumerWidget so we can get the WidgetRef object in the build method and access any river-pod using the ref variable. getAppTheme(context, ref.watch(appThemeProvider)) method listens to any change in the app theme and updates the app accordingly.
class MyApp extends ConsumerWidget { const MyApp({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { return MaterialApp( title: 'Flutter Light/Dark Theme', debugShowCheckedModeBanner: false, theme: getAppTheme(context, ref.watch(appThemeProvider)), home: const MyHomePage(), ); } }
ref.read(appThemeProvider.notifier).state = value. We are updating the theme state in appThemeProvider when the switch state is changed from light/dark mood.
Switch( activeColor: Colors.orange, onChanged: (value) { ref.read(appThemeProvider.notifier).state = value; }, value: isDarkMode )
It works fine as far as it will show the same color in all icons and texts. If we want to use different colors on icons, we have to create an extension for the Theme. Create a class and extend with ThemeExtension and add necessary fields that you want to customize.
class AppColors extends ThemeExtension { final Color? color1; final Color? color2; final Color? color3; const AppColors({ required this.color1, required this.color2, required this.color3, }); @override AppColors copyWith({ Color? color1, Color? color2, Color? color3, }) { return AppColors( color1: color1 ?? this.color1, color2: color2 ?? this.color2, color3: color3 ?? this.color3, ); } @override AppColors lerp(ThemeExtension ? other, double t) { if (other is! AppColors) { return this; } return AppColors( color1: Color.lerp(color1, other.color1, t), color2: Color.lerp(color2, other.color2, t), color3: Color.lerp(color3, other.color3, t), ); } }
Now add this extension attribute in ThemeData in our create method getAppTheme and define the theme according to colors.
extensions: >[ AppColors( color1: isDarkTheme ? Colors.blue : Colors.blueGrey, color2: isDarkTheme ? Colors.pink : Colors.pinkAccent, color3: isDarkTheme ? Colors.yellow : Colors.limeAccent, ),
Create another extension function which we use to access custom color easily.
We can access these colors in the widget simply using colors(context).color1. If we do not specify the Icon color, it will fetch the color from listTileTheme.
ListTile( leading: Icon(Icons.chat_outlined, color: colors(context).color3), title: Text( "Help Center", style: Theme.of(context).textTheme.titleSmall), ), ListTile( leading: const Icon(Icons.notifications), title: Text("Notification", style: Theme.of(context).textTheme.titleSmall), ),
Here is the code for theme class:
import 'package:flutter/material.dart'; AppColors colors(context) => Theme.of(context).extension ()!; ThemeData getAppTheme(BuildContext context, bool isDarkTheme) { return ThemeData( extensions: >[ AppColors( color1: isDarkTheme ? Colors.blue : Colors.green, color2: isDarkTheme ? Colors.pink : Colors.blue, color3: isDarkTheme ? Colors.yellow : Colors.red, ), ], scaffoldBackgroundColor: isDarkTheme ? Colors.black : Colors.white, textTheme: Theme.of(context) .textTheme .copyWith( titleSmall: Theme.of(context).textTheme.titleSmall?.copyWith(fontSize: 12), ) .apply( bodyColor: isDarkTheme ? Colors.white : Colors.black, displayColor: Colors.grey, ), switchTheme: SwitchThemeData( thumbColor: MaterialStateProperty.all( isDarkTheme ? Colors.orange : Colors.purple), ), listTileTheme: ListTileThemeData( iconColor: isDarkTheme ? Colors.orange : Colors.purple), appBarTheme: AppBarTheme( backgroundColor: isDarkTheme ? Colors.black : Colors.white, iconTheme: IconThemeData(color: isDarkTheme ? Colors.white : Colors.black54)), ); } @immutable class AppColors extends ThemeExtension { final Color? color1; final Color? color2; final Color? color3; const AppColors({ required this.color1, required this.color2, required this.color3, }); @override AppColors copyWith({ Color? color1, Color? color2, Color? color3, }) { return AppColors( color1: color1 ?? this.color1, color2: color2 ?? this.color2, color3: color3 ?? this.color3, ); } @override AppColors lerp(ThemeExtension ? other, double t) { if (other is! AppColors) { return this; } return AppColors( color1: Color.lerp(color1, other.color1, t), color2: Color.lerp(color2, other.color2, t), color3: Color.lerp(color3, other.color3, t), ); } }
Here is code of our main screen:
import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:light_dark_mode/provider%20/app_theme_provider.dart'; import 'package:light_dark_mode/utils/app_theme.dart'; void main() { runApp(const ProviderScope(child: MyApp())); } class MyApp extends ConsumerWidget { const MyApp({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { return MaterialApp( title: 'Flutter Light/Dark Theme', debugShowCheckedModeBanner: false, theme: getAppTheme(context, ref.watch(appThemeProvider)), home: const MyHomePage(), ); } } class MyHomePage extends ConsumerWidget { const MyHomePage({Key? key}) : super(key: key); @override Widget build(BuildContext context, WidgetRef ref) { var isDarkMode = ref.watch(appThemeProvider); return Scaffold( appBar: AppBar( elevation: 0, leading: const Icon(Icons.arrow_back_ios_sharp), actions: const [ Padding( padding: EdgeInsets.symmetric(horizontal: 15.0), child: Icon(Icons.add_circle_outline), ) ], ), body: Padding( padding: const EdgeInsets.symmetric(horizontal: 8.0), child: ListView( children: [ CircleAvatar( radius: 60, backgroundColor: Colors.grey, child: Padding( padding: const EdgeInsets.all(1), // Border radius child: ClipRRect( borderRadius: BorderRadius.circular(60), child: Image.asset( "assets/ic_profile.jpeg", fit: BoxFit.fill, width: 120, height: 120, )), ), ), Container( margin: const EdgeInsets.only(top: 10, bottom: 60), alignment: Alignment.center, child: Text( "Testing User", style: Theme.of(context).textTheme.titleLarge, ), ), ListTile( leading: Icon(isDarkMode ? Icons.brightness_3 : Icons.sunny), title: Text( isDarkMode ? "Dark mode" : "Light mode", style: Theme.of(context).textTheme.titleSmall, ), trailing: Consumer(builder: (context, ref, child) { return Transform.scale( scale: 0.7, child: Switch( activeColor: Colors.orange, onChanged: (value) { ref.read(appThemeProvider.notifier).state = value; }, value: isDarkMode, ), ); }), ), ListTile( leading: Icon(Icons.grid_on_sharp, color: colors(context).color1,), title: Text( "Story", style: Theme.of(context).textTheme.titleSmall, ), ), ListTile( leading: Icon(Icons.settings, color: colors(context).color2), title: Text("Settings and Privacy", style: Theme.of(context).textTheme.titleSmall), ), ListTile( leading: Icon(Icons.chat_outlined, color: colors(context).color3), title: Text( "Help Center", style: Theme.of(context).textTheme.titleSmall, ), ), ListTile( leading: const Icon(Icons.notifications), title: Text( "Notification", style: Theme.of(context).textTheme.titleSmall, ), ), ], ), ), ); } }
You can find full code here: GitHub Repository.
I hope this blog will help to customize your Flutter app theme. In this blog we customized Colors in ThemeExtension; however, we can use any field to customize the value. Flutter theming is just one aspect, there are several other solutions that Flutter app development company like Bacancy can provide you. Contact us Now.
Navigating client's requirement with precision is what our developers' focuses on. Besides, we develop to innovate and deliver the best solutions to our clients.
get in touchYour Success Is Guaranteed !
We accelerate the release of digital product and guaranteed their success
We Use Slack, Jira & GitHub for Accurate Deployment and Effective Communication.