Flutter OHOS 插件开发实战(Windows 踩坑版):以 `ohos_immersive_light` 为例
2026/6/5 12:20:21 网站建设 项目流程

从 0 到 1 写一个 Flutter OHOS 插件ohos_immersive_light(HDS TabBar 沉浸光感),把一路上踩过的坑一次性总结给你。
本文侧重Windows 平台特有的坑——macOS用户可以略过第三章,但通用部分(PlatformView、Builder 限制、HashMap 解码、AbilityAware)请务必看。


一、为什么写ohos_immersive_light

  • 目标:封装 OpenHarmony UIDesignKit 的HdsTabs,给 Flutter 提供悬浮样式 + 沉浸光感效果的 TabBar。
  • 能力
    • OhosView嵌入原生HdsTabs组件
    • 支持phone/tablet/2in1设备的悬浮样式(API 23+),其他设备自动回退
    • 支持selectedColor/unselectedColor/ 自定义 Tab 列表
    • Tab 切换事件实时回传 Dart 侧
    • 通过 MethodChannel 暴露isFloatingSupported()给业务方做门控

底层机制就是Dart 侧OhosView+ MethodChannel,OHOS 侧FlutterPlugin+PlatformViewFactory+@Builder+ 原生组件。把这一套打通,换个原生组件就能复刻出第二个、第三个插件。


二、通用开发流程(跨平台)

2.1 Dart 层

  1. 定义ImmersiveLightOptions/ImmersiveLightTabItem配置类,配套toMap()把字段转成原生可识别的基本类型(String/bool/num/List/Map)。
  2. 入口类OhosImmersiveLight提供getPlatformVersion()/isFloatingSupported()等方法,走MethodChannel
  3. 视图类ImmersiveLightView extends StatefulWidget,内部用OhosViewviewType: 'immersive_light_view'creationParams: options.toMap(),并在onPlatformViewCreated里给独立 MethodChannel 设setMethodCallHandler,接收onTabChange等原生回调。

2.2 OHOS 层

  1. OhosImmersiveLightPlugin implements FlutterPlugin, MethodCallHandler, AbilityAware
  2. onAttachedToEngine里:注册主MethodChannel+ 注册PlatformViewFactorygetPlatformViewRegistry().registerViewFactory(viewType, factory))。
  3. onAttachedToAbility里:保存AbilityPluginBinding,需要UIAbilityContext时从这里拿(本插件用不到,但写上不亏)。
  4. ImmersiveLightViewFactory extends PlatformViewFactory,构造里super(StandardMessageCodec.INSTANCE)
  5. ImmersiveLightPlatformView extends PlatformViewgetView()返回一个WrappedBuilder,包住@Builder函数。
  6. @Builder函数签名是($$: Params),里面只能写 UI 组件语法,不能let、不能const
  7. @Component内部组件从params.platformView拿到业务对象(强转),把字段赋给原生组件;事件通过查viewChannelMap找到对应 MethodChannel,回调到 Dart。

跨平台通用 Bug 清单(与 Windows 无关,但每写一个新插件都会撞上)见文末「附录:通用 Bug 速查」。


三、Windows 平台专属踩坑(重点

如果你在 Windows 上开发 Flutter OHOS 插件,下面三件事是必看的,照着做能省掉至少 2 天的弯路。

3.1 自带的example工程跑不起来,要自己建一个

现象

  • flutter create生成的example/目录里,ohos/子工程跑不起来。
  • 在 DevEco Studio 里打开example/ohos,hvigor 同步失败、签名缺失、依赖拉不到。
  • 即便能 build,运行时也找不到插件注册(Plugin OhosImmersiveLight not found)。

原因

  • 插件模板里example/ohos只搭了个空壳,没有完整的build-profile.json5/hvigorfile.ts/hvigor/hvigor-config.json5,也没有正确的AppScope/app.json5
  • 它的dependencies指向本插件的path:引用没问题,但entry模块的oh-package.json5没把插件的 har 显式声明,hvigor 不会自动把插件原生代码加进构建。

解决自己建一个工程(参考仓库里的ohos_immersive_light/exam_app/),步骤如下:

# 1. 在插件同级目录新建一个空 Flutter 工程flutter create exam_appcdexam_app
# 2. pubspec.yaml 用 path 引用本地插件dependencies:ohos_immersive_light:path:../
# 3. 拉依赖(注意:不要用 git 引用,见 3.2)flutter pub get# 4. 生成 ohos 工程flutter create--platforms=ohos.# 5. 用 DevEco Studio 打开 exam_app/ohos,配置签名后 Run

要点:

  • exam_app/ohos/hvigorconfig.tsinjectNativeModules即可(见 3.3)。
  • exam_app/ohos/entry/oh-package.json5不需要手写插件依赖,injectNativeModules会自动注入。
  • exam_app/当作「调试宿主」,发布前所有调试都在这里做;插件的example/可以删掉,也可以留作最小 demo,但不能拿来当联调工程。

结论:Windows 上别试图「一键跑通插件 example」,老老实实自己建一个exam_app

3.2 不要用git:引入依赖

现象

  • pubspec.yaml里写:
    dependencies:ohos_immersive_light:git:url:https://gitcode.com/zjx_jason/ohos_immersive_light
  • flutter pub get报:路径含中文 / 含空格 /Unable to resolvegit checkout失败、ohos/目录被识别为 Git 子模块 / 软链接异常。

原因(Windows 特有):

  • Windows 默认core.longpaths没开,路径长度超过 260 字符就 GG;Flutter OHOS 工程路径通常都很深。
  • git:引用会让 pub 把仓库 clone 到%LOCALAPPDATA%\Pub\Cache\git\...,再用软链引回工程;在 Windows 上软链 / Junction 经常被 DevEco Studio / hvigor 拒识。
  • 仓库里如果带ohos/子目录且.gitignore不完善,pub 还会把ohos/当成本地覆盖目录来链接,进一步炸。

解决

  • 开发阶段:统一用path:引用本地插件。
    dependencies:ohos_immersive_light:path:../
  • 发布给用户:在 README 里同时给path:/pub:/git:三种安装方式,但你自己 Windows 联调时只用path:
  • 如果必须用git:,请在仓库根加好.gitignore(至少忽略ohos/build/ohos/.hvigor/ohos/.idea/example/build/),并确保core.longpaths = true

结论:Windows 开发期,pubspec 永远用path:,别用git:

3.3hvigorconfig.tssrcPath必须是相对路径

现象

  • 自己用flutter create --platforms=ohos .生成的工程,build-profile.json5modules[0].srcPath写成了类似E:\\flutter_pub\\exam_app\\ohos\\entry绝对路径
  • 把整个exam_app/拷给同事、或者 CI 上拉下来换盘符后,hvigor 直接报「module not found」「path not exist」。
  • flutter run时偶发hvigor assembleHar failed

原因

  • 早期某些 Flutter OHOS 模板在 Windows 上生成build-profile.json5时会写成绝对路径。
  • DevEco Studio 在某些版本上导入工程后,会主动把srcPath改写成绝对路径(IDE 的小聪明)。

解决

  • 打开exam_app/ohos/build-profile.json5,手动把modules[*].srcPath改成相对路径
    { "modules": [ { "name": "entry", "srcPath": "./entry", // ← 必须是相对路径 "targets": [ { "name": "default", "applyToProducts": ["default"] } ] } ] }
  • 同样地,宿主的ohos/hvigor/hvigor-config.json5不要有dependencies: { "xxx": "E:\\...\\plugin\\ohos" }这种绝对依赖;通过injectNativeModules(__dirname, path.dirname(__dirname))让 hvigor 自动发现插件 har 即可。
// exam_app/ohos/hvigorconfig.tsimportpathfrom'path';import{injectNativeModules}from'flutter-hvigor-plugin';injectNativeModules(__dirname,path.dirname(__dirname));

结论:每次新建 / 拷贝 OHOS 宿主工程后,先检查build-profile.json5srcPath,不是相对路径就改掉。


四、ohos_immersive_light实战要点(HDS TabBar)

4.1 在@Builder里使用原生组件

@BuilderfunctionhdsTabsBuilder(params:Params){ImmersiveLightInternalComponent({platformView:params.platformViewasImmersiveLightPlatformView,controller:(params.platformViewasImmersiveLightPlatformView).controller,});}

这里(params.platformView as ImmersiveLightPlatformView)内联两次是故意的——@Builder里不能let中间变量。

4.2 设备类型判断(悬浮样式门控)

HdsTabs的悬浮样式仅在phone/tablet/2in1上有效,要在原生侧用deviceInfo.deviceType做门控,TV / Watch / Car 一律回退到普通 TabBar。

functionisFloatingSupportedByDevice():boolean{constt:string=deviceInfo.deviceType;returnt==='phone'||t==='tablet'||t==='2in1';}

Dart 侧也可以通过 MethodChannel 问原生「当前设备是否支持悬浮」,避免把门控逻辑写死:

finalsupported=awaitOhosImmersiveLight().isFloatingSupported();if(!supported){// 提示用户当前设备不支持悬浮样式}

4.3 颜色 / 渐变字段

  • Dart 端用int传颜色(如0xFF0A59F7),原生侧按number接收。
  • floatingStyle/barWidth/gradientMask/miniBar这种结构化字段,原生侧用HashMap<string, Object>接收,配合parseFloatingStyle(fsMap)一类工具函数容错解析:
functionparseFloatingStyle(fsMap:HashMap<string,Object>):HdsFloatingStyleConfig{constcfg:HdsFloatingStyleConfig={};constrawBarWidth=fsMap.get('barWidth')asHashMap<string,Object>|undefined;if(rawBarWidth){cfg.barWidth={smallWidth:rawBarWidth.get('smallWidth')asnumber|undefined,mediumWidth:rawBarWidth.get('mediumWidth')asnumber|undefined,largeWidth:rawBarWidth.get('largeWidth')asnumber|undefined,};}// ... 其余字段同理returncfg;}

关键原则:所有 HashMap 取值都要as <期望类型> | undefined,再判空 + 赋值;永远不要try { ... } catch { }一次性吞掉,否则类型被推断成any直接被 ArkTS 拒。

4.4 Tab 切换事件

controller.onChange((index:number)=>{constchannel=viewChannelMap.get(this.viewId);channel?.invokeMethod('onTabChange',index);});

Dart 端在onTabChange回调里setState(() => _currentTabIndex = index)即可联动 UI。

ImmersiveLightView(options:ImmersiveLightOptions(tabItems:[...]),onTabChange:(index)=>setState(()=>_currentTabIndex=index),)

4.5 资源图标:路径 vs PixelMap

HdsTabs的 Tab 图标支持两种形式:

  • 资源路径iconUrl: 'assets/icons/home.png'):Dart 端把资源打进 bundle,原生侧用$r('app.media.xxx')$rawfile(...)加载。
  • PixelMap:通过 MethodChannel 把image.PixelMap传过去,适合动态图标(未读消息数 badge 等)。

本插件默认走iconUrl资源路径,Dart 侧把assets/icons/写到pubspec.yamlflutter.assets即可。


五、发布与维护

5.1 pubspec 必填项

name:ohos_immersive_lightdescription:"OpenHarmony HDS TabBar 插件,支持悬浮样式和沉浸光感效果"version:0.1.1homepage:https://gitcode.com/zjx_jason/ohos_immersive_lightenvironment:sdk:^3.9.2flutter:">=3.3.0"# ← 必加,否则 pub 校验会报 SDK 1.9.x 旧默认dependencies:flutter:sdk:flutterplugin_platform_interface:^2.0.2flutter:plugin:platforms:ohos:pluginClass:OhosImmersiveLightPlugin

5.2 发布命令

# 1. 提交所有修改(pub 校验要求干净状态)gitadd.gitcommit-m"chore: ready to publish 0.1.0"# 2. 发布PUB_HOSTED_URL=https://pub.dev flutter pub publish# 输入 y 确认

Windows 上跑flutter pub publish时,如果工程路径深、有中文、含空格,可能卡在生成 tar 包阶段。把工程挪到短路径、无中文、无空格的盘符根目录附近通常能解决。

5.3 README 与 CHANGELOG

  • README 给「兼容性表格」(Flutter / Dart / SDK / IDE / 设备类型)。
  • 安装方式同时给path:/git:/pub:三种,但自己 Windows 联调时只演示path:
  • 单独维护docs/DEVICE_COMPATIBILITY.md,标明哪些设备型号实测通过、哪些已知问题。
  • CHANGELOG 按 Keep a Changelog 写。

六、调试技巧

  1. 看原生日志hilog过滤tag == 'OhosImmersiveLightPlugin',Dart 侧print配合flutter run控制台。
  2. MethodChannel 入参解码失败:在原生MethodCallHandler.handleMethodCall入口先hilog整条call.args,对 HashMap 字段,别用属性访问,只用get(key)
  3. PlatformView 不显示:99% 是@Builder内写了非 UI 语法,编译期会报;或viewType拼写不一致。
  4. 签名报错build-profile.json5里的signingConfigs.material.certpath等要指向本机.ohos/config/下的文件,换机器后不要提交该路径。
  5. hvigor 缓存:改build-profile.json5/oh-package.json5后,跑一次hvigor clean再 build。
  6. HdsTabs 不渲染悬浮样式:先hilogdeviceInfo.deviceType,确认设备类型在白名单(phone/tablet/2in1)内;不在就回退。

七、小结

Windows 上写 Flutter OHOS 插件,记住三件最重要的事:

  1. 自带的example工程跑不通,自己新建一个exam_app/当宿主
  2. pubspec 用path:引用本地插件,不要用git:
  3. build-profile.json5srcPath必须是相对路径

把上面这套流程跑通,剩下的就是通用 Flutter OHOS 插件开发——PlatformView+MethodChannel+AbilityAware+@Builder+HashMap取值。把这套模板记熟后,换个原生组件就能复刻出第二个、第三个插件。


附录:通用 Bug 速查(跨平台)

#现象原因解决
1Module '"@ohos/flutter_ohos"' has no exported member 'Params'主包未 export Params@ohos/flutter_ohos/src/main/ets/plugin/platform/PlatformView子路径导入
2Only UI component syntax can be written here@Builder 内写了let/const删掉中间变量,全部内联
3Property 'getPlatformViewsController' does not existOHOS 绑定 API 不同改用binding.getPlatformViewRegistry().registerViewFactory(...)
4Expected 1 arguments, but got 0(Factory super)PlatformViewFactory 需要 MessageCodecsuper(StandardMessageCodec.INSTANCE)
5implements PlatformView报缺方法PlatformView 是抽象类extends PlatformView,只实现 getView / dispose
6titleColors 等 private 字段报「Property is private」@Component 私有字段去掉private修饰,或改用 Builder 内联传值
7HashMap 字段取到undefinedargs['key']属性访问args as HashMap<string, Object>; argMap.get('key')
8Use explicit types instead of any, unknownArkTS 严格模式禁 any写强类型as <type> | undefined,try/catch 容错回退
9pub 报 SDK 1.9.x 旧默认没写environment.flutterflutter: ">=3.3.0"
10pub 报「1 checked-in file is modified」未 commitgit add && git commit再 publish

文档版本:与ohos_immersive_light0.1.x 配套;如 API 变更以仓库与 pub.dev 为准。

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

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

立即咨询