Contents
1. Introduction
FlexColorScheme
is a very powerful theme framework for Flutter, there is complete documentation and tutorials, and there are many existing themes that can be used, you also can design your theme online.
FlexColorScheme
is at its core an advanced ThemeData factory. It makes customization of ThemeData simpler. It does not expose everything you can do with ThemeData via its own APIs. Its focus is on things that are complex and tedious to do with ThemeData. You can further tune the produced ThemeData by using copyWith and ThemeData’s own APIs.
The Themes Playground is a visual web configuration tool for the FlexColorScheme
API. It allows you to play with different styles interactively, and copy the FlexColorScheme
API setup code for the theme you created. The Playground does not cover all APIs offered by FlexColorScheme
, but most of them, certainly the most commonly used ones.
2. Use in GetX
Because GetX
can easy to help with dynamic change and handle the theme, I will base it on my previous article “Create Flutter project with GetX Pattern” to show you how to use FlexColorScheme
in GetX
.
2.1 Get the themes from FlexColorScheme
Create the theme
folder under /lib/app/ui/
, go to FlexColorScheme
‘s playground to design or choose the existing theme, and click the Copy theme code
button, then you can easy to copy the theme codes.
the theme code should be like below:
// Theme config for FlexColorScheme version 7.3.x. Make sure you use
// same or higher package version, but still same major version. If you
// use a lower package version, some properties may not be supported.
// In that case remove them after copying this theme to your app.
theme: FlexThemeData.light(
scheme: FlexScheme.orangeM3,
surfaceMode: FlexSurfaceMode.levelSurfacesLowScaffold,
blendLevel: 7,
subThemesData: const FlexSubThemesData(
blendOnLevel: 10,
blendOnColors: false,
useTextTheme: true,
useM2StyleDividerInM3: true,
adaptiveRadius: FlexAdaptive.excludeWebAndroidFuchsia(),
alignedDropdown: true,
useInputDecoratorThemeInDialogs: true,
),
visualDensity: FlexColorScheme.comfortablePlatformDensity,
useMaterial3: true,
swapLegacyOnMaterial3: true,
// To use the Playground font, add GoogleFonts package and uncomment
// fontFamily: GoogleFonts.notoSans().fontFamily,
),
darkTheme: FlexThemeData.dark(
scheme: FlexScheme.orangeM3,
surfaceMode: FlexSurfaceMode.levelSurfacesLowScaffold,
blendLevel: 13,
subThemesData: const FlexSubThemesData(
blendOnLevel: 20,
useTextTheme: true,
useM2StyleDividerInM3: true,
adaptiveRadius: FlexAdaptive.excludeWebAndroidFuchsia(),
alignedDropdown: true,
useInputDecoratorThemeInDialogs: true,
),
visualDensity: FlexColorScheme.comfortablePlatformDensity,
useMaterial3: true,
swapLegacyOnMaterial3: true,
// To use the Playground font, add GoogleFonts package and uncomment
// fontFamily: GoogleFonts.notoSans().fontFamily,
),
// If you do not have a themeMode switch, uncomment this line
// to let the device system mode control the theme mode:
// themeMode: ThemeMode.system,
but we still need to change something, we need to create a class to handle each theme, for the above theme code, we can create the class ThemeBrown
below
create a file /lib/app/ui/theme/theme_brown.dart
and past the theme code:
import 'package:flex_color_scheme/flex_color_scheme.dart';
import 'package:flutter/material.dart';
class ThemeBrown {
static ThemeData lightTheme() => FlexThemeData.light(
scheme: FlexScheme.orangeM3,
surfaceMode: FlexSurfaceMode.levelSurfacesLowScaffold,
blendLevel: 7,
subThemesData: const FlexSubThemesData(
blendOnLevel: 10,
blendOnColors: false,
useTextTheme: true,
useM2StyleDividerInM3: true,
adaptiveRadius: FlexAdaptive.excludeWebAndroidFuchsia(),
alignedDropdown: true,
useInputDecoratorThemeInDialogs: true,
),
visualDensity: FlexColorScheme.comfortablePlatformDensity,
useMaterial3: true,
swapLegacyOnMaterial3: true,
// To use the Playground font, add GoogleFonts package and uncomment
// fontFamily: GoogleFonts.notoSans().fontFamily,
);
static ThemeData darkTheme() => FlexThemeData.dark(
scheme: FlexScheme.orangeM3,
surfaceMode: FlexSurfaceMode.levelSurfacesLowScaffold,
blendLevel: 13,
subThemesData: const FlexSubThemesData(
blendOnLevel: 20,
useTextTheme: true,
useM2StyleDividerInM3: true,
adaptiveRadius: FlexAdaptive.excludeWebAndroidFuchsia(),
alignedDropdown: true,
useInputDecoratorThemeInDialogs: true,
),
visualDensity: FlexColorScheme.comfortablePlatformDensity,
useMaterial3: true,
swapLegacyOnMaterial3: true,
// To use the Playground font, add GoogleFonts package and uncomment
// fontFamily: GoogleFonts.notoSans().fontFamily,
);
}
as you can see, we put the light
and dark
theme to lightTheme
and darkTheme
method in ThemeBrown
class, and then we can easy to use them later.
for example, I want to create 10 themes in my App, just repeat the above flow and create 10 theme classes and files
2.2 Create theme service
Because we need to handle multiple themes, we can create a service for handling the below theme logic:
- define the theme’s name
- get the theme by name
- get the current theme mode from storage
so the file should be /lib/app/data/service/theme_service.dart
:
import 'package:get_storage/get_storage.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../ui/theme/theme_brown.dart';
import '../../ui/theme/theme_deepBlue.dart';
import '../../ui/theme/theme_gold.dart';
import '../../ui/theme/theme_green.dart';
import '../../ui/theme/theme_hippie_blue.dart';
import '../../ui/theme/theme_indigo.dart';
import '../../ui/theme/theme_orang.dart';
import '../../ui/theme/theme_purple.dart';
import '../../ui/theme/theme_red.dart';
import '../../ui/theme/theme_sakura.dart';
class ThemeService {
//define each theme's name
static const String Red = 'Red';
static const String Indigo = 'Indigo';
static const String HippieBlue = 'HippieBlue';
static const String Green = 'Green';
static const String DeepBlue = 'DeepBlue';
static const String Sakura = 'Sakura';
static const String Purple = 'Purple';
static const String Brown = 'Brown';
static const String Gold = 'Gold';
static const String Orang = 'Orang';
//use GetStorage to handle the current theme mode
final _getStorage = GetStorage();
final _storageKey = 'ThemeMode';
static ThemeService instance = ThemeService._();
// ignore: empty_constructor_bodies
ThemeService._() {}
set themeMode(ThemeMode themeMode) {
if (themeMode == ThemeMode.system) {
_getStorage.remove(_storageKey);
} else {
_getStorage.write(_storageKey, themeMode == ThemeMode.dark);
}
Get.changeThemeMode(themeMode);
}
ThemeMode get themeMode {
switch (_getStorage.read(_storageKey)) {
case true:
return ThemeMode.dark;
case false:
return ThemeMode.light;
default:
return ThemeMode.system;
}
}
//Get the theme by name and handle light and dark mode
ThemeData getTheme(String name, {bool isDark = false}) {
ThemeData currTheme = ThemeData();
switch (name) {
case Red:
currTheme = isDark ? ThemeRed.darkTheme() : ThemeRed.lightTheme();
break;
case Indigo:
currTheme = isDark ? ThemeIndigo.darkTheme() : ThemeIndigo.lightTheme();
break;
case HippieBlue:
currTheme =
isDark ? ThemeHippieBlue.darkTheme() : ThemeHippieBlue.lightTheme();
break;
case Green:
currTheme = isDark ? ThemeGreen.darkTheme() : ThemeGreen.lightTheme();
break;
case DeepBlue:
currTheme =
isDark ? ThemeDeepBlue.darkTheme() : ThemeDeepBlue.lightTheme();
break;
case Sakura:
currTheme = isDark ? ThemeSakura.darkTheme() : ThemeSakura.lightTheme();
break;
case Purple:
currTheme = isDark ? ThemePurple.darkTheme() : ThemePurple.lightTheme();
break;
case Brown:
currTheme = isDark ? ThemeBrown.darkTheme() : ThemeBrown.lightTheme();
break;
case Gold:
currTheme = isDark ? ThemeGold.darkTheme() : ThemeGold.lightTheme();
break;
case Orang:
currTheme = isDark ? ThemeOrang.darkTheme() : ThemeOrang.lightTheme();
break;
}
return currTheme;
}
}
2.3 Show the theme selection
We need to show the theme selection to the user to choose which theme they want to use, to simplify the operation, I use babstrap_settings_screen and hardcode to put the theme items in a ListView
, of course you can use other way to present it.
below is only a code snippet for presenting one theme item with babstrap_settings_screen
in ListView
in UI page
/lib/app/ui/pages/settings_theme_page/settings_theme_page.dart
ListView(
children: [
SettingsGroup(
items: [
SettingsItem(
onTap: () {
controller.changeTheme(
isDarkMode,
ThemeService.Red,
isDarkMode
? ThemeRed.darkTheme()
: ThemeRed.lightTheme());
},
icons: CupertinoIcons.paintbrush_fill,
iconStyle: IconStyle(
iconsColor: Colors.white,
withBackground: true,
backgroundColor: Colors.red,
),
title: LabelKeys.theme1.tr,
trailing: Radio<String>(
value: ThemeItem.Red,
groupValue: controller.selectedValue.value,
activeColor: Theme.of(context)
.primaryColor, // Change the active radio button color here
fillColor: MaterialStateProperty.all(Theme.of(context)
.secondaryHeaderColor), // olor when selected
splashRadius:
20, // Change the splash radius when clicked
onChanged: (value) {
controller.changeTheme(
isDarkMode,
value!,
isDarkMode
? ThemeRed.darkTheme()
: ThemeRed.lightTheme());
},
),
),
],
),
],
)
and we can get the current theme mode by the below code
Brightness currentBrightness = MediaQuery.of(context).platformBrightness;
bool isDarkMode = currentBrightness == Brightness.dark;
and handle the backend logic in controller
/lib/app/controllers/settings_theme_controller.dart
class SettingsThemeController extends GetxController {
//define a variable to save selected theme value
var selectedValue = Global.defaultTheme.obs;
//use the local storage to save the selected theme
var localStorage = Get.find<LocalStorageService>();
@override
void onReady() async {
super.onReady();
//get the current theme from local storage
var currTheme =
await localStorage.getValue<String>(LocalStorageKeys.currentTheme);
//set the current theme
selectedValue.value = currTheme!;
}
//change the theme when user click the item
void changeTheme(bool isDarkMode, String name, ThemeData theme) {
selectedValue.value = name;
localStorage.setValue<String>(LocalStorageKeys.currentTheme, name);
Get.changeThemeMode(isDarkMode ? ThemeMode.dark : ThemeMode.light);
Get.changeTheme(theme);
}
}
2.4 Handle default theme when App startup
In the end, we need to handle the default theme or selected theme when the app starts. So we need to update the main.dart
file
If you create the GetX project like this article, you should get something like the below code in the main.dart
void main() async {
WidgetsFlutterBinding.ensureInitialized();
DependecyInjection.init();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return ScreenUtilInit(
builder: (_, __) {
return GetMaterialApp(
title: 'Win_reading_harry_potter',
debugShowCheckedModeBanner: false,
translations: Translation(),
initialBinding: AppBindings(),
initialRoute: AppRoutes.SPLASH,
unknownRoute: AppPages.unknownRoutePage,
getPages: AppPages.pages,
builder: (_, child) {
return MainLayout(child: child!);
},
);
},
//! Must change it to true if you want to use the ScreenUtil
designSize: const Size(411, 823),
);
}
}
we need to add some codes to support the theme
void main() async {
...
//get the selected theme from local storage
var localStorage = LocalStorageService();
var currTheme =
await localStorage.getValue<String>(LocalStorageKeys.currentTheme);
//if there is no theme for first time, then use the default theme
if (currTheme == null) {
currTheme = Global.defaultTheme;
localStorage.setValue<String>(
LocalStorageKeys.currentTheme, Global.defaultTheme);
}
//pass the current theme to material app
runApp(MyApp(currTheme: currTheme));
}
so also update the below
class MyApp extends StatelessWidget {
//support the parameter for setting the current theme
const MyApp({super.key, this.currTheme});
final String? currTheme;
@override
Widget build(BuildContext context) {
//get the light and dark theme base on current theme
ThemeData lightTheme = currTheme == null
? ThemeDeepBlue.lightTheme()
: ThemeItem.getTheme(currTheme!);
ThemeData darkTheme = currTheme == null
? ThemeDeepBlue.darkTheme()
: ThemeItem.getTheme(currTheme!, isDark: true);
return ScreenUtilInit(
builder: (_, __) {
return GetMaterialApp(
title: 'Win_reading_harry_potter',
debugShowCheckedModeBanner: false,
theme: lightTheme, //set the light theme
darkTheme: darkTheme, //set the dark theme
themeMode: ThemeService.instance.themeMode, //set the default theme mode
translations: Translation(),
initialBinding: AppBindings(),
initialRoute: AppRoutes.SPLASH,
unknownRoute: AppPages.unknownRoutePage,
getPages: AppPages.pages,
builder: (_, child) {
return MainLayout(child: child!);
},
);
},
//! Must change it to true if you want to use the ScreenUtil
designSize: const Size(411, 823),
);
}
}
Done!
3. Conclusion
FlexColorScheme
can help to make beautiful Flutter Material Design themes, and GetX
and easy to handle and change the theme dynamic, so if you combine them, you will find that a great job!