基于GetX 搭建通用flutter 项目《三》(暗黑模式)

2,430 阅读6分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路

基于GetX 搭建通用flutter 项目《一》

基于GetX 搭建通用flutter 项目《二》(界面规范抽象类)

基于GetX 搭建通用flutter 项目《四》(国际化)

基于GetX 搭建通用flutter 项目《五》(基于GetX 进行动态刷新)

加快脚步,马不停歇,趁这几天事情不多,赶快把之前欠的东西补上。好了,废话不多说了,上“技师”

  • DEMO更新日志

    2022-07-14 完成国际化
    2022-06-22 完成暗黑模式功能开发
    2022-06-16 完成项目基础架构
    

您能在这里看到啥

  1. 运行效果
  2. 暗黑模式
  3. 跟随系统
  4. 主题配置
  5. 项目地址 相对基于GetX 搭建通用flutter 项目《二》(界面规范抽象类)这篇文章来说,暗黑模式和基于GetX 搭建通用flutter 项目《四》(国际化)相对来说简单了许多,最多的就是,需要你有耐心,把所需要的主题和语言配置好就行了

运行效果

1657349104736057.gif

暗黑模式

这里我就不再介绍啥是暗黑模式了,咱们直接上代码,因为代码最直观.

主题切换

主题配置主要分为三步 第一步就是👆我们配置主题配置项,不管是用第一种,还是第二种,都是要把对应的主题颜色进行配置 第二步就是在入口页MaterialApp进行主题的设置. 第三步就是在切换主题事件中,进行主题切换,以及状态更新

1.配置主题

下面是我项目中配置的颜色主题。 我在这里也定义了一个主题颜色的抽象类

abstract class AbsThemeColorConfig {
    /// 这里配置正常颜色
    Map<String, Color?> lightInfo = {};
    /// 这里配置暗黑颜色
    Map<String, Color?> darkInfo = {};
    /// 获取配置当前颜色
    Color confgColor(String colkey, {bool reversal = false}) {
        bool isbackmodel = loadThemeModel();
        if (reversal) {
            isbackmodel = !isbackmodel;
        }
        Map colorinfo = isbackmodel ? darkInfo : lightInfo;
        return colorinfo[colkey] ?? Colors.black;
    }
    /// 获取当前的主题模式 使用者可以重写这个方法,来改变当前的主题状态
    bool loadThemeModel() {
      return false;
    }
}

👇是我项目里使用的情况

/// 这是我定义的颜色key
class HzyColorSring {
  static String coltheme = "theme";
  static String colffffff = "ffffff";
  static String col000000 = "000000";
  static String whitebgblacktextcolor = "whiteback";
}

class HzyThemeColor extends AbsThemeColorConfig {
  /// 这是个单利
  HzyThemeColor._internal();
  factory HzyThemeColor() => _instance;
  static late final HzyThemeColor _instance = HzyThemeColor._internal();

  @override
  Map<String, Color?> get lightInfo => 
      HzyColorSring.coltheme: CommentColorS.col2865ff,
      HzyColorSring.colffffff: CommentColorS.colffffff,
      HzyColorSring.col000000: CommentColorS.col000000,
  };

  @override
  Map<String, Color?> get darkInfo => {
      HzyColorSring.coltheme: CommentColorS.col2865ff,
      HzyColorSring.colffffff: CommentColorS.col171717,
      HzyColorSring.col000000: CommentColorS.colffffff,
  };
  @override
  bool loadThemeModel() {
      /// 这里里面是我获取 主题的方法来我们看看一下里面实现的内容
      return ThemeTool.isdark();
  }
}

现在开始讲一下 具体的主题改变的状态监听

static isdark() {
   /// 这是本地存的 主题的状态,因为需要持有化,所以会把对应的状态,存起来
   /// 深色模式 0: 关闭 1: 开启 2: 随系统
   int type = CommonSpUtil.getThemeType();
   if (type == 2) {
       /// 这里是 跟随系统的逻辑,判断当前状态,来返回对应的主题
       return MediaQuery.of(Get.context!).platformBrightness == Brightness.dark;
   } else {
       return Get.isDarkMode;
   }
}

为了使用方便,我又把上面的做了一下封装,具体你们封不封装 取决于习惯,下面是我封装的类

class HzyColorConfig {
   Color colthemes = HzyThemeColor().confgColor(HzyColorSring.coltheme);
   Color col000000 = HzyThemeColor().confgColor(HzyColorSring.col000000);
}

到此,其实颜色的配置已经完成,具体您的项目,可能会有很多颜色值的配置,那就是重复的东西了,费点时间就可以下了. 当然你也可以用系统自带的Theme* 来做处理,👇我也简单的举个🌰.

//创建Dark ThemeData对象
final ThemeData appDarkThemeData = ThemeData(
    brightness: Brightness.dark,
    primaryColor: CommentColorS.col010101,
    splashColor: Colors.white.withOpacity(0),
    splashFactory: const NormalNoSplashFactory(),
    highlightColor: Colors.white.withOpacity(0),
    scaffoldBackgroundColor:CommentColorS.col010101,
    textTheme: const TextTheme(
        headline5: TextStyle(
            color: CommentColorS.colffffff,
            fontWeight: FontWeight.bold,
            fontSize: 24),
    bodyText1: TextStyle(
        color: CommentColorS.colffffff,
        fontSize: 16.0,
    ),
    bodyText2: TextStyle(
        color: CommentColorS.colffffff,
        fontSize: 14,
        ),
    ),
    appBarTheme: const AppBarTheme(
        iconTheme: IconThemeData(color: Colors.white),
        backgroundColor: CommentColorS.col010101,
    ),
);

//创建light ThemeData对象
final ThemeData appLightThemeData = ThemeData(
    brightness: Brightness.light,
    primaryColor: CommentColorS.colffffff,
    splashColor: Colors.white.withOpacity(0),
    highlightColor: Colors.white.withOpacity(0),
    splashFactory: const NormalNoSplashFactory(),
    scaffoldBackgroundColor:
    CommentColorS.colffffff, 
    textTheme: const TextTheme(
    /// 字体24
    headline5: TextStyle(
        color: CommentColorS.col333333,
        fontWeight: FontWeight.bold,
    ),
    bodyText1: TextStyle(
        color: CommentColorS.col333333,
        fontSize: 16.0,
    ),
    bodyText2:
    TextStyle(color: CommentColorS.col333333, fontSize: 14)), // 字体主题
    appBarTheme: const AppBarTheme(
    iconTheme: IconThemeData(color: Colors.black),
        backgroundColor: Colors.white,
    ),
);
2.MaterialApp

👇是我代码中MaterialApp配置清单

class Application extends StatelessWidget {
   Application({Key? key}) : super(key: key);
   final easyLoading = EasyLoading.init();
   @override

   Widget build(BuildContext context) {
       String rootroutes = UtilsTool.configrootpageid();
       return ScreenUtilInit(
           designSize: const Size(375, 667),
           minTextAdapt: true,
           splitScreenMode: true,
           builder: (_) {
           return GestureDetector(
               /// 全局点击 事件
               onTap: () => CommentTools.keydissmiss(context),
                   /// 通过GetX 创建根视图
                   child: GetMaterialApp(
                       // /// 入口路由
                       initialRoute: rootroutes,
                       /// 所有路由集合
                       getPages: RouterS.getAllRoutS(),
                       /// 是否显示 导航栏右上角 debug 标识
                       debugShowCheckedModeBanner: false,
                       builder: (context, child) => MediaQuery(
                           //设置文字大小不随系统设置改变
                           data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0),
                           child: easyLoading(context, child),
                       ),
                       /// 这里如果你使用ThemeData 可以在这里使用你定义的themeData  
                       theme: ThemeData(brightness: Brightness.light),
                       darkTheme: ThemeData(brightness: Brightness.dark),
                       /// 配置 本地存储 主题类型
                       themeMode: ThemeTool.getlocalprofileaboutThemeModel(),
                       /// 国际化配置 代理
                       localizationsDelegates: const [
                           GlobalMaterialLocalizations.delegate,
                           GlobalWidgetsLocalizations.delegate,
                           GlobalCupertinoLocalizations.delegate, // ios
                       ],
                       initialBinding: AllControllerBinding(),
                       /// 本地支持语言
                       supportedLocales: const [
                           Locale('zh', 'CN'),
                           Locale('en', 'US'),
                       ],

                   );    
               );
           },
       );
   }
}

这里面相对关键就是下面这个方法,是为了配置APP启动的时候,配置当前的主题模式.

   /// 配置 本地存储 主题类型
   themeMode: ThemeTool.getlocalprofileaboutThemeModel(),

我们来看一下这个getlocalprofileaboutThemeModel()方法里是怎么实现呢

/// 获取本地 主题配置
static getlocalprofileaboutThemeModel() {
    int theme = CommonSpUtil.getThemeType();
    ThemeMode themeMode = ThemeMode.light;
    if (theme == 0) {
        /// 正常模式
        themeMode = ThemeMode.light;
    } else if (theme == 1) {
        /// 暗黑模式
        themeMode = ThemeMode.dark;
    } else if (theme == 2) {
        /// 跟随系统模式
        themeMode = ThemeMode.system;
    }
    return themeMode;
}
3.切换主题

前面两步配置好了,下面就是我们来使用切换主题的事情了,具体页面样式,是你们自己的,我这边是模仿微信写的,来,我们看看具体的切换事件是怎么实现的

/// 切换主题
static changeTheme({int? type = 1}) {
    /// 获取当前的主题模型
    ThemeMode mode = getlocalprofileaboutThemeModel();
    /// 配置当前的主题数据
    ThemeData themeData = getlocalprofileaboutThemeData();
    /// 由于我这里使用第三方加载动画,也把这个进行了状态的修改
    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;
    /// 然后使用GetX 提供的切换方式,进行动态更新就可以
    Get.changeThemeMode(mode);
    Get.changeTheme(themeData);
    /// 这个比较重要,如果不使用这个,可能会导致主题没有及时更新
    updateTheme();
}

/// 更新主题
static updateTheme() {
    Future.delayed(const Duration(milliseconds: 250), () {
        Get.forceAppUpdate();
    });
}

/// 根据本地存储的状态,进行主题数据赋值
static getlocalprofileaboutThemeData() {
    int theme = CommonSpUtil.getThemeType();
    ThemeData themeData = ThemeData.light();
    if (theme == 0) {
        themeData = ThemeData(brightness: Brightness.light);
    } else if (theme == 1) {
        themeData = ThemeData(brightness: Brightness.dark);
    } else if (theme == 2) {
        if (!Get.isDarkMode) {
            themeData = ThemeData(brightness: Brightness.dark);
        } else {
             themeData = ThemeData(brightness: Brightness.light);
        }
    }
    return themeData;
}

到此主题切换就完成,好像本地存储没有讲,但是这个相对来说简单点,你可以自行看项目

好了分享结束,马上开始写第四篇文章 基于GetX 搭建通用flutter 项目《四》(国际化)

喜欢的可以点点赞,你们的鼓励,就是我前进的动力.谢谢

hzy_normal_widget 是我在使用GetX搭建项目时,总结的一些通用开发控件,方便我们在开发的时候,减少重复性界面代码的创建.

ttcomment 通用项目的界面接口基类,和一些通用工具类,喜欢的可以点点star.

当然接下的时间里我也会总结OCswift 相应的通用项目文章,喜欢的可以点点关注