
一个App如果颜色乱七八糟的,用户看着就会觉得不专业。所以主题配置这块虽然不涉及什么复杂的业务逻辑,但对整体体验的影响还是挺大的。我们这个垃圾分类App把所有颜色和主题相关的东西都集中放在AppTheme类里,改起来方便,也不容易出现"这个页面用了这个绿,那个页面用了另一个绿"的尴尬情况。
颜色体系的设计
垃圾分类有四大类,每类都得有自己的代表色,不然用户分不清。我们是这么定义的:
dart
/// App主题配置类
/// 集中管理所有颜色、样式相关的配置
class AppTheme {
// 私有构造函数,防止实例化
AppTheme._();
/// 主色调 - Material Design Green 500
/// 选择绿色是因为垃圾分类是环保主题
/// 这个绿色饱和度适中,既醒目又不刺眼
static const Color primaryColor = Color(0xFF4CAF50);
/// 次要色调 - 浅绿色
/// 主要用在渐变效果里,跟主色搭配
static const Color secondaryColor = Color(0xFF81C784);
/// 强调色 - 用于重要操作按钮
static const Color accentColor = Color(0xFF66BB6A);
主色调选的是Material Design里的Green 500,这个绿色不深不浅,看着舒服。secondaryColor是浅一点的绿,主要用在渐变效果里,跟主色搭配。
四种垃圾的颜色:
dart
/// 可回收物颜色 - 蓝色
/// 蓝色给人干净、可循环的感觉
static const Color recyclableColor = Color(0xFF2196F3);
/// 有害垃圾颜色 - 红色
/// 红色本身就有警示的意思
static const Color hazardousColor = Color(0xFFE53935);
/// 厨余垃圾颜色 - 浅绿色
/// 让人联想到蔬菜水果
static const Color kitchenColor = Color(0xFF8BC34A);
/// 其他垃圾颜色 - 灰色
/// 表示普通、没有特殊属性
static const Color otherColor = Color(0xFF9E9E9E);
为啥这么选:
- 可回收物用蓝色,给人干净、可循环的感觉
- 有害垃圾用红色,红色本身就有警示的意思
- 厨余垃圾用浅绿色,让人联想到蔬菜水果
- 其他垃圾用灰色,表示普通、没有特殊属性
这套配色跟现实中垃圾桶的颜色基本一致,用户不用刻意去记。这种设计叫做"现实映射",利用用户已有的认知来降低学习成本。
文字颜色定义
dart
/// 主要文字颜色
static const Color textPrimary = Color(0xFF212121);
/// 次要文字颜色
static const Color textSecondary = Color(0xFF757575);
/// 提示文字颜色
static const Color textHint = Color(0xFFBDBDBD);
/// 链接文字颜色
static const Color textLink = Color(0xFF1976D2);
文字颜色也要统一管理,不同重要程度的文字用不同深浅的颜色。
亮色主题配置
亮色主题是App的默认主题,大部分用户都会用这个:
dart
/// 亮色主题
static ThemeData lightTheme = ThemeData(
// 启用Material 3
useMaterial3: true,
// 亮度模式
brightness: Brightness.light,
// 主色调
primaryColor: primaryColor,
useMaterial3: true开启了Material 3的新特性,组件的形状会更圆润一些,整体风格更现代。Material 3是Google最新的设计语言,提供了更丰富的颜色系统和更灵活的组件样式。
颜色方案的配置:
dart
// 颜色方案
colorScheme: const ColorScheme.light(
primary: primaryColor,
secondary: secondaryColor,
surface: Colors.white,
background: Color(0xFFF5F5F5),
error: Color(0xFFB00020),
onPrimary: Colors.white,
onSecondary: Colors.white,
onSurface: textPrimary,
onBackground: textPrimary,
onError: Colors.white,
),
// 页面背景色
scaffoldBackgroundColor: const Color(0xFFF5F5F5),
背景色的选择 :页面背景用的是浅灰色
0xFFF5F5F5,而不是纯白。这样做的好处是,白色的卡片放上去会有层次感,不会跟背景糊在一起。
ColorScheme是Material Design的颜色系统,定义了一组语义化的颜色:
primary:主色,用于主要UI元素secondary:次要色,用于次要UI元素surface:表面色,用于卡片、对话框等background:背景色error:错误色onXxx:在对应颜色上的文字/图标颜色
AppBar的统一样式
导航栏的样式需要全局统一:
dart
// AppBar主题
appBarTheme: const AppBarTheme(
// 背景色
backgroundColor: primaryColor,
// 前景色(标题和图标)
foregroundColor: Colors.white,
// 去掉阴影
elevation: 0,
// 标题居中
centerTitle: true,
// 标题文字样式
titleTextStyle: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.w600,
),
// 图标主题
iconTheme: IconThemeData(
color: Colors.white,
size: 24,
),
),
几个关键点:
- elevation: 0 去掉阴影,让界面更扁平
- centerTitle: true 标题居中,这是移动端比较常见的做法
- foregroundColor 控制标题和图标的颜色,设成白色跟绿色背景搭配
卡片主题
卡片在App里用得很多,统一配置可以省不少重复代码:
dart
// 卡片主题
cardTheme: CardTheme(
// 阴影高度
elevation: 2,
// 形状
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
// 背景色
color: Colors.white,
// 外边距
margin: EdgeInsets.zero,
),
12像素的圆角不大不小,看着比较柔和。阴影高度设成2,有一点立体感但不会太夸张。
输入框主题
搜索框、表单输入框这些,样式也得统一:
dart
// 输入框主题
inputDecorationTheme: InputDecorationTheme(
// 启用填充
filled: true,
// 填充颜色
fillColor: Colors.white,
// 边框样式
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide.none,
),
// 启用边框
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide.none,
),
// 聚焦边框
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(color: primaryColor, width: 2),
),
// 错误边框
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(color: Colors.red, width: 1),
),
// 内边距
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
// 提示文字样式
hintStyle: const TextStyle(color: textHint),
),
设计思路 :
filled: true加上白色背景,让输入框有个明确的区域。BorderSide.none去掉边框线,整体看起来更简洁。聚焦时显示主题色边框,让用户知道当前在哪个输入框。
按钮主题
dart
// 凸起按钮主题
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
backgroundColor: primaryColor,
foregroundColor: Colors.white,
elevation: 2,
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
textStyle: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
),
// 文字按钮主题
textButtonTheme: TextButtonThemeData(
style: TextButton.styleFrom(
foregroundColor: primaryColor,
textStyle: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
),
),
),
// 轮廓按钮主题
outlinedButtonTheme: OutlinedButtonThemeData(
style: OutlinedButton.styleFrom(
foregroundColor: primaryColor,
side: const BorderSide(color: primaryColor),
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
),
暗色主题配置
现在很多用户喜欢用深色模式,特别是晚上看手机的时候。我们也得支持:
dart
/// 暗色主题
static ThemeData darkTheme = ThemeData(
useMaterial3: true,
brightness: Brightness.dark,
primaryColor: primaryColor,
colorScheme: const ColorScheme.dark(
primary: primaryColor,
secondary: secondaryColor,
surface: Color(0xFF1E1E1E),
background: Color(0xFF121212),
error: Color(0xFFCF6679),
onPrimary: Colors.white,
onSecondary: Colors.white,
onSurface: Colors.white,
onBackground: Colors.white,
onError: Colors.black,
),
scaffoldBackgroundColor: const Color(0xFF121212),
背景色用的是0xFF121212,这是Material Design推荐的暗色主题背景色。没有用纯黑0xFF000000,因为纯黑对比度太高,看久了眼睛会累。
暗色模式下的AppBar:
dart
appBarTheme: const AppBarTheme(
backgroundColor: Color(0xFF1E1E1E),
foregroundColor: Colors.white,
elevation: 0,
centerTitle: true,
),
AppBar背景用稍浅一点的灰色0xFF1E1E1E,跟页面背景形成微妙的层次区分。
卡片在暗色模式下:
dart
cardTheme: CardTheme(
color: const Color(0xFF2C2C2C),
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
卡片背景用更浅的灰色,确保上面的文字能看清楚。
主题的应用
在main.dart里把主题配置应用上:
dart
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await GetStorage.init();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return GetMaterialApp(
title: '垃圾分类指南',
debugShowCheckedModeBanner: false,
// 亮色主题
theme: AppTheme.lightTheme,
// 暗色主题
darkTheme: AppTheme.darkTheme,
// 主题模式:跟随系统
themeMode: ThemeMode.system,
// 初始路由
initialRoute: Routes.main,
// 路由配置
getPages: AppPages.routes,
);
}
}
themeMode: ThemeMode.system的意思是跟随系统设置。用户手机开了深色模式,App就自动切换成暗色主题。
当然用户也可以在App里手动切换,调用Get.changeThemeMode()就行:
dart
void toggleTheme() {
final isDark = Get.isDarkMode;
Get.changeThemeMode(isDark ? ThemeMode.light : ThemeMode.dark);
// 保存用户偏好
GetStorage().write('themeMode', isDark ? 'light' : 'dark');
}
实际开发中的好处
把主题配置集中管理有几个明显的好处:
1. 改颜色方便
产品说"这个绿色太深了,换个浅一点的",你只需要改AppTheme里的一个值,整个App的颜色就都变了。
2. 保持一致性
所有页面都从AppTheme取颜色,不会出现"这个页面的按钮是这个蓝,那个页面是另一个蓝"的问题。
3. 支持主题切换
亮色暗色主题切换是现在App的标配了,提前把架子搭好,后面加功能就很顺。
4. 便于团队协作
设计师给的颜色值统一放在一个地方,开发人员不用到处找。
颜色工具方法
可以添加一些工具方法来方便使用:
dart
/// 根据垃圾类型获取颜色
static Color getColorByType(GarbageType type) {
switch (type) {
case GarbageType.recyclable:
return recyclableColor;
case GarbageType.hazardous:
return hazardousColor;
case GarbageType.kitchen:
return kitchenColor;
case GarbageType.other:
return otherColor;
}
}
/// 获取颜色的浅色版本
static Color getLightColor(Color color, [double opacity = 0.1]) {
return color.withOpacity(opacity);
}
/// 判断颜色是否是深色
static bool isDarkColor(Color color) {
return color.computeLuminance() < 0.5;
}
/// 获取适合在指定背景色上显示的文字颜色
static Color getTextColorOn(Color backgroundColor) {
return isDarkColor(backgroundColor) ? Colors.white : Colors.black;
}
主题配置看起来是小事,但做好了能让整个App的品质感上一个台阶。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net