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. 推荐目录结构

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 侧封装方法调用:

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. 返回值允许为空时要给兜底文案。

鸿蒙端处理方法:

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。

@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 包装:

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 注册

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

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 页面同时使用方法和原生视图

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});

  
  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 ?? '原生调用失败');
    }
  }

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

  
  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. 构建和验证

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 注册分开,后续调试会更清晰,也更适合扩展真实业务组件。

Logo

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

更多推荐