美文网首页
Flutter 一键换肤、切换主题

Flutter 一键换肤、切换主题

作者: AndyLiYL | 来源:发表于2023-01-17 11:25 被阅读0次

    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(); //监测 自动切换暗黑和正常模式
      }
    

    效果如下:

    Simulator Screen Shot - iPhone 14 Pro Max - 2023-01-17 at 17.40.34.png
    QQ20230118-111706.gif

    我这目前只演示了替换导航颜色,其他文本以及颜色目前未全部处理,不过千篇一律,按照色值读取以及设置即可~

    长风破浪会有时,直挂云帆济沧海!加油~

    相关文章

      网友评论

          本文标题:Flutter 一键换肤、切换主题

          本文链接:https://www.haomeiwen.com/subject/adithdtx.html