适合谁看

  • 项目里已经接了多个鸿蒙能力的人

  • 想给团队补统一约定的人

  • 不想让平台层越来越乱的人

问题背景

多能力项目最容易出现的混乱:

混乱

示例

后果

文件命名风格不一致

有的叫 service,有的叫 bridge

找文件困难

通道名没有统一前缀

com.foodvoyage.xxx vs com.xxx.yyy

调试混乱

参数有的传 Map,有的传字符串

不同能力参数格式不同

维护困难

页面层直接和原生细节纠缠

页面知道原生 API 细节

耦合度高

项目中的真实场景

食界探味当前的 4 个能力已经形成了一套模式:

能力

Flutter 文件

ArkTS 文件

channel 名

语音识别

speech_recognition_channel.dart

SpeechRecognitionPlugin.ets

com.foodvoyage.speech_recognition

TTS

text_to_speech_channel.dart

TextToSpeechPlugin.ets

com.foodvoyage.text_to_speech

Intent 导航

intent_navigation_channel.dart

IntentNavigationPlugin.ets

com.foodvoyage.intent_navigation

防窥保护

anti_peep_protection_channel.dart

AntiPeepProtectionPlugin.ets

com.foodvoyage.anti_peep_protection

核心实现

一、目录先按"平台边界层"集中

Flutter 侧:

app/lib/core/platform/
├── speech_recognition_channel.dart
├── text_to_speech_channel.dart
├── intent_navigation_channel.dart
└── anti_peep_protection_channel.dart

鸿蒙侧:

app/ohos/entry/src/main/ets/plugins/
├── SpeechRecognitionPlugin.ets
├── TextToSpeechPlugin.ets
├── IntentNavigationPlugin.ets
└── AntiPeepProtectionPlugin.ets

为什么这样组织:

好处

说明

新同事一眼知道去哪找

平台能力都在 core/platform/

排错时快速缩小范围

先看目录就知道涉及哪些能力

新增能力不用重新发明规则

照着现有文件加一个就行

Flutter 和鸿蒙两边对应关系清楚

文件名一一对应

不要这样做:

❌ 把能力分散到各个页面目录
  features/search/speech_service.dart
  features/ai_assistant/tts_bridge.dart
  features/dish_detail/navigation_helper.dart

✅ 统一收在平台边界层
  core/platform/speech_recognition_channel.dart
  core/platform/text_to_speech_channel.dart

二、命名先按"能力本身"统一

当前项目已经形成了很稳的命名模式:

命名模式

示例

Flutter 侧

xxx_channel.dart

speech_recognition_channel.dart

ArkTS 侧

XxxPlugin.ets

SpeechRecognitionPlugin.ets

channel 名

com.foodvoyage.xxx

com.foodvoyage.speech_recognition

方法名

动词 + 名词

startListeningstopListening

命名规范表格:

规范

说明

示例

Flutter 文件

snake_case_channel.dart

text_to_speech_channel.dart

ArkTS 文件

PascalCasePlugin.ets

TextToSpeechPlugin.ets

channel 名

com.foodvoyage.snake_case

com.foodvoyage.text_to_speech

类名

PascalCaseChannel / PascalCasePlugin

TextToSpeechChannel / TextToSpeechPlugin

方法名

camelCase

startListeningspeakstop

如果命名不统一,会怎样:

❌ 命名混乱:
  speech_service.dart / TtsBridge.ets / com.xxx.tts
  → 找文件困难,channel 名对不上

✅ 命名统一:
  text_to_speech_channel.dart / TextToSpeechPlugin.ets / com.foodvoyage.text_to_speech
  → 一看就知道对应关系

三、通道协议要先统一"什么是命令,什么是事件"

当前项目的 4 个能力分属 3 种通道模型:

通道模型

特点

示例

命令型

发起一次命令,等待一次结果

语音识别、TTS

事件型

开启后持续接收状态事件

防窥保护

混合型

原生主动推送 + Flutter 主动消费

Intent 导航

命令型通道模式:

// Flutter 侧
static Future<String> startListening() async {
  return await _channel.invokeMethod<String>('startListening');
}

static Future<void> stop() async {
  await _channel.invokeMethod<void>('stop');
}
// ArkTS 侧
onMethodCall(call: MethodCall, result: MethodResult): void {
  switch (call.method) {
    case 'startListening': this.handleStart(result); break;
    case 'stopListening': this.handleStop(result); break;
  }
}

事件型通道模式:

// Flutter 侧
static void initialize() {
  _channel.setMethodCallHandler((call) async {
    if (call.method == 'onEvent') {
      final event = call.arguments['event'] as String?;
      state.value = event == 'ACTIVE' ? EventState.active : EventState.idle;
    }
  });
}

static Future<void> activate() async {
  await _channel.invokeMethod<void>('activate');
}
// ArkTS 侧
private onStatusChange(status: string): void {
  this.channel?.invokeMethod('onEvent', { event: status });
}

混合型通道模式:

// Flutter 侧
static void init(GoRouter router) {
  _channel.setMethodCallHandler((call) async {
    if (call.method == 'onIntentNavigation') {
      // 实时推送
    }
  });
  _consumePending();  // 主动消费 pending
}

统一协议不是要求所有能力都只剩一个 invokeMethod,而是要求:

  • 同类能力用相近语义

  • 不同类能力的差异是有意识的

四、参数风格也应该尽量统一

命令型参数:结构化 Map

// 语音识别
await _channel.invokeMethod('startListening', {'language': 'zh-CN'});

// TTS
await _channel.invokeMethod('speak', {'text': '你好'});

// Intent 导航
await _channel.invokeMethod('navigateToPage', {'pageId': 'search', 'dishId': 'xxx'});

事件型参数:固定字段

// 防窥事件
{
  'event': 'HIDE',        // 事件名
  'detail': 'anti-peek'   // 事件详情
}

错误码:统一格式

// 成功
result.success(null);

// 参数错误
result.error('INVALID_ARGUMENT', '参数为空', null);

// 业务错误
result.error('TTS_ERROR', '播报失败', null);

// 权限错误
result.error('PERMISSION_DENIED', '权限被拒绝', null);

参数风格统一的好处:

好处

说明

页面层消费方式一致

不需要为每个能力写不同的解析逻辑

调试时日志格式一致

更容易定位问题

新增能力时有模板

照着现有模式写就行

错误处理可以复用

统一的错误码可以统一处理

关键代码位置

文件

作用

app/lib/core/platform/

Flutter 通道层(统一目录)

app/ohos/entry/src/main/ets/plugins/

鸿蒙插件层(统一目录)

app/ohos/entry/src/main/ets/entryability/EntryAbility.ets

插件注册(统一入口)

统一规则总结

目录统一
  ├─ Flutter:app/lib/core/platform/
  └─ 鸿蒙:app/ohos/entry/src/main/ets/plugins/

命名统一
  ├─ Flutter 文件:xxx_channel.dart
  ├─ ArkTS 文件:XxxPlugin.ets
  └─ channel 名:com.foodvoyage.xxx

通道模型统一
  ├─ 命令型:invokeMethod → 等待返回
  ├─ 事件型:invokeMethod 启动 + onEvent 回推
  └─ 混合型:invokeMethod + pending 消费

参数风格统一
  ├─ 命令型:结构化 Map
  ├─ 事件型:{ event, detail }
  └─ 错误:{ code, message }

常见坑

  • 每加一种能力就重新发明一套命名 — 应该照着现有模式

  • 文件既有 service、又有 bridge、又有 channel — 统一用 channel

  • 通道协议没有统一事件和错误字段 — 事件用 { event, detail },错误用 { code, message }

  • 参数风格完全靠临时习惯决定 — 应该有统一规范

  • Flutter 和 ArkTS 两边各自命名一套 — 文件名应该一一对应

  • 新能力一来就复制旧代码,但没有同步复制命名和字段规范 — 复制时也要复制规范

可复用模板

新增能力命名模板

新增能力:[能力名]

Flutter 文件:app/lib/core/platform/[能力名]_channel.dart
ArkTS 文件:app/ohos/entry/src/main/ets/plugins/[能力名]Plugin.ets
channel 名:com.foodvoyage.[能力名]

类名:
  Flutter:[能力名]Channel
  ArkTS:[能力名]Plugin

通道协议模板

命令型:
  Flutter: static Future<T> method(Map<String, dynamic> args)
  ArkTS: onMethodCall → result.success(result)

事件型:
  Flutter: initialize() + setMethodCallHandler
  ArkTS: systemApi.on → channel.invokeMethod('onEvent', { event, detail })

混合型:
  Flutter: init() + consumePending
  ArkTS: navigateToPage() + setPendingNavigation()

参数风格模板

命令型参数:{ 'key': 'value' }
事件型参数:{ 'event': 'EVENT_NAME', 'detail': 'optional detail' }
错误格式:result.error('ERROR_CODE', '错误描述', null)

新增能力检查清单

目录:
  □ Flutter 文件在 core/platform/ 下?
  □ ArkTS 文件在 plugins/ 下?

命名:
  □ Flutter 文件名是 xxx_channel.dart?
  □ ArkTS 文件名是 XxxPlugin.ets?
  □ channel 名是 com.foodvoyage.xxx?
  □ 类名和文件名对应?

通道模型:
  □ 是命令型、事件型还是混合型?
  □ 同类能力用相近语义?

参数风格:
  □ 命令型传结构化 Map?
  □ 事件型用 { event, detail }?
  □ 错误用 { code, message }?

本篇总结

多能力并存之后,工程约定本身就会变成开发效率问题。目录、命名和协议越早统一,后面继续扩展越不容易乱:

  1. 目录统一 — Flutter 在 core/platform/,鸿蒙在 plugins/

  2. 命名统一xxx_channel.dart / XxxPlugin.ets / com.foodvoyage.xxx

  3. 通道模型统一 — 命令型 / 事件型 / 混合型,同类能力用相近语义

  4. 参数风格统一 — 结构化 Map、固定事件字段、统一错误格式

当前项目已经有一套不错的雏形,后面最重要的是继续保持一致。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐