Flutter 学习 之 主题设置 ThemeData

基于ThemeData 实现主题切换
1. 实现可以亮暗主题切换
2. 实现可以颜色主题的切换
3.当主题为白色的时候替换一个其他主颜色
4.当颜色为浅色时候需要做反色处理

演示效果


a5tnj-j7y0p.gif

一. 修改Main.dark 文件

    //多Provider 当前只用了一个 为以后打基础
    return MultiProvider(
      providers: [
        ChangeNotifierProvider<ThemeViewModel>(
          create: (context) => ThemeViewModel(),
        ),
      ],
      child: Consumer<ThemeViewModel>(
        builder: (context, themeProvider, Widget? child) {
          return MaterialApp(
            ///title android用来在多任务上显示昵称用的
            title: 'Flutter Theme Demo',
            //首页
            home: const MyHomePage(),
            //明亮的主题
            theme: themeProvider.getTheme(),
            //夜间模式
            darkTheme: themeProvider.getTheme(isDarkMode: true),
            //当前主题模式
            themeMode: themeProvider.getThemeMode(),
          );
        },
      ),
    );

二.定义 theme_provider.dart

  • 先拓展一下ThemeData 好通过SP保存
extension ThemeModeExitension on ThemeMode {
  String get value => <String>['System', 'Light', 'Dark'][index];
}
  • 创建类
class ThemeViewModel extends ChangeNotifier {
  ///主题模式 亮色 暗色 跟随系统
  ThemeMode? _themeMode;

  //亮色主题色
  late MaterialColor _themeColor;

  //暗色主题色
   final MaterialColor _themeDarkColor= ColorUtil.createMaterialColor(ColorConfig.darkBGColor);

  ThemeViewModel() {
    Color primaryColor=Colors.white; //设定一个初始颜色
    //如果由保存颜色则替换颜色
    String? color = CacheUtil().get<String>(SPName.themeColor);
    if (color != null) {
      //把存储的#RRGGBB颜色转换十六进制的0xFF格式的
      primaryColor = ColorUtil.hexToColor(color);
    }
    _themeColor = ColorUtil.createMaterialColor(primaryColor);
  }



  get selectColor => isDark() ? _themeDarkColor : _themeColor;

  bool isDark() {
    //如果当前是跟随系统那么判定当前系统是不是暗色模式
    if (_themeMode == ThemeMode.system) {
      return SchedulerBinding.instance?.window.platformBrightness ==
          Brightness.dark;
    }
    return _themeMode == ThemeMode.dark;
  }

//获取当前主题模式
  ThemeMode getThemeMode() {
    String? themeModel = CacheUtil().get<String>(SPName.themeMode);
    switch (themeModel) {
      case "Dark":
        _themeMode = ThemeMode.dark;
        break;
      case "System":
        _themeMode = ThemeMode.system;
        break;
      default:
        _themeMode = ThemeMode.light;
        break;
    }
    return _themeMode!;
  }
//获取保存的主题颜色
  Color getThemColor() {
    String? color = CacheUtil().get<String>(SPName.themeColor);
    return _themeColor =
        ColorUtil.createMaterialColor(ColorUtil.hexToColor(color));
  }

///设置主题
  set them(ThemeMode themeMode) {
    CacheUtil().setString(SPName.themeMode, themeMode.value);
    _themeMode = themeMode;
    notifyListeners();
  }
///设置主题颜色
  set themeColor(Color color) {
    CacheUtil().setString(SPName.themeColor, ColorUtil.color2HEX(color));
    _themeColor = ColorUtil.createMaterialColor(color);
    them=ThemeMode.light;
  }
  /// 获取主题
  ThemeData getTheme({bool isDarkMode = false}) {
    var themeData = ThemeData(
        brightness: isDarkMode ? Brightness.dark : Brightness.light,
        //错误颜色
        errorColor: Colors.red,
        //主题色
        primaryColor: isDarkMode ? _themeDarkColor : _themeColor,
        //浅主题色
        primaryColorLight: isDarkMode ? _themeDarkColor[50] : _themeColor[50],
        //深主题色
        primaryColorDark: isDarkMode ? _themeDarkColor[700] : _themeColor[700],
        // Tab指示器的颜色 如果当前主题色是白色 那么指点杆的颜色指定为另一个
        indicatorColor: isDarkMode
            ? ColorConfig.darkBtnColor
            : ColorUtil.isWhiteColor(_themeColor)
                ? ColorConfig.lightBtnColor
                : _themeColor,
        // 页面背景色
        scaffoldBackgroundColor:
            isDarkMode ? ColorConfig.darkBGColor : ColorConfig.lightBGColor,
        //开关 单选和多选按钮选中效果
        toggleableActiveColor: isDarkMode
            ? ColorConfig.darkBtnColor
            : ColorUtil.isWhiteColor(_themeColor)
                ? ColorConfig.lightBtnColor
                : _themeColor,
        //progressIndicator 的背景颜色
        progressIndicatorTheme: ProgressIndicatorThemeData(
          color: isDarkMode
              ? ColorConfig.darkBtnColor
              : ColorUtil.isWhiteColor(_themeColor)
                  ? ColorConfig.lightBtnColor
                  : _themeColor,
        ),
        // textButtonTheme: TextButtonThemeData(style: ButtonStyle(MaterialStateProperty.all())),
        //Material 组件默认使用这个颜色 如果没配置则是一个蓝色
        primarySwatch: isDarkMode ? _themeDarkColor : _themeColor,
        //大部分的ListTile 控件都会用到 除了  ExpansionTile 他会覆盖一部分组件样式
        listTileTheme: ListTileThemeData(
          selectedTileColor: Colors.blue,
          iconColor: isDark()
              ? ColorConfig.darkBtnColor
              : ColorUtil.isLightColor(_themeColor)
                  ? ColorConfig.lightBtnColor
                  : _themeColor,
        ),
        //icon的主题设置
        iconTheme: IconThemeData(
            color: isDarkMode
                ? ColorConfig.darkBtnColor
                : ColorUtil.isWhiteColor(_themeColor)
                    ? ColorConfig.lightBtnColor
                    : _themeColor,
            size: 16),
        // textButtonTheme:TextButtonThemeData() ,
        //具体组件的颜色配置
        appBarTheme: AppBarTheme(
          backgroundColor: isDarkMode ? _themeDarkColor : _themeColor,
          elevation: 0,
          centerTitle: true,
          titleTextStyle: TextStyle(
              color: isDark()
                  ? ColorConfig.dartH1Color
                  : ColorUtil.isLightColor(_themeColor)
                      ? Colors.black
                      : Colors.white,
              fontSize: 20),
        ),
        //具体文字的颜色配置
        textTheme: TextTheme(
          //ListTitle中的Title用到
          subtitle1: TextStyle(
              color: isDarkMode
                  ? ColorConfig.dartH1Color
                  : ColorConfig.lightH1Color,
              fontSize: 18),
          subtitle2: TextStyle(
              color: isDark()
                  ? ColorConfig.dartH1Color
                  : ColorUtil.isLightColor(_themeColor)
                      ? Colors.black
                      : Colors.white,
              fontSize: 16),
          //用在非Material组件上的文字显示,
          bodyText1: TextStyle(
              color: isDarkMode
                  ? ColorConfig.dartH2Color
                  : ColorConfig.lightH2Color,
              fontSize: 16),
          //Material组件上的文字显示
          bodyText2: TextStyle(
              color: isDarkMode
                  ? ColorConfig.dartH2Color
                  : ColorConfig.lightH2Color,
              fontSize: 16),
          //ListTitle的副标题用这个颜色
          caption: TextStyle(
              color: isDarkMode
                  ? ColorConfig.dartH3Color
                  : ColorConfig.lightH3Color,
              fontSize: 14),

          button: TextStyle(
              color: isDarkMode
                  ? ColorConfig.darkBtnColor
                  : ColorUtil.isWhiteColor(_themeColor)?ColorConfig.lightBtnColor:_themeColor,
              fontSize: 16),
        ));
    return themeData;
  }
}

三.在另一界面的使用

  • 创建ViewModel
class HomePageViewModel extends BaseViewModel {
    //创建模式选择
  List<Map<String, dynamic>> modelItem = [
    {"name": '跟随系统', "mode": ThemeMode.system},
    {"name": '白天模式', "mode": ThemeMode.light},
    {"name": '夜间模式', "mode": ThemeMode.dark},
  ];
  //创建主题颜色
  List<String> colorList = [
    "#FFFFFF",
    "#843900",
    "#905d1d",
    "#b3424a",
    "#181d4b",
    "#d9d6c3",
    "#281f1d",
    "#ffeb3b",
    "#2A2C37",
    "#00FA9A",
    "#009ACD",
  ];
  //当前的模式
  ThemeMode? selectModel;
  //上下文
  BuildContext? context;
  //选择的颜色
  Color? selectColor;
  //themeViewModel
  var provider;
//切换模式
  void changeModel(value) {
    provider.them = value;
    selectModel = value;
    notifyListeners();
  }
//切换颜色
  void onChangeColor(String colorStr) {
    Color col = ColorUtil.hexToColor(colorStr);
    ThemeMode mode = ThemeMode.light;
    //如果切换的颜色是夜间模式 那么直接切换模式
    if (col == ColorConfig.darkBGColor) {
      mode = ThemeMode.dark;
      changeModel(mode);
    } else {
      provider.themeColor = col;
    }
    selectColor = col;
    selectModel = mode;
    notifyListeners();
  }
//初始化加载
  onLoad(context) {
    this.context = context;
    provider = Provider.of<ThemeViewModel>(context);
    selectModel = provider.getThemeMode();
    selectColor = provider.getThemColor();
    setIdly();
  }
}

详细使用详见源码

四. 使用到的颜色工具类

import 'dart:ui';

import 'package:flutter/material.dart';

class ColorUtil{
  ///判断一个颜色是否是亮色 用来判断反色
  static bool isLightColor(Color color){
    double darkness =  1 - (0.299 * color.red + 0.587 * color.green + 0.114 * color.blue) / 255;
    return darkness<0.5;
  }
  //判断它是不是白色
  static bool isWhiteColor(Color color){
    return color.value.toRadixString(16) == "ffffffff";
  }
  /// Construct a color from a hex code string, of the format #RRGGBB.
  static Color hexToColor(String? code) {
    if (code==null||code==""|| code.length != 7) {
      return Colors.blue;
    }
    return Color(int.parse(code.substring(1, 7), radix: 16) + 0xFF000000);
  }

  ///创建Material风格的color
  static MaterialColor createMaterialColor(Color color) {
    List strengths = <double>[.05];
    Map swatch = <int, Color>{};
    final int r = color.red, g = color.green, b = color.blue;

    for (int i = 1; i < 10; i++) {
      strengths.add(0.1 * i);
    }
    for (var strength in strengths) {
      final double ds = 0.5 - strength;
      swatch[(strength * 1000).round()] = Color.fromRGBO(
        r + ((ds < 0 ? r : (255 - r)) * ds).round(),
        g + ((ds < 0 ? g : (255 - g)) * ds).round(),
        b + ((ds < 0 ? b : (255 - b)) * ds).round(),
        1,
      );
    }
    return MaterialColor(color.value, swatch as Map<int, Color>);
  }

  /// 颜色检测只保存 #RRGGBB格式 FF透明度
  /// [color] 格式可能是材料风/十六进制/string字符串
  /// 返回[String] #rrggbb 字符串
  static String? color2HEX(Object? color) {
    if (color is Color) {
      // OxFFFFFFFF
      //将十进制转换成为16进制 返回字符串但是没有0x开头
      String temp = color.value.toRadixString(16);
      color = "#" + temp.substring(2, 8);
    }
    return color.toString();
  }
}

源码地址:链接:https://pan.baidu.com/s/1iip8W3jy0YmWqMIaLhih5Q
提取码:xisr

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容