AtomGit Flutter鸿蒙客户端:主题系统
2026/6/17 16:59:56 网站建设 项目流程

设计理念:遵循 Material 3

项目采用 Material 3 的主题体系,核心理念是"从单一种子色生成完整的设计系统"。Material 3 引入了基于 HCT(Hue-Chroma-Tone,色调-色度-明度)的颜色系统,只需提供一个种子色,框架会自动生成 Primary、Secondary、Tertiary、Error、Surface 五个颜色组,每个组包含多个明度变体。

AppTheme 类

import'package:flutter/material.dart';classAppTheme{staticconst_seedColor=Color(0xFF1A73E8);// Google BluestaticThemeDatagetlight=>_buildTheme(Brightness.light);staticThemeDatagetdark=>_buildTheme(Brightness.dark);staticThemeData_buildTheme(Brightnessbrightness){finalcolorScheme=ColorScheme.fromSeed(seedColor:_seedColor,brightness:brightness,);returnThemeData(colorScheme:colorScheme,useMaterial3:true,// 组件主题覆盖appBarTheme:constAppBarTheme(centerTitle:false,elevation:0,scrolledUnderElevation:1,),cardTheme:CardTheme(elevation:1,shape:RoundedRectangleBorder(borderRadius:BorderRadius.circular(12),),clipBehavior:Clip.antiAlias,),inputDecorationTheme:InputDecorationTheme(border:OutlineInputBorder(borderRadius:BorderRadius.circular(12),),contentPadding:constEdgeInsets.symmetric(horizontal:16,vertical:12,),isDense:true,),filledButtonTheme:FilledButtonThemeData(style:FilledButton.styleFrom(shape:RoundedRectangleBorder(borderRadius:BorderRadius.circular(8),),),),);}}

种子色的选择

#1A73E8(Google Blue)被选为种子色。这个颜色在蓝色系中具有良好的饱和度和对比度,Material 3 的ColorScheme.fromSeed会基于它生成完整的调色板。如果后续需要更换品牌色,只需修改这一个常量。

Brightness 枚举的使用

_buildTheme接收Brightness枚举并根据其值生成对应的 ColorScheme。Material 3 的ColorScheme.fromSeed在接收brightness: Brightness.dark时,会自动调整所有颜色的明度——Primary 色会调亮以在深色背景上有足够对比度,Surface 色会变暗。

组件主题覆盖的详细说明

全局的ThemeData是组件样式的最高优先级。通过ThemeData的组件属性覆盖,可以确保应用中所有同类组件保持一致的视觉效果,无需在每个使用点重复设置样式。

AppBar 主题

appBarTheme:constAppBarTheme(centerTitle:false,// 标题左对齐(而非居中)elevation:0,// 无阴影(扁平设计)scrolledUnderElevation:1,// 滚动时显示 1px 阴影),

centerTitle: false是 Material 3 的默认值,左对齐标题适合内容密度高的应用。scrolledUnderElevation: 1在用户滚动内容时在 AppBar 底部添加细微阴影,提供视觉层次——内容滚动到 AppBar 下面时,阴影分离了两者。

Card 主题

cardTheme:CardTheme(elevation:1,// 轻微阴影shape:RoundedRectangleBorder(borderRadius:BorderRadius.circular(12),// 12px 圆角),clipBehavior:Clip.antiAlias,// 抗锯齿裁剪),

elevation: 1创建非常轻微的阴影——刚好能让 Card 从背景中浮出来,但不会有 Material 2 那种厚重的投影感。12px 的圆角属于 Material 3 的"中等圆角"风格。

clipBehavior: Clip.antiAlias确保 Card 的子 Widget 被裁剪到圆角边界内。没有这个设置,子 Widget(如 InkWell 的水波纹)可能超出圆角范围,产生视觉瑕疵。

InputDecoration 主题

inputDecorationTheme:InputDecorationTheme(border:OutlineInputBorder(borderRadius:BorderRadius.circular(12),),contentPadding:constEdgeInsets.symmetric(horizontal:16,vertical:12,),isDense:true,),

OutlineInputBorder是 Material 3 推荐的输入框样式(对比 Material 2 的 UnderlineInputBorder 线条样式)。12px 圆角与 Card 保持一致。isDense: true减小输入框的默认高度,适合移动端紧凑布局。

FilledButton 主题

filledButtonTheme:FilledButtonThemeData(style:FilledButton.styleFrom(shape:RoundedRectangleBorder(borderRadius:BorderRadius.circular(8),),),),

FilledButton是 Material 3 的主要按钮样式,使用 Primary 色填充。8px 圆角(比 Card 的 12px 略小)是 Material 3 按钮的推荐圆角值。

在 MaterialApp 中使用主题

classAtomGitAppextendsStatelessWidget{@overrideWidgetbuild(BuildContextcontext){returnMultiProvider(providers:[...],child:MaterialApp(title:'AtomGit',theme:AppTheme.light,darkTheme:AppTheme.dark,themeMode:ThemeMode.system,// 跟随系统设置home:constMainShell(),onGenerateRoute:AtomGitApp.generateRoute,),);}}

themeMode: ThemeMode.system让主题自动跟随系统:

  • 系统浅色模式 → AppTheme.light
  • 系统深色模式 → AppTheme.dark

Flutter 在系统主题切换时会自动重建 MaterialApp,无需应用代码手动处理。这是在ThemeData级别实现的——两个ThemeData对象中的颜色值不同,MaterialApp 自动选择。

组件中消费主题

所有组件通过Theme.of(context)获取当前主题,而不是硬编码颜色值:

// 获取主题色finalprimaryColor=Theme.of(context).colorScheme.primary;// 获取错误色finalerrorColor=Theme.of(context).colorScheme.error;// 获取 Surface 容器色(代码块背景、头像占位)finalcontainerColor=Theme.of(context).colorScheme.surfaceContainerHighest;// 获取文字样式finaltitleStyle=Theme.of(context).textTheme.titleMedium;finalbodyStyle=Theme.of(context).textTheme.bodySmall;

为什么不能硬编码颜色

// 不好:切换深色模式后文字不可见Text('Hello',style:TextStyle(color:Colors.black));// 好:自动适配深色/浅色Text('Hello',style:TextStyle(color:Theme.of(context).colorScheme.onSurface));

硬编码颜色只在一个主题下是正确的。Theme.of(context)获取的颜色是当前有效主题的值,自动适配系统切换。

ColorScheme 的关键令牌

Material 3 的ColorScheme提供了丰富的颜色令牌。项目中使用频率最高的:

令牌浅色模式示例深色模式示例用途
primary亮蓝淡蓝主题色,用于按钮、选中态、Tab 指示器
onPrimary白色深蓝黑Primary 色上的文字色
primaryContainer淡蓝深蓝展示 primary 的背景容器
onPrimaryContainer深蓝淡蓝容器上的文字色
secondary蓝灰浅蓝灰次要元素(FilterChip)
error红色浅红错误状态图标和文字
surface近白深灰页面背景
surfaceContainerHighest浅灰深灰最突出的表面容器(代码块背景)
onSurface深灰(近黑)近白主要文字色
onSurfaceVariant中灰浅灰次要文字色

Material 3 ColorScheme.fromSeed 的原理

ColorScheme.fromSeed使用 Material 3 的 HCT 色彩系统:

  1. 输入:一个种子色(#1A73E8)和一个亮度模式
  2. 色调(Hue)分析:从种子色提取色调值
  3. 色度(Chroma)分配:为 Primary 分配最高色度(鲜艳),Secondary 降低色度(柔和),Tertiary 取互补色调
  4. 明度(Tone)调整:根据 brightness 参数为每个颜色生成不同明度的变体
  5. 对比度保证OnXxx颜色自动选择与对应Xxx色具有足够对比度的明度值(满足 WCAG 无障碍标准)

深色模式的颜色适配

Material 3 的深色模式不是简单地反转颜色,而是精心调整色调和明度:

  • Primary 变淡:在深色背景上,primary变得更亮以确保对比度
  • Surface 变暗:背景色从浅灰变为深灰,减少屏幕发光量
  • Container 颜色反转:浅色模式下 Container 是主色的淡化版(白底上带淡色),深色模式下 Container 是主色的加深版(黑底上带暗色)
  • OnSurface 变亮:文字色从深色变为浅色,保证在暗背景上的可读性

项目中不需要为深色模式写任何额外的颜色代码——所有颜色通过ColorScheme引用,自动获得适配。

API 常量和应用配置

classApiConstants{staticconstStringbaseUrl='https://api.atomgit.com/api/v5';staticconstStringauthorizeUrl='https://api.atomgit.com/login/oauth/authorize';staticconstStringtokenUrl='https://api.atomgit.com/login/oauth/access_token';staticconstStringredirectUri='atomgit://oauth/callback';staticconstStringapiVersion='2023-02-21';staticconstStringscope='repo user';staticconstint pageSize=30;staticconstint rateLimitAuthenticated=5000;staticconstint rateLimitUnauthenticated=60;}

API 相关常量集中在一个类中。这遵循"单一数据源"原则——如果 API 地址变更,只需修改一处。

主题扩展

未来可以为应用添加应用内主题切换(不依赖系统设置)。实现思路:

enumAppThemeMode{light,dark,system}classThemeProviderextendsChangeNotifier{AppThemeMode_mode=AppThemeMode.system;ThemeModegetthemeMode=>switch(_mode){AppThemeMode.light=>ThemeMode.light,AppThemeMode.dark=>ThemeMode.dark,AppThemeMode.system=>ThemeMode.system,};voidsetMode(AppThemeModemode){_mode=mode;notifyListeners();}}

然后在MaterialApp中:

MaterialApp(themeMode:context.watch<ThemeProvider>().themeMode,// ...)

使用主题的最佳实践

几个贯穿整个项目的主题使用原则:

  1. 永远使用Theme.of(context),不要缓存主题引用。系统主题切换时缓存的引用会过时。

  2. 使用语义颜色令牌,而非低级颜色值。colorScheme.error好于Colors.red,因为 error 色在深色模式下会自动调节明度。

  3. 使用textTheme的样式层级,而非自定义 fontSize。textTheme.titleMedium好于fontSize: 16,因为字体大小适配无障碍设置。

  4. Card 和 InputDecoration 的圆角保持一致(12px),FilledButton 略小(8px),形成清晰的视觉层次。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询