App项目一键换肤功能比较常见了,一般项目都附带有该功能,由于近期项目内也加入了此功能,也顺带记录下过程。
由于产品说还想要从后台配置相关配色,通过后台随时控制,所以我反手就给他一个大比兜,然后就开写代码了~ (づ ̄3 ̄)づ╭❤~
首先第一步先配置下主题相关:
创了一个类专门管理以及处理颜色读取相关:
abstract class ThemeColorConfig {
//正常模式、也可读取后台数据模式
Map<String, dynamic> normalColorInfo = {};
//暗黑模式、可跟随系统
Map<String, dynamic> darkColorInfo = {};
Color configColor(String colorKey) {
//读取是否是暗黑模式 这里是读取存储的模式,使用的SpUtil封装的,比较简易,就不贴了
String isDark = CommonSpUtil.getThemeType();
Map colorInfo = isDark == "isDark" ? darkColorInfo : normalColorInfo;
return ColorsUtil.hexToColor(colorInfo[colorKey]);
}
}
class ThemColorUtil extends ThemeColorConfig {
@override
// TODO: implement darkColorInfo
// 暗黑模式,可随系统变化
Map<String, dynamic> get darkColorInfo => {
'backgroundColor': "#2865ff",
'text': "#6A5ACD",
'button': "#1E90FF",
'content': "#000000",
'themColor': "#8A2BE2",
'line': "#999999",
'space': "#E8E8E8",
'cloc3c3c3': "#c3c3c3",
'redText': "#FF6650",
'cloC6C9DB': "#C6C9DB",
};
@override
// TODO: implement normalColorInfo
// 正常模式、也可读取后台数据模式
Map<String, dynamic> get normalColorInfo =>
SpUtil.getString("colorMaps")!.isNotEmpty
? jsonDecode(SpUtil.getString("colorMaps")!)
: {
'text': "#66FFCC",
'button': "#1F1D2B",
'backgroundColor': "#1F1D2B",
'content': "#000000",
'themColor': "#1F1D2B",
'line': "#999999",
'space': "#E8E8E8",
'c3c3c3': "#c3c3c3",
'redText': "#FF6650"
};
}
创建一个ColorsUtil类,以及使用上面类来管理颜色以及主要主题色,便于使用和取值:
class ColorsUtil {
// 颜色键值,取值用
static String them = "themColor";
static String background = "backgroundColor";
static String text = "text";
static String button = "button";
static String content = "content";
static String line = "line";
static String space = "space";
static String cloc3c3c3 = "cloc3c3c3";
static String redText = "redText";
static String cloC6C9DB = "cloC6C9DB";
/// 十六进制颜色,
/// hex, 十六进制值,例如:0xffffff,
/// alpha, 透明度 [0.0,1.0]
static Color hexToColor(dynamic string) {
/// 如果传入的十六进制颜色值不符合要求,返回默认值
if (string == null ||
string.length != 7 ||
int.tryParse(string.substring(1, 7), radix: 16) == null) {
string = '#999999';
}
return Color(int.parse(string.substring(1, 7), radix: 16) + 0xFF000000);
}
///生成随机颜色
static Color randomColor() {
return Color.fromRGBO(
Random().nextInt(255), Random().nextInt(255), Random().nextInt(255), 1);
}
/// 项目主题色
Color themeColor = ThemColorUtil().configColor(
them); //Color(0xFF1F1D2B); //ColorsUtil.hexToColor("#1F1D2B");
/// 项目背景色
Color backgroundColor = ThemColorUtil().configColor(background);
/// 文本
Color textColor = ThemColorUtil().configColor(text);
/// cloC6C9DB
Color colorC6C9DB = ThemColorUtil().configColor(cloC6C9DB);
}
好了,主题色值基本配置完毕,可以本地设置好多种肤色或者通过后台接口请求来,然后根据键值对进行对比取值即可。
第二步:
切换主题:
class ThemeTool {
/// 切换主题
static changeTheme() {
ThemeMode mode = getLocalThemeModel();
ThemeData themeData = getLocalThemeData();
EasyLoadingStyle easyLoadingStyle = EasyLoadingStyle.dark;
if (mode == ThemeMode.dark) {
easyLoadingStyle = EasyLoadingStyle.light;
} else if (mode == ThemeMode.system) {
if (!Get.isDarkMode) {
easyLoadingStyle = EasyLoadingStyle.light;
}
}
EasyLoading.instance.loadingStyle = easyLoadingStyle;
Get.changeThemeMode(mode);
Get.changeTheme(themeData);
//这里设置这个延迟原因是:在调用切换主题后,无法立即生效,会有一些延迟,如果不延迟会读取还是上个主题
//使用Get 强制更新app状态
Future.delayed(const Duration(milliseconds: 300), () {
print("执行这里");
Get.forceAppUpdate();
});
}
/// 获取本地 主题配置
static getLocalThemeModel() {
//读取是否是暗黑模式
String isDark = CommonSpUtil.getThemeType();
ThemeMode themeMode = ThemeMode.light;
if (isDark == "isDark") {
themeMode = ThemeMode.system;
} else {
themeMode = ThemeMode.light;
}
return themeMode;
}
static getLocalThemeData() {
//读取是否是暗黑模式
String isDark = CommonSpUtil.getThemeType();
ThemeData themeData = ThemeData.light();
if (isDark == "isDark") {
if (!Get.isDarkMode) {
themeData = ThemeData(brightness: Brightness.dark);
} else {
themeData = ThemeData(brightness: Brightness.light);
}
} else {
themeData = ThemeData(brightness: Brightness.light);
}
return themeData;
}
}
最后,配置main入口函数中的GetMaterialApp:
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return ScreenUtilInit(
designSize: const Size(375, 812),
builder: (context, w) {
return GetMaterialApp(
title: 'App',
debugShowCheckedModeBanner: false,
initialBinding: InitBinding(),
initialRoute: RouterUtil.tabBar,
getPages: RouterUtil.getPages,
// translations: StringRes(),
defaultTransition: Transition.cupertino,
locale: LocaleTool.languageConfig().isNotEmpty
? Locale(LocaleTool.languageConfig()[0],
LocaleTool.languageConfig()[1])
: null, //默认展示本地语言
fallbackLocale: const Locale('zh', 'CN'), //语言选择无效时,备用语言
/// 支持语言
supportedLocales: S.delegate.supportedLocales,
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
CustomLocalDelegate.delegate,
S.delegate
],
theme: ThemeData(brightness: Brightness.light),
darkTheme: ThemeData(brightness: Brightness.dark),
**/// 配置 本地存储 主题类型**
themeMode: ThemeTool.getLocalThemeModel(),
builder: EasyLoading.init(builder: (context, child) {
return GestureDetector(
onTap: () {
FocusScopeNode currentFocus = FocusScope.of(context);
if (!currentFocus.hasPrimaryFocus &&
currentFocus.focusedChild != null) {
FocusManager.instance.primaryFocus?.unfocus();
}
},
child: child,
);
}),
);
},
);
}
}
到这一步基本一键切换主题,就基本完成了,可以尽情切换了。(当时我也以为完事了,但是忽略了一个问题,跟随系统可变)
上面配置完以后,通过测试发现,无法跟随系统变那,不符合需求,那么就需要监听手机的主题切换了。
我们可以在项目首页home_page内,继承WidgetsBindingObserver来监听
@override
void initState() {
// TODO: implement initState
super.initState();
WidgetsBinding.instance.addObserver(this);
}
重写:didChangePlatformBrightness方法,在此方法内切换主题即可
@override
void didChangePlatformBrightness() {
// 系统自动变化、切换暗黑模式和正常模式,回调方法
// TODO: implement didChangePlatformBrightness
super.didChangePlatformBrightness();
ThemeTool.changeTheme(); //监测 自动切换暗黑和正常模式
}
效果如下:
我这目前只演示了替换导航颜色,其他文本以及颜色目前未全部处理,不过千篇一律,按照色值读取以及设置即可~
长风破浪会有时,直挂云帆济沧海!加油~