Flutter 一鍵換膚寞焙、切換主題

App項(xiàng)目一鍵換膚功能比較常見(jiàn)了,一般項(xiàng)目都附帶有該功能互婿,由于近期項(xiàng)目?jī)?nèi)也加入了此功能捣郊,也順帶記錄下過(guò)程。

由于產(chǎn)品說(shuō)還想要從后臺(tái)配置相關(guān)配色慈参,通過(guò)后臺(tái)隨時(shí)控制呛牲,所以我反手就給他一個(gè)大比兜,然后就開寫代碼了~ (づ ̄3 ̄)づ╭?~

首先第一步先配置下主題相關(guān):

創(chuàng)了一個(gè)類專門管理以及處理顏色讀取相關(guān):

abstract class ThemeColorConfig {
  //正常模式驮配、也可讀取后臺(tái)數(shù)據(jù)模式
  Map<String, dynamic> normalColorInfo = {};

  //暗黑模式娘扩、可跟隨系統(tǒng)
  Map<String, dynamic> darkColorInfo = {};

  Color configColor(String colorKey) {
    //讀取是否是暗黑模式 這里是讀取存儲(chǔ)的模式,使用的SpUtil封裝的壮锻,比較簡(jiǎn)易琐旁,就不貼了
    String isDark = CommonSpUtil.getThemeType();
    Map colorInfo = isDark == "isDark" ? darkColorInfo : normalColorInfo;
    return ColorsUtil.hexToColor(colorInfo[colorKey]);
  }
}

class ThemColorUtil extends ThemeColorConfig {
  @override
  // TODO: implement darkColorInfo
  // 暗黑模式,可隨系統(tǒng)變化
  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
  // 正常模式猜绣、也可讀取后臺(tái)數(shù)據(jù)模式
  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"
            };
}

創(chuàng)建一個(gè)ColorsUtil類灰殴,以及使用上面類來(lái)管理顏色以及主要主題色,便于使用和取值:

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";

  /// 十六進(jìn)制顏色牺陶,
  /// hex, 十六進(jìn)制值,例如:0xffffff,
  /// alpha, 透明度 [0.0,1.0]
  static Color hexToColor(dynamic string) {
    /// 如果傳入的十六進(jìn)制顏色值不符合要求辣之,返回默認(rèn)值
    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);
  }

  ///生成隨機(jī)顏色
  static Color randomColor() {
    return Color.fromRGBO(
        Random().nextInt(255), Random().nextInt(255), Random().nextInt(255), 1);
  }

  /// 項(xiàng)目主題色
  Color themeColor = ThemColorUtil().configColor(
      them); //Color(0xFF1F1D2B); //ColorsUtil.hexToColor("#1F1D2B");

  /// 項(xiàng)目背景色
  Color backgroundColor = ThemColorUtil().configColor(background);

  /// 文本
  Color textColor = ThemColorUtil().configColor(text);

  /// cloC6C9DB
  Color colorC6C9DB = ThemColorUtil().configColor(cloC6C9DB);
}

好了掰伸,主題色值基本配置完畢,可以本地設(shè)置好多種膚色或者通過(guò)后臺(tái)接口請(qǐng)求來(lái)怀估,然后根據(jù)鍵值對(duì)進(jìn)行對(duì)比取值即可狮鸭。

第二步:

切換主題:

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

    //這里設(shè)置這個(gè)延遲原因是:在調(diào)用切換主題后,無(wú)法立即生效奏夫,會(huì)有一些延遲怕篷,如果不延遲會(huì)讀取還是上個(gè)主題
    //使用Get 強(qiáng)制更新app狀態(tài)
    Future.delayed(const Duration(milliseconds: 300), () {
      print("執(zhí)行這里");
      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入口函數(shù)中的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, //默認(rèn)展示本地語(yǔ)言
          fallbackLocale: const Locale('zh', 'CN'), //語(yǔ)言選擇無(wú)效時(shí)酗昼,備用語(yǔ)言
          /// 支持語(yǔ)言
          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),

          **/// 配置 本地存儲(chǔ) 主題類型**
          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,
            );
          }),
        );
      },
    );
  }
}

到這一步基本一鍵切換主題廊谓,就基本完成了,可以盡情切換了麻削。(當(dāng)時(shí)我也以為完事了蒸痹,但是忽略了一個(gè)問(wèn)題春弥,跟隨系統(tǒng)可變)

上面配置完以后,通過(guò)測(cè)試發(fā)現(xiàn)叠荠,無(wú)法跟隨系統(tǒng)變那匿沛,不符合需求,那么就需要監(jiān)聽手機(jī)的主題切換了榛鼎。

我們可以在項(xiàng)目首頁(yè)home_page內(nèi)逃呼,繼承WidgetsBindingObserver來(lái)監(jiān)聽

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

重寫:didChangePlatformBrightness方法,在此方法內(nèi)切換主題即可

  @override
  void didChangePlatformBrightness() {
    // 系統(tǒng)自動(dòng)變化者娱、切換暗黑模式和正常模式抡笼,回調(diào)方法
    // TODO: implement didChangePlatformBrightness
    super.didChangePlatformBrightness();
    ThemeTool.changeTheme(); //監(jiān)測(cè) 自動(dòng)切換暗黑和正常模式
  }

效果如下:

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

QQ20230118-111706.gif

我這目前只演示了替換導(dǎo)航顏色,其他文本以及顏色目前未全部處理黄鳍,不過(guò)千篇一律推姻,按照色值讀取以及設(shè)置即可~

長(zhǎng)風(fēng)破浪會(huì)有時(shí),直掛云帆濟(jì)滄海框沟!加油~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末藏古,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子忍燥,更是在濱河造成了極大的恐慌拧晕,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,123評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件灾前,死亡現(xiàn)場(chǎng)離奇詭異防症,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)哎甲,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)饲嗽,“玉大人炭玫,你說(shuō)我怎么就攤上這事∶蚕海” “怎么了吞加?”我有些...
    開封第一講書人閱讀 156,723評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)尽狠。 經(jīng)常有香客問(wèn)我衔憨,道長(zhǎng),這世上最難降的妖魔是什么袄膏? 我笑而不...
    開封第一講書人閱讀 56,357評(píng)論 1 283
  • 正文 為了忘掉前任践图,我火速辦了婚禮,結(jié)果婚禮上沉馆,老公的妹妹穿的比我還像新娘码党。我一直安慰自己德崭,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,412評(píng)論 5 384
  • 文/花漫 我一把揭開白布揖盘。 她就那樣靜靜地躺著眉厨,像睡著了一般。 火紅的嫁衣襯著肌膚如雪兽狭。 梳的紋絲不亂的頭發(fā)上憾股,一...
    開封第一講書人閱讀 49,760評(píng)論 1 289
  • 那天,我揣著相機(jī)與錄音箕慧,去河邊找鬼服球。 笑死,一個(gè)胖子當(dāng)著我的面吹牛销钝,可吹牛的內(nèi)容都是我干的有咨。 我是一名探鬼主播,決...
    沈念sama閱讀 38,904評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼蒸健,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼座享!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起似忧,我...
    開封第一講書人閱讀 37,672評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤渣叛,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后盯捌,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體淳衙,經(jīng)...
    沈念sama閱讀 44,118評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,456評(píng)論 2 325
  • 正文 我和宋清朗相戀三年饺著,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了箫攀。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,599評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡幼衰,死狀恐怖靴跛,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情渡嚣,我是刑警寧澤梢睛,帶...
    沈念sama閱讀 34,264評(píng)論 4 328
  • 正文 年R本政府宣布,位于F島的核電站识椰,受9級(jí)特大地震影響绝葡,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜腹鹉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,857評(píng)論 3 312
  • 文/蒙蒙 一藏畅、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧种蘸,春花似錦墓赴、人聲如沸竞膳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)坦辟。三九已至,卻和暖如春章办,著一層夾襖步出監(jiān)牢的瞬間锉走,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工藕届, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留挪蹭,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,286評(píng)論 2 360
  • 正文 我出身青樓休偶,卻偏偏與公主長(zhǎng)得像梁厉,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子踏兜,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,465評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容