Flutter 一键换肤、切换主题

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 {
  
  // 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",
      };

  
  // 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});

  
  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来监听

  
  void initState() {
    // TODO: implement initState
    super.initState();
    WidgetsBinding.instance.addObserver(this);
  }

重写:didChangePlatformBrightness方法,在此方法内切换主题即可

  
  void didChangePlatformBrightness() {
    // 系统自动变化、切换暗黑模式和正常模式,回调方法
    // TODO: implement didChangePlatformBrightness
    super.didChangePlatformBrightness();
    ThemeTool.changeTheme(); //监测 自动切换暗黑和正常模式
  }

效果如下:

在这里插入图片描述
在这里插入图片描述

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

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

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值