Flutter for OpenHarmony垃圾分类指南App实战:主题配置实现

一个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

相关推荐
2601_949833393 小时前
flutter_for_openharmony口腔护理app实战+知识实现
android·javascript·flutter
晚霞的不甘3 小时前
Flutter for OpenHarmony从基础到专业:深度解析新版番茄钟的倒计时优化
android·flutter·ui·正则表达式·前端框架·鸿蒙
ujainu3 小时前
无物理引擎实现吸附轨道逻辑 —— Flutter + OpenHarmony 实战指南
flutter·游戏·openharmony
kirk_wang3 小时前
Flutter艺术探索-Flutter地图与定位:google_maps_flutter与geolocator
flutter·移动开发·flutter教程·移动开发教程
鸟儿不吃草3 小时前
android的Retrofit请求https://192.168.43.73:8080/报错:Handshake failed
android·retrofit
Minilinux20183 小时前
Android音频系列(09)-AudioPolicyManager代码解析
android·音视频·apm·audiopolicy·音频策略
mocoding4 小时前
使用专业的 Flutter 天气图标库weather_icons统一风格的图标,提升鸿蒙版天气预报应用专业度
flutter
ujainu4 小时前
Flutter + OpenHarmony 游戏开发进阶:动态关卡生成——随机圆环布局算法
算法·flutter·游戏·openharmony
2603_949462104 小时前
Flutter for OpenHarmony 社团管理App实战 - 资产管理实现
开发语言·javascript·flutter