目录前言Theme的基本使用方式1.Theme的注册2.读取ThemeData里的配置:小技巧介绍ThemeData内置字段不够用,如何扩展?如何实现一键换肤1.首先在yaml新增...
目录
前言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设计包含的色值数量或者字体样式数量超出了 ThemeDjsata 可供设置数量怎么办呢?
比如:我们想新增一个色值配置,名字就叫 connerColor,我们还想保持统一,一律通过 ThemeData 来统一读取统一配置,要如何处理呢?
小编在项目里是这么做的,将 ThemeData 进行一层封装,以新增 connerColor 为例,具体代码结合下面的一键换肤查询。
如何实现一键换肤
有了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扩展的资料请关注我们其它相关文章!










