flutter中如何使用和扩展ThemeData实现详解

2023-12-07 0 466
目录
  • 前言
  • Theme 的基本使用方式
    • 1. Theme 的注册
    • 2. 读取 ThemeData 里的配置:
      • 小技巧介绍
  • ThemeData 内置字段不够用,如何扩展?
    • 如何实现一键换肤
      • 1. 首先在 yaml 新增引入 provider
        • 2. 创建主题枚举
          • 3. ThemeData 进行一层封装处理
            • 4. 创建一个主题管理类 ThemeConfig
              • 5. 通过ThemeData进行读取保持统一
                • 6. 基于 provider 的使用
                  • 7. 创建一个主题仓库,里面存放两套主题,用于演示 Demo
                    • 8.完整的 demo 代码及效果

                    前言

                    做过UI开发的同学都知道,在开发中我们通常会将 文字大小、色值 等内容放在配置文件中,通过统一的管理类来读取(严禁在UI代码中写死)。以便后续调整时不用修改源码,只需要修改配置文件即可。

                    例如这样:

                    • 定义常量存放

                    /// 存放颜色常量
                    abstract class ColorConfigs {
                    static const Color background = Color(0xFFFF6600);
                    static const Color textHint = Color(0xFFA0A4A7);
                    }

                    • 通过统一获取

                    /// GOOD
                    Container(
                    //通过 ColorConfig 获取色值
                    color: ColorConfigs.background,
                    )
                    /// BAD
                    Container(
                    //写死
                    color: Color(0xFFFF6600),
                    )

                    Flutter为我们提供了Theme类,可以让我们节省封装常量配置类(如上示例中的 ColorConfigs)的步骤。将色值、字体风格等配置内容存入ThemeData中,子控件可统一通过 Theme.of(context)读取 color、textStyle、等配置信息。

                    本篇通过换肤demo,介绍在flutter项目中如何使用 theme 以及如何对 themeData 进行字段扩展,实现全局的主题配置管理。

                    Theme 的基本使用方式

                    1. Theme 的注册

                    MaterialApp(
                    theme: myThemeData, //一个ThemeData的实例,下面提供具体代码
                    home: BodyWidget(),
                    )

                    我们做全局的主体配置,在 MaterialApp 中对 theme 字段进行入参赋值。示例代码中的 myThemeData 是一个 ThemeData 的实现实例,可通过 ThemeData 的构造方法来查看其可供保存的主体及样式信息,按照各自所需进行参数赋值。

                    下面是小编在自己项目中用到的ThemeData配置项,定义了各种状态颜色、字体样式、可供参考:

                    myThemeData:

                    val myThemeData = ThemeData(
                    primaryColor: Colors.white,
                    disabledColor: const Color(0xffcbced0),
                    backgroundColor: const Color(0xfff3f4f5),
                    hintColor: const Color(0xffe2e5e7),
                    errorColor: const Color(0xffe21a1a),
                    highlightColor: const Color(0xffa7d500),
                    shadowColor: const Color(0xffa0a4a7),
                    selectedRowColor: const Color(0xfff3f4f5),
                    colorScheme: const ColorScheme.light(
                    primary: Colors.white,
                    secondary: Color(0xffa7d500),
                    background: Color(0xfff3f4f5),
                    error: Color(0xffe21a1a),
                    onPrimary: Color(0xff242524),
                    onError: Colors.white,
                    onBackground: Color(0xffe2e5e7),
                    onSecondary: Color(0xff707275),
                    ),
                    textTheme: TextTheme(
                    headline1: TextStyle(
                    fontSize: 17.sp,
                    fontWeight: FontWeight.bold,
                    color: const Color(0xff242524),
                    ),
                    headline2: TextStyle(
                    fontSize: 16.sp,
                    fontWeight: FontWeight.bold,
                    color: const Color(0xff242524),
                    ),
                    …中间省略 healin3 ~ headline5,只是配置不一样
                    headline6: TextStyle(
                    fontSize: 14.sp,
                    fontWeight: FontWeight.bold,
                    color: const Color(0xff707275),
                    ),
                    subtitle1: TextStyle(
                    fontSize: 12.sp,
                    fontWeight: FontWeight.w500,
                    color: const Color(0xff242524),
                    ),
                    subtitle2: TextStyle(
                    fontSize: 12.sp,
                    fontWeight: FontWeight.w500,
                    color: const Color(0xff707275),
                    ),
                    bodyText1: TextStyle(
                    fontSize: 11.sp,
                    fontWeight: FontWeight.normal,
                    color: const Color(0xff242524),
                    ),
                    bodyText2: TextStyle(
                    fontSize: 11.sp,
                    fontWeight: FontWeight.normal,
                    color: const Color(0xff242524),
                    ),
                    ),
                    )

                    2. 读取 ThemeData 里的配置:

                    @override
                    Widget build(BuildContext context) {
                    return Container(
                    color: Theme.of(context).backgroundColor,
                    child: Text(
                    \’hellow\’,
                    style: Theme.of(context).headline).bodyText1,
                    );
                    }

                    • Theme.of(context).backgroundColor:读取主题配置中的背景颜色,在 myThemeData 中进行过赋值操作
                    • Theme.of(context).headline).bodyText1:读取主题配置中键值为 bodyText1 的字体样式

                    小技巧介绍

                    通常为了便于开发阅读,我们也可以使用extension对 ThemeData 内属性进行重命名获取:

                    新建 extension_theme.dart,文件名字随意:

                    ///用于重命名颜色属性
                    extension ThemeDataColorExtension on ThemeData {
                    Color get bgColor => colorScheme.onBackground;

                    }
                    ///用于重命名字体样式属性
                    extension ThemeDataTextStyleExtension on ThemeData {
                    TextStyle get bodyStyle => textTheme.bodyText1!;

                    }

                    在UI页面进行引用导入使用,上面的 demo 可改为:

                    import ./extension_theme.dart

                    @override
                    Widget build(BuildContext context) {
                    return Container(
                    color: Theme.of(context).bgColor,
                    child: Text(
                    \’hellow\’,
                    style: Theme.of(context).bodyStyle,
                    );
                    }

                    ThemeData 内置字段不够用,如何扩展?

                    从 ThemeData 的构造函数中我们可以看到,ThemeData 内置的字段是有限的。假如我们的UI设计包含的色值数量或者字体样式数量超出了 ThemeData 可供设置数量怎么办呢?

                    比如:我们想新增一个色值配置,名字就叫 connerColor,我们还想保持统一,一律通过 ThemeData 来统一读取统一配置,要如何处理呢?

                    小编在项目里是这么做的,将 ThemeData 进行一层封装,以新增 connerColor 为例,具体代码结合下面👇🏻的一键换肤查询。

                    如何实现一键换肤

                    有了ThemeData作为统一管理存放配置信息后,实现一键换肤的思路就很清晰了,大致是这样的:

                    flutter中如何使用和扩展ThemeData实现详解

                    从上图可以看到,除了需要ThemeData用于存放配置信息,我们还需要封装一个监听类用于监听选中主题发生变更,这个功能我们在下面用provider来实现。

                    1. 首先在 yaml 新增引入 provider

                    dependencies:
                    provider: ^6.0.2

                    2. 创建主题枚举

                    假设我们提供两种主题切换

                    ///主题类型
                    enum ThemeEnum {
                    yellow,
                    red,
                    }

                    3. ThemeData 进行一层封装处理

                    我们对 ThemeData 进行一层封装处理,添加 connerColor 进行颜色字段扩展

                    ///自定义模型,包装一下 themeData
                    class ThemeItem {
                    final ThemeEnum themeEnum;
                    final ThemeData themeData;
                    // 扩展一个字段,用于表示自定义色值
                    final Color connerColor;
                    ThemeItem(
                    this.themeEnum,
                    this.themeData, {
                    required this.connerColor,
                    });
                    }

                    4. 创建一个主题管理类 ThemeConfig

                    abstract class ThemeConfig {
                    ///记录当前选中主题
                    static late ThemeItem _currentTheme;
                    static ThemeData get currentThemeData => _currentTheme.themeData;
                    static ThemeEnum get currentTheme => _currentTheme.themeEnum;
                    ///提供获取扩展的色值
                    static Color? get connerColor => _currentTheme.connerColor;
                    ///设置选中主题,提供外部调用,更换当前主题
                    static void initTheme(ThemeItem theme) {
                    _currentTheme = theme;
                    }
                    }

                    5. 通过ThemeData进行读取保持统一

                    为保持统一通过ThemeData进行读取,使用extension对新增字段connerColor进行读取扩展

                    extension ExTheme on ThemeData {
                    ///扩展获取自定义色值
                    Color get connerColor => ThemeConfig.connerColor!;
                    }

                    6. 基于 provider 的使用

                    我们添加一个工具类,用于通知设置主题变更

                    class AppInfoProvider with ChangeNotifier {
                    ThemeData get currentTheme => ThemeConfig.currentThemeData;
                    ///切换主题
                    setTheme(ThemeItem theme) {
                    ThemeConfig.initTheme(theme);
                    notifyListeners();
                    }
                    }

                    切换主题时,直接调用:

                    Provider.of<AppInfoProvider>(context, listen: false).setTheme(themeItem);

                    7. 创建一个主题仓库,里面存放两套主题,用于演示 Demo

                    ///主题仓库
                    abstract class ThemeStore {
                    static List<ThemeItem> themes = [
                    //红色主题
                    ThemeItem(
                    ThemeEnum.yellow,
                    ThemeData(
                    primaryColor: Colors.yellow,
                    backgroundColor: Colors.yellow,
                    ),
                    connerColor: Colors.blue,
                    ),
                    //黄色主题
                    ThemeItem(
                    ThemeEnum.red,
                    ThemeData(
                    primaryColor: Colors.red,
                    backgroundColor: Colors.red,
                    ),
                    connerColor: Colors.green,
                    ),
                    ];
                    }

                    8.完整的 demo 代码及效果

                    main.dart

                    void main() {
                    ///初始化主题
                    ThemeConfig.initTheme(
                    ThemeStore.themes.first,
                    );
                    runApp(const Material(
                    child: MyApp(),
                    ));
                    }
                    class MyApp extends StatelessWidget {
                    const MyApp({Key? key}) : super(key: key);
                    @override
                    Widget build(BuildContext context) {
                    return MultiProvider(
                    providers: [
                    ChangeNotifierProvider.value(value: AppInfoProvider()),
                    ],
                    child: Consumer<AppInfoProvider>(
                    builder: (context, appInfo, _) {
                    return MaterialApp(
                    theme: appInfo.currentTheme,
                    home: Column(
                    mainAxisSize: MainAxisSize.max,
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: const [
                    BodyWidget(),
                    SizedBox(
                    height: 30,
                    ),
                    _ThemePageButton(),
                    ],
                    ),
                    );
                    },
                    ),
                    );
                    }
                    }
                    class _ThemePageButton extends StatelessWidget {
                    const _ThemePageButton({Key? key}) : super(key: key);
                    @override
                    Widget build(BuildContext context) {
                    return TextButton(
                    onPressed: () {
                    Navigator.push(
                    context,
                    MaterialPageRoute(
                    builder: (context) => const ThemeSetWidget(),
                    ),
                    );
                    },
                    child: const Text(\’打开主题设置页面\’),
                    );
                    }
                    }

                    body_widget

                    class BodyWidget extends StatelessWidget {
                    const BodyWidget({Key? key}) : super(key: key);
                    @override
                    Widget build(BuildContext context) {
                    return Container(
                    height: 300,
                    width: 300,
                    //读取标准色值
                    color: Theme.of(context).backgroundColor,
                    child: Center(
                    child: Container(
                    height: 100,
                    width: 150,
                    //读取自定义色值
                    color: Theme.of(context).connerColor,
                    ),
                    ),
                    );
                    }
                    }

                    两个方块,外层方块读取的 ThemeData 标注字段色值,内层方块读取扩展字段色值。统一通过 ThemeData 读取。

                    theme_set_widget

                    extension ExThemeEnum on ThemeEnum {
                    Color get value {
                    switch (this) {
                    case ThemeEnum.yellow:
                    return Colors.yellow;
                    case ThemeEnum.red:
                    return Colors.red;
                    }
                    }
                    }

                    ///主题选择页面
                    class ThemeSetWidget extends StatelessWidget {
                    const ThemeSetWidget({Key? key}) : super(key: key);
                    @override
                    Widget build(BuildContext context) {
                    return Scaffold(
                    appBar: AppBar(
                    title: const Text(\”颜色主题\”),
                    backgroundColor: Theme.of(context).backgroundColor,
                    ),
                    body: ExpansionTile(
                    leading: const Icon(Icons.color_lens),
                    title: const Text(\’颜色主题\’),
                    initiallyExpanded: true,
                    children: <Widget>[
                    Padding(
                    padding: const EdgeInsets.only(
                    left: 10,
                    right: 10,
                    bottom: 10,
                    ),
                    child: Wrap(
                    spacing: 8,
                    runSpacing: 8,
                    children: ThemeStore.themes
                    .map((e) => _createItemWidget(context, e))
                    .toList(),
                    ),
                    )
                    ],
                    ),
                    );
                    }
                    Widget _createItemWidget(
                    BuildContext context,
                    ThemeItem theme,
                    ) {
                    return InkWell(
                    onTap: () {
                    Provider.of<AppInfoProvider>(context, listen: false).setTheme(theme);
                    },
                    child: Container(
                    width: 40,
                    height: 40,
                    color: theme.themeEnum.value,
                    child: ThemeConfig.currentTheme == theme.themeEnum
                    ? const Icon(
                    Icons.done,
                    color: Colors.white,
                    )
                    : null,
                    ),
                    );
                    }
                    }

                    flutter中如何使用和扩展ThemeData实现详解

                    以上就是flutter中如何使用和扩展ThemeData实现详解的详细内容,更多关于flutter ThemeData扩展的资料请关注悠久资源网其它相关文章!

                    您可能感兴趣的文章:

                    • flutterBloc更新后事件同步与异步详解
                    • 使用PlatformView将 Android 控件view制作成Flutter插件
                    • Flutter状态管理scopedmodel源码解读
                    • Flutter改变状态变量是否必须写在setState回调详解
                    • Flutter 绘制风车实现示例详解
                    • flutterInheritedWidget使用方法总结

                    收藏 (0) 打赏

                    感谢您的支持,我会继续努力的!

                    打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
                    点赞 (0)

                    悠久资源 Dart flutter中如何使用和扩展ThemeData实现详解 https://www.u-9.cn/biancheng/dart/117360.html

                    常见问题

                    相关文章

                    发表评论
                    暂无评论
                    官方客服团队

                    为您解决烦忧 - 24小时在线 专业服务