Flutter 调用鸿蒙原生组件:MethodChannel 与 PlatformView 的选择和落地

Flutter 调用鸿蒙原生组件:MethodChannel 与 PlatformView 的选择和落地

Flutter 鸿蒙项目里,"调用原生组件"这个说法很容易混淆。有人只是想调一个鸿蒙方法,有人是想把 ArkTS 写好的 Swiper 放进 Flutter 页面,还有人既要显示原生 UI,又要把点击事件传回 Flutter。不同需求对应不同方案,选错方案会让代码越来越难维护。

本文把原稿里的 MethodChannel 和 PlatformView 两条链路拆开讲:简单数据交互用 MethodChannel,原生 UI 嵌入用 PlatformView,原生 UI 事件回传可以在 PlatformView 基础上再配合 MethodChannel。

1. 先按需求选择方案

需求 推荐方式 典型例子
Flutter 调原生方法 MethodChannel 获取设备信息、调用原生计算
Flutter 嵌入原生 UI PlatformView 嵌入 ArkTS Swiper、地图、播放器
原生 UI 通知 Flutter PlatformView + MethodChannel Swiper 点击回传 itemId
高频数据流 EventChannel 或自定义协议 定位、传感器、进度流

不要把所有事情都塞进一个 NativePlugin。方法调用和视图嵌入最好分文件管理。

2. 推荐目录结构

text 复制代码
project/
  lib/
    pages/
      NativeDemo/
        index.dart
    bridge/
      native_bridge.dart
  ohos/
    entry/
      src/
        main/
          ets/
            plugins/
              NativeMethodPlugin.ets
            views/
              NativeSwiperView.ets
            entryability/
              EntryAbility.ets

代码解释:

  1. native_bridge.dart 封装 Flutter 侧通道。
  2. NativeMethodPlugin.ets 处理方法调用。
  3. NativeSwiperView.ets 处理原生 UI。
  4. EntryAbility.ets 只负责注册。

3. MethodChannel 适合简单交互

Flutter 侧封装方法调用:

dart 复制代码
import 'package:flutter/services.dart';

class NativeBridge {
  static const MethodChannel _channel =
      MethodChannel('com.example.native/component');

  Future<String> getNativeTitle() async {
    final title = await _channel.invokeMethod<String>('getTitle');
    return title ?? '鸿蒙原生组件';
  }
}

代码解释:

  1. 通道名要稳定,并和 ArkTS 端一致。
  2. 方法名 getTitle 要在原生端分发。
  3. 返回值允许为空时要给兜底文案。

鸿蒙端处理方法:

typescript 复制代码
import { FlutterPlugin, FlutterPluginBinding } from '@ohos/flutter_ohos';
import { MethodCall, MethodCallHandler, MethodChannel, MethodResult } from '@ohos/flutter_ohos';

export class NativeMethodPlugin implements FlutterPlugin, MethodCallHandler {
  private channel?: MethodChannel;

  onAttachedToEngine(binding: FlutterPluginBinding): void {
    this.channel = new MethodChannel(binding.getBinaryMessenger(), 'com.example.native/component');
    this.channel.setMethodCallHandler(this);
  }

  onDetachedFromEngine(binding: FlutterPluginBinding): void {
    this.channel?.setMethodCallHandler(null);
    this.channel = undefined;
  }

  onMethodCall(call: MethodCall, result: MethodResult): void {
    if (call.method === 'getTitle') {
      result.success('来自鸿蒙原生的标题');
      return;
    }
    result.notImplemented();
  }
}

代码解释:

  1. result.success 返回给 Flutter。
  2. 未识别的方法要调用 notImplemented,不要静默失败。
  3. 插件解绑时清理 handler。

4. PlatformView 适合嵌入原生 UI

如果要在 Flutter 页面里显示鸿蒙原生 Swiper,就使用 PlatformView。

typescript 复制代码
@Component
struct NativeBannerComponent {
  build() {
    Swiper() {
      Text('鸿蒙原生 Banner 1').fontSize(24).fontColor(Color.White)
      Text('鸿蒙原生 Banner 2').fontSize(24).fontColor(Color.White)
      Text('鸿蒙原生 Banner 3').fontSize(24).fontColor(Color.White)
    }
    .autoPlay(true)
    .interval(3000)
    .indicator(true)
    .width('100%')
    .height('100%')
  }
}

代码解释:

  1. ArkTS 组件负责真实 UI。
  2. 根节点要有明确宽高。
  3. 复杂业务数据建议先定义 interface,再进入组件渲染。

PlatformView 包装:

typescript 复制代码
import { PlatformView, PlatformViewFactory, StandardMessageCodec } from '@ohos/flutter_ohos';

@Builder
function NativeBannerBuilder(params: ESObject) {
  NativeBannerComponent();
}

class NativeBannerPlatformView extends PlatformView {
  getView(): WrappedBuilder<[ESObject]> {
    return new WrappedBuilder(NativeBannerBuilder);
  }

  dispose(): void {}
}

export class NativeBannerFactory extends PlatformViewFactory {
  constructor() {
    super(StandardMessageCodec.INSTANCE);
  }

  create(context: Context, viewId: number, args: Object): PlatformView {
    return new NativeBannerPlatformView();
  }
}

代码解释:

  1. NativeBannerBuilder 把 ArkTS 组件交给 PlatformView。
  2. NativeBannerFactory 由 FlutterEngine 注册。
  3. args 可接收 Flutter 传入的初始化参数。

5. 统一在 EntryAbility 注册

注册逻辑集中在入口,便于排查。

typescript 复制代码
import { FlutterAbility, FlutterEngine } from '@ohos/flutter_ohos';
import { GeneratedPluginRegistrant } from '../plugins/GeneratedPluginRegistrant';
import { NativeMethodPlugin } from '../plugins/NativeMethodPlugin';
import { NativeBannerFactory } from '../views/NativeBannerView';

export default class EntryAbility extends FlutterAbility {
  configureFlutterEngine(flutterEngine: FlutterEngine): void {
    super.configureFlutterEngine(flutterEngine);
    GeneratedPluginRegistrant.registerWith(flutterEngine);

    const methodPlugin = new NativeMethodPlugin();
    methodPlugin.onAttachedToEngine(flutterEngine.getPluginBinding());

    flutterEngine
      .getPlatformViewRegistry()
      .registerViewFactory('native-banner-view', new NativeBannerFactory());
  }
}

代码解释:

  1. 自动生成插件注册保留在前面。
  2. MethodChannel 插件和 PlatformView 注册可以共存。
  3. 不同模板的 binding 获取方式可能不同,按当前工程生成 API 调整。
  4. 修改后要完整重启应用。

6. Flutter 页面同时使用方法和原生视图

dart 复制代码
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_ohos/flutter_ohos.dart';
import '../../bridge/native_bridge.dart';

class NativeDemoPage extends StatefulWidget {
  const NativeDemoPage({super.key});

  @override
  State<NativeDemoPage> createState() => _NativeDemoPageState();
}

class _NativeDemoPageState extends State<NativeDemoPage> {
  final NativeBridge _bridge = NativeBridge();
  String _title = '等待原生返回';

  Future<void> _loadTitle() async {
    try {
      final title = await _bridge.getNativeTitle();
      setState(() => _title = title);
    } on PlatformException catch (e) {
      setState(() => _title = e.message ?? '原生调用失败');
    }
  }

  @override
  void initState() {
    super.initState();
    _loadTitle();
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Padding(
          padding: const EdgeInsets.all(16),
          child: Text(_title),
        ),
        const SizedBox(
          height: 220,
          child: OhosView(
            viewType: 'native-banner-view',
            creationParams: {'page': 'native-demo'},
            creationParamsCodec: StandardMessageCodec(),
          ),
        ),
      ],
    );
  }
}

代码解释:

  1. 顶部标题来自 MethodChannel。
  2. 下方 Banner 来自 PlatformView。
  3. OhosView 必须给出明确高度。
  4. viewType 必须和鸿蒙端注册名一致。

7. 构建和验证

bash 复制代码
flutter clean
flutter pub get
flutter build hap --debug
flutter run

代码解释:

  1. 原生代码变更后需要重新构建。
  2. 先确认 HAP 能构建,再看运行效果。
  3. 如果只有 Flutter UI 改动,可以热重载;如果改了注册、ArkTS 组件、插件,建议完整重启。

8. 排查表

问题 排查方向 修复建议
MethodChannel 无响应 通道名、注册、方法名 对齐三处名称并重启
OhosView 空白 尺寸、viewType、注册 给高度,检查注册名
ArkTS 编译失败 类型声明不完整 补 interface,避免隐式对象
参数没传到原生 codec 或字段名 对齐 creationParams 和 args
热重载不生效 改了原生层 停止应用后重新运行

9. 总结

Flutter 调用鸿蒙原生组件时,先选对桥:数据交互用 MethodChannel,原生 UI 嵌入用 PlatformView,事件回传再组合通道。工程上把 Flutter 页面、Dart bridge、ArkTS plugin、ArkTS view 和 EntryAbility 注册分开,后续调试会更清晰,也更适合扩展真实业务组件。

相关推荐
zh_xuan1 小时前
c++ std::Any 用法
开发语言·c++·any
小杍随笔1 小时前
【Rust全栈项目接入UUID主键实战:从选型到PostgreSQL适配(含v7优化)】
开发语言·postgresql·rust
千逐681 小时前
Uniapp 鸿蒙实战之 AtomGit APP - 首页设计与仓库列表实现
华为·uni-app·harmonyos
FriendshipT1 小时前
Ultralytics:解读PSABlock模块
人工智能·pytorch·python·深度学习·目标检测
minxihou1 小时前
TRINITY-Router: 用数据证伪LLM路由假设 - 8模型316题实验报告
人工智能·深度学习·语言模型·智能路由器
fl1768311 小时前
苹果成熟度检测数据集VOC+YOLO格式2039张4类别
深度学习·yolo·机器学习
●VON2 小时前
HarmonyKit | 鸿蒙新特性规范:10 个工具页 UI 一致性设计系统
ui·华为·harmonyos·鸿蒙·新特性
无限的鲜花10 小时前
反射(原创推荐)
java·开发语言
yongche_shi10 小时前
ragas官方文档中文版(五十)
开发语言·python·ai·ragas·如何评估和改进 rag 应用