
引言
随着《"十四五"国家老龄事业发展规划》和《信息无障碍建设指导意见》的推进,适老化与无障碍支持 已从"可选项"变为行业应用强制要求。尤其在政务、医疗、金融、电力等关键领域,应用必须满足:
- 👂 屏幕阅读器兼容(如 TalkBack / VoiceOver);
- 👁️ 高对比度 & 大字体模式;
- ✋ 简化交互路径(减少点击层级);
- 🎨 色彩不依赖(色盲友好)。
然而,Flutter 虽提供 Semantics 组件,但在 OpenHarmony 上无法自动对接系统无障碍服务;而 OpenHarmony 原生支持完善的无障碍框架,却缺乏跨端 UI 能力。
本文将教你如何:
✅ 打通 Flutter 与 OpenHarmony 无障碍服务
✅ 动态响应系统级字体/对比度设置
✅ 构建符合《移动互联网应用适老化通用设计规范》的 UI
✅ 实战开发一个 "老年人健康打卡"应用,支持语音播报、一键呼叫、大按钮操作。
所有方案基于 OpenHarmony API 10(4.1 SDK) + Flutter 3.19,已在华为 MatePad(OpenHarmony 版)实测通过。
一、问题本质:Flutter 在 OpenHarmony 上的无障碍断层
| 能力 | Android/iOS Flutter | OpenHarmony Flutter |
|---|---|---|
| 自动注册无障碍节点 | ✅ 系统桥接 | ❌ 无原生桥接 |
| 监听系统字体缩放 | ✅ MediaQuery.textScaleFactor |
⚠️ 部分失效 |
| 响应高对比度模式 | ✅ 通过平台通道 | ❌ 未暴露 |
| 触发系统语音反馈 | ✅ TalkBack 自动读 |
❌ 无声 |
🔍 根本原因 :社区版
flutter_ohos引擎未实现AccessibilityBridge,导致语义信息无法传递给 OpenHarmony 的 Accessibility Ability。
二、解决方案:双通道融合架构
┌───────────────────────┐
│ Flutter (Dart) │
│ - UI 使用 Semantics │
│ - 监听系统设置变更 │ ← EventChannel
└───────────▲───────────┘
│
┌───────────┴───────────┐
│ OpenHarmony (ArkTS) │
│ - 注册 Accessibility │
│ - 广播系统设置变更 │
│ - 主动触发语音播报 │ ← MethodChannel
└───────────▲───────────┘
│
┌───────────┴───────────┐
│ OpenHarmony 系统服务 │
│ - AccessibilityAbility │
│ - SettingsObserver │
│ - TextToSpeech (TTS) │
└───────────────────────┘
✅ 核心策略:
- Flutter 主动声明语义 → ArkTS 转发为无障碍事件;
- 系统设置变更 → ArkTS 推送至 Flutter 动态调整 UI;
- 关键操作 → ArkTS 调用 TTS 语音反馈。
三、Step 1:监听系统级显示设置(字体/对比度)
ArkTS:观察系统配置变更
typescript
// service/AccessibilityService.ts
import settings from '@ohos.settings';
import common from '@ohos.app.ability.common';
export class AccessibilityService {
private context: common.Context;
private onSettingsChanged: (type: string, value: any) => void;
constructor(context: common.Context, callback: (type: string, value: any) => void) {
this.context = context;
this.onSettingsChanged = callback;
}
startObserving() {
// 监听字体大小
settings.SettingsObserver.on('fontSize', (value) => {
this.onSettingsChanged('font_size', value);
});
// 监听高对比度模式(OpenHarmony 4.1+)
settings.SettingsObserver.on('highContrastText', (enabled) => {
this.onSettingsChanged('high_contrast', enabled);
});
}
}
Flutter:接收并应用设置
dart
// lib/providers/accessibility_provider.dart
final accessibilityProvider = StateProvider<AccessibilitySettings>((ref) {
return AccessibilitySettings();
});
class AccessibilitySettings {
double fontSizeScale = 1.0; // 默认
bool highContrast = false;
}
// 在 main.dart 中监听
void _listenSystemSettings() {
const eventChannel = EventChannel('com.example.accessibility/settings');
eventChannel.receiveBroadcastStream().listen((event) {
final data = event as Map<String, dynamic>;
final type = data['type'] as String;
final value = data['value'];
final settings = context.read(accessibilityProvider.notifier);
if (type == 'font_size') {
// OpenHarmony 字体值:100=标准,120=大,140=超大
settings.state = settings.state.copyWith(
fontSizeScale: (value as int) / 100.0,
);
} else if (type == 'high_contrast') {
settings.state = settings.state.copyWith(
highContrast: value as bool,
);
}
});
}
UI 动态适配
dart
// lib/widgets/adaptive_text.dart
class AdaptiveText extends ConsumerWidget {
final String text;
final double? baseSize;
const AdaptiveText(this.text, {this.baseSize = 16, super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final settings = ref.watch(accessibilityProvider);
final size = baseSize! * settings.fontSizeScale;
final color = settings.highContrast ? Colors.black : Theme.of(context).textTheme.bodyMedium?.color;
return Text(
text,
style: TextStyle(
fontSize: size,
color: color,
fontWeight: settings.highContrast ? FontWeight.bold : null,
),
);
}
}
四、Step 2:打通无障碍语义(关键!)
由于 Flutter 无法自动注册节点,我们采用 手动上报 + 焦点模拟 方案。
ArkTS:注册虚拟无障碍节点
typescript
// EntryAbility.ts
import accessibility from '@ohos.accessibility';
private registerAccessibilityNode(label: string, rect: { x: number, y: number, width: number, height: number }) {
const node = new accessibility.AccessibilityElement();
node.setLabel(label);
node.setBounds(rect.x, rect.y, rect.width, rect.height);
node.setActionHandler((action) => {
if (action === accessibility.Action.CLICK) {
// 通过 EventChannel 通知 Flutter 执行点击
this.sendEventToFlutter('accessibility_click', { label: label });
}
});
accessibility.registerElement(node);
}
Flutter:在关键按钮上报语义
dart
// lib/widgets/large_button.dart
class LargeButton extends ConsumerWidget {
final String label;
final VoidCallback onPressed;
const LargeButton({required this.label, required this.onPressed, super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
return GestureDetector(
onTap: () {
onPressed();
// 主动触发语音反馈
_speak(label);
},
child: Container(
width: 200,
height: 80,
decoration: BoxDecoration(
color: ref.watch(accessibilityProvider).highContrast ? Colors.yellow : Colors.blue,
borderRadius: BorderRadius.circular(16),
),
child: Center(
child: AdaptiveText(label, baseSize: 24),
),
),
);
}
Future<void> _speak(String text) async {
await MethodChannel('com.example.tts/speak').invokeMethod('speak', text);
}
}
💡 技巧 :对"提交""呼叫""返回"等关键操作,主动调用 TTS,弥补屏幕阅读器缺失。
五、Step 3:集成系统 TTS(语音播报)
ArkTS:调用文本转语音
typescript
// service/TtsService.ts
import tts from '@ohos.multimedia.tts';
export class TtsService {
async speak(text: string): Promise<void> {
try {
const engine = tts.createTtsEngine();
await engine.speak({
text: text,
language: 'zh-CN',
speed: 1.0,
volume: 1.0
});
} catch (e) {
console.error('TTS failed:', e);
}
}
}
Flutter 调用
dart
// 在需要语音反馈处
MethodChannel('com.example.tts/speak').invokeMethod('speak', '血压记录成功');
六、适老化 UI 设计规范落地
| 规范要求 | 实现方案 |
|---|---|
| 字体 ≥ 18pt | AdaptiveText(baseSize: 18) + 动态缩放 |
| 按钮 ≥ 48×48dp | LargeButton 最小 80×80 |
| 操作路径 ≤ 3 步 | 首页直接展示"测血压""吃药提醒""一键呼叫" |
| 禁用纯图标按钮 | 所有按钮带文字标签 |
| 高对比度配色 | 黄底黑字 / 白底深蓝字(非红绿色) |
示例:首页布局
dart
class HomePage extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(24),
child: Column(
children: [
AdaptiveText('健康打卡', baseSize: 32),
const SizedBox(height: 40),
LargeButton(label: '记录血压', onPressed: _recordBp),
const SizedBox(height: 20),
LargeButton(label: '吃药提醒', onPressed: _setMedicineAlarm),
const SizedBox(height: 20),
LargeButton(label: '一键呼叫子女', onPressed: _callFamily),
],
),
),
);
}
}
七、测试与验证
1. 开启 OpenHarmony 无障碍服务
- 设置 → 辅助功能 → 屏幕阅读器(开启)
- 设置 → 显示 → 字体大小(调至"超大")
- 设置 → 显示 → 高对比度文本(开启)
2. 验证项
- 所有按钮可被语音朗读
- 字体随系统设置放大
- 高对比度下文字清晰可辨
- 关键操作有语音反馈
3. 自动化检测(未来)
OpenHarmony 计划推出 ohos-audit 工具,可扫描 APK 是否符合无障碍规范。
八、总结:让技术更有温度
通过本文,你已掌握:
✅ 监听系统级无障碍设置
✅ 手动上报语义节点
✅ 集成 TTS 语音反馈
✅ 构建合规适老化 UI
❤️ 无障碍不是功能,而是责任 。在国产操作系统生态建设中,每一个老年用户都不应被数字鸿沟抛弃。
随着 OpenHarmony 官方 Flutter 引擎的完善,未来或将原生支持 Semantics 桥接。但在当下,主动集成是唯一可靠路径。