
作者:晚霞的不甘 日期:2025年12月5日 标签:Flutter · OpenHarmony · Design System · UI/UX · 响应式设计 · 无障碍 · 鸿蒙生态

在 OpenHarmony 的超级终端时代,用户可能在手机、手表、车机、智慧屏上使用你的应用。 若每个设备都采用不同设计语言:
更严重的是,不符合《OpenHarmony 人因设计规范》的应用将无法通过 AppGallery 审核。
本文将指导你从零构建一套跨设备、高一致性、强可维护、无障碍友好的 Flutter + OpenHarmony 设计系统,涵盖色彩、排版、组件、动效、适配五大核心模块,助你实现:
┌──────────────────────────────────────┐
│ Design Tokens │ ← 色彩 / 间距 / 字体 / 圆角
├──────────────────────────────────────┤
│ Foundation │ ← 布局 / 网格 / 响应式规则
├──────────────────────────────────────┤
│ Components │ ← 按钮 / 卡片 / 导航 / 表单
├──────────────────────────────────────┤
│ Patterns │ ← 页面模板 / 交互流程
└──────────────────────────────────────┘✅ 核心原则:
--color-primary)类型 | Light 模式 | Dark 模式 | 用途 |
|---|---|---|---|
Primary | #007AFF | #0A84FF | 主按钮、选中状态 |
Surface | #FFFFFF | #1C1C1E | 背景、卡片底色 |
Text Primary | #1D1D1F | #FFFFFF | 标题文字 |
Error | #FF3B30 | #FF453A | 错误提示 |
🎨 实现方式(Flutter):
// lib/theme/colors.dart
class OhColors {
static const primary = Color(0xFF007AFF);
static const surface = Color(0xFFFFFFFF);
static const textPrimary = Color(0xFF1D1D1F);
}Token | 值 (dp) | 用途 |
|---|---|---|
--space-xs | 4 | 图标内边距 |
--space-s | 8 | 元素间隔 |
--space-m | 16 | 卡片内边距 |
--radius-s | 8 | 按钮圆角 |
--radius-l | 24 | 大卡片圆角 |
// lib/theme/spacing.dart
class OhSpacing {
static const xs = 4.0;
static const s = 8.0;
static const m = 16.0;
}层级 | 字号 (sp) | 字重 | 用途 |
|---|---|---|---|
Display Large | 34 | Bold | 首页标题 |
Headline Medium | 24 | SemiBold | 页面标题 |
Body Large | 17 | Regular | 正文 |
Label Small | 11 | Medium | 按钮标签 |
💡 注意:手表端字体 ≥ 18sp,车机 ≥ 20sp。
设备类型 | 屏幕尺寸 | 布局策略 |
|---|---|---|
Wearable | < 2" | 单列、极简、大触控区 |
Phone | 4–7" | 单列、手势导航 |
Tablet | 8–12" | 双栏、富交互 |
Car | 10–15" | 大按钮、语音主导、防误触 |
TV | > 32" | 焦点导航、遥控器优化 |
Widget build(BuildContext context) {
final device = OhDeviceType.of(context); // 自定义设备类型检测
return Scaffold(
body: device.isTv || device.isCar
? TvOptimizedLayout() // 焦点 + 大字体
: device.isWearable
? WatchCompactLayout() // 单列滚动
: ResponsivePhoneLayout(), // 默认手机布局
);
}// 使用 flutter_staggered_grid_view
StaggeredGrid.count(
crossAxisCount: device.isTv ? 12 : device.isTablet ? 8 : 4,
mainAxisSpacing: OhSpacing.s,
crossAxisSpacing: OhSpacing.s,
children: [...],
)变体 | 样式 | 适用场景 |
|---|---|---|
Primary | 实心主色 | 主要操作(如“提交”) |
Secondary | 白底描边 | 次要操作(如“取消”) |
Text | 无背景 | 内联操作(如“查看更多”) |
// lib/components/oh_button.dart
class OhButton extends StatelessWidget {
final String text;
final VoidCallback onPressed;
final OhButtonVariant variant;
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
Color bgColor = variant == OhButtonVariant.primary
? OhColors.primary
: Colors.transparent;
return Container(
height: 48,
decoration: BoxDecoration(
color: bgColor,
borderRadius: BorderRadius.circular(OhRadius.s),
border: variant == OhButtonVariant.secondary
? Border.all(color: OhColors.outline)
: null,
),
child: Text(text, style: theme.textTheme.labelLarge),
);
}
}elevation: 2padding: EdgeInsets.all(OhSpacing.m)borderRadius: OhRadius.l设备 | 导航形式 |
|---|---|
手机 | 底部 TabBar |
车机 | 左侧垂直菜单 |
智慧屏 | 顶部横幅 + 遥控器焦点 |
Curves.easeOut(自然减速)// 页面切换淡入
PageRouteBuilder(
pageBuilder: (_, __, ___) => NextPage(),
transitionsBuilder: (_, anim, __, child) {
return FadeTransition(opacity: anim, child: child);
},
)
// 按钮点击反馈
InkWell(
onTap: () {},
borderRadius: BorderRadius.circular(OhRadius.s),
child: OhButton(...),
)项目 | 标准 |
|---|---|
颜色对比度 | ≥ 4.5:1(正文) / ≥ 3:1(大字) |
触控区域 | ≥ 48×48 dp |
语义标签 | 所有图标按钮需 Semantics(label: "搜索") |
焦点顺序 | 逻辑清晰(车机/TV 必须支持) |
// 为图标按钮添加语义
Semantics(
label: '打开设置',
child: IconButton(icon: Icon(Icons.settings), onPressed: _openSettings),
)
// 动态调整字体(响应系统设置)
Text(
'Hello',
style: TextStyle(fontSize: MediaQuery.textScaleFactorOf(context) * 17),
)
@oh-design/tokens@1.2.0)✅ 所有颜色来自 OhColors
✅ 所有间距使用 OhSpacing
✅ 手机/平板/车机均有 UI 预览
✅ 无障碍扫描评分 ≥ 90
✅ 深色模式完整适配
✅ 字体缩放至 200% 仍可用
一套好的设计系统,让:
🎨 行动建议:
OhColors 和 OhSpacingOhButton 组件因为一致的体验,是最好的品牌语言。
附录:资源推荐
美,不是装饰,而是功能与情感的和谐统一。