Flutter 嵌入鸿蒙原生 Swiper:PlatformView 从注册到页面显示完整实践

Flutter 页面里可以自己写轮播,也可以嵌入鸿蒙原生 Swiper。什么时候值得嵌入原生组件?通常是你已经有 ArkTS 组件、需要复用鸿蒙侧 UI 能力,或者希望某个区域完全由原生渲染。这个场景不要用 MethodChannel 硬传截图或数据,而要使用 PlatformView。

在这里插入图片描述

本文以“Flutter 页面嵌入鸿蒙原生 Swiper”为例,讲清楚 Flutter 端 OhosView、鸿蒙端 PlatformViewFactoryPlatformViewWrappedBuilder 的职责边界,并补上参数传递、生命周期和空白页排查。

在这里插入图片描述

在这里插入图片描述

1. PlatformView 的职责边界

PlatformView 不是“调用一个原生方法”,而是让 Flutter 页面留出一个区域,由鸿蒙原生组件负责渲染。

层级 负责内容 不建议做的事
Flutter 页面 布局尺寸、传入参数、页面状态 不直接写 ArkTS UI
OhosView 声明原生视图 viewType 不处理原生业务
PlatformViewFactory 根据 viewType 创建视图 不写复杂 UI
PlatformView 返回原生 WrappedBuilder 不保存无用全局状态
ArkTS Component 真正渲染 Swiper 不处理 Flutter 路由

这个边界清楚后,空白页、参数不生效、重复注册就更容易定位。

2. 推荐项目结构

project/
  lib/
    pages/
      Film/
        index.dart
  ohos/
    entry/
      src/
        main/
          ets/
            entryability/
              EntryAbility.ets
            views/
              NativeSwiperView.ets

代码解释:

  1. Flutter 的 Film/index.dart 负责页面布局。
  2. 鸿蒙的 NativeSwiperView.ets 负责原生 Swiper。
  3. EntryAbility.ets 负责注册 viewType。

3. ArkTS 定义 Swiper 数据和组件

先把原生 Swiper 组件写出来,不要一开始就注册 PlatformView。这样能把 UI 逻辑和注册逻辑分开。

interface SwiperItem {
  id: number;
  title: string;
  color: ResourceColor;
}

@Component
struct NativeSwiperComponent {
  private controller: SwiperController = new SwiperController();
  private data: SwiperItem[] = [
    { id: 1, title: '鸿蒙原生轮播 1', color: '#FF5722' },
    { id: 2, title: '鸿蒙原生轮播 2', color: '#2196F3' },
    { id: 3, title: '鸿蒙原生轮播 3', color: '#4CAF50' }
  ];

  build() {
    Column() {
      Swiper(this.controller) {
        ForEach(this.data, (item: SwiperItem) => {
          Column() {
            Text(item.title)
              .fontSize(24)
              .fontColor(Color.White)
              .fontWeight(FontWeight.Bold)
            Text('ID: ' + item.id.toString())
              .fontSize(16)
              .fontColor(Color.White)
              .margin({ top: 8 })
          }
          .width('100%')
          .height('100%')
          .justifyContent(FlexAlign.Center)
          .backgroundColor(item.color)
        })
      }
      .autoPlay(true)
      .interval(3000)
      .indicator(true)
      .loop(true)
    }
    .width('100%')
    .height('100%')
  }
}

代码解释:

  1. SwiperItem 显式定义字段,避免 ArkTS 对对象字面量类型报错。
  2. SwiperController 放在组件内部,生命周期跟随组件。
  3. ForEach 中给出明确类型,减少类型推断不稳定。
  4. 根容器必须有宽高,否则 Flutter 侧可能看到空白区域。

4. 封装 WrappedBuilder 和 PlatformView

PlatformView 返回的是鸿蒙侧可渲染的 Builder。

import { PlatformView, PlatformViewFactory, StandardMessageCodec } from '@ohos/flutter_ohos';

@Builder
function NativeSwiperBuilder(params: ESObject) {
  NativeSwiperComponent();
}

export class NativeSwiperPlatformView extends PlatformView {
  getView(): WrappedBuilder<[ESObject]> {
    return new WrappedBuilder(NativeSwiperBuilder);
  }

  dispose(): void {
    console.info('[NativeSwiperPlatformView] dispose');
  }
}

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

  create(context: Context, viewId: number, args: Object): PlatformView {
    console.info('[NativeSwiperFactory] create viewId=' + viewId);
    return new NativeSwiperPlatformView();
  }
}

代码解释:

  1. StandardMessageCodec.INSTANCE 用于接收 Flutter 创建视图时传来的参数。
  2. create 每创建一个 Flutter 原生视图区域,就返回一个 PlatformView。
  3. dispose 用来释放资源或记录日志,页面销毁时会用到。
  4. 不同 Flutter 鸿蒙版本接口签名可能略有差异,优先参照当前工程模板。

5. 注册 viewType

Flutter 端通过 viewType 找原生工厂,所以注册名要稳定。

import { FlutterAbility, FlutterEngine } from '@ohos/flutter_ohos';
import { GeneratedPluginRegistrant } from '../plugins/GeneratedPluginRegistrant';
import { NativeSwiperFactory } from '../views/NativeSwiperView';

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

    flutterEngine
      .getPlatformViewRegistry()
      .registerViewFactory('native-swiper-view', new NativeSwiperFactory());
  }
}

代码解释:

  1. native-swiper-view 是 Flutter 端要使用的 viewType。
  2. 注册动作要在 FlutterEngine 初始化后执行。
  3. 修改注册代码后,需要完整重启应用。

6. Flutter 端嵌入 OhosView

Flutter 页面要给原生视图明确尺寸。PlatformView 放在无限高度的滚动区域里时,最容易出现空白。

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_ohos/flutter_ohos.dart';

class NativeSwiperPage extends StatelessWidget {
  const NativeSwiperPage({super.key});

  
  Widget build(BuildContext context) {
    return Column(
      children: [
        const Padding(
          padding: EdgeInsets.all(16),
          child: Text('下方区域由鸿蒙原生 Swiper 渲染'),
        ),
        SizedBox(
          height: 220,
          width: double.infinity,
          child: OhosView(
            viewType: 'native-swiper-view',
            creationParams: {
              'source': 'flutter-film-page',
              'autoPlay': true,
            },
            creationParamsCodec: const StandardMessageCodec(),
          ),
        ),
      ],
    );
  }
}

代码解释:

  1. viewType 必须和鸿蒙端注册名一致。
  2. SizedBox 给原生视图确定宽高,避免布局阶段没有尺寸。
  3. creationParams 可以把页面参数传给原生工厂。
  4. creationParamsCodec 要和原生工厂的 codec 对应。

7. 接收 Flutter 传入的参数

如果希望 Flutter 控制标题、自动播放等配置,可以在 create 中读取参数。

interface SwiperOptions {
  source?: string;
  autoPlay?: boolean;
}

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

  create(context: Context, viewId: number, args: Object): PlatformView {
    const options = args as SwiperOptions;
    console.info('[NativeSwiperFactory] source=' + options?.source);
    return new NativeSwiperPlatformView();
  }
}

代码解释:

  1. args 来自 Flutter 的 creationParams
  2. 定义 SwiperOptions 可以让字段更清晰。
  3. 如果参数要影响组件渲染,需要继续传给 Builder 或组件状态。

8. 构建运行与验证

flutter clean
flutter pub get
flutter build hap --debug
flutter run

代码解释:

  1. 改了 ArkTS 视图和注册逻辑后,不要只依赖热重载。
  2. 先构建 HAP,可以提前发现 ArkTS 类型错误。
  3. 运行后查看日志,确认 NativeSwiperFactory create 被打印。

9. 常见问题

现象 原因 处理方式
页面空白 没给 OhosView 高度 使用 SizedBox 或固定高度容器
找不到 viewType 注册名不一致 对齐 native-swiper-view
ArkTS 类型错误 对象字段未定义 使用 interface 显式声明
热重载无效 原生注册未重启 完整停止并重新运行
参数没生效 codec 或键名不一致 对齐 StandardMessageCodec 和字段名

10. 总结

PlatformView 的关键是“注册名一致、尺寸明确、原生视图可独立渲染”。Flutter 端只声明一个原生视图区域,鸿蒙端负责创建和返回真正的 Swiper。只要先把 Swiper 组件写稳,再注册工厂,最后用 OhosView 嵌入,排查会比一边写 UI 一边调注册简单很多。

Logo

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

更多推荐