在鸿蒙(HarmonyOS)应用开发中,纯 Flutter 开发虽然能实现跨端复用,但面对鸿蒙系统特有的原生能力(如方舟卡片、服务卡片、系统级弹窗)时,往往需要与鸿蒙原生组件进行混合开发。本文将聚焦鸿蒙 Flutter 混合开发,讲解如何在 Flutter 页面中嵌入鸿蒙原生组件、开发自定义 Flutter 插件,并结合完整代码案例,帮助开发者打通 Flutter 与鸿蒙原生的双向融合通道。

一、鸿蒙 Flutter 混合开发核心模式

鸿蒙 Flutter 混合开发主要有两种核心模式,适用于不同的业务场景:

  1. Flutter 嵌入原生:在鸿蒙原生页面中嵌入 Flutter 模块,适用于已有原生项目需扩展跨端功能的场景。
  2. 原生嵌入 Flutter:在 Flutter 页面中嵌入鸿蒙原生组件(如TextClockArkCard),适用于需要调用鸿蒙独有原生 UI 的场景。
  3. 插件化开发:将鸿蒙原生能力封装为 Flutter 插件,供多个 Flutter 项目复用,是混合开发的最佳实践。

本文重点讲解原生组件嵌入 Flutter自定义插件开发两种场景。

二、案例 1:Flutter 页面中嵌入鸿蒙原生组件

鸿蒙系统提供了大量特色原生组件,例如TextClock(系统时钟)、RoundProgressBar(圆形进度条)等。通过PlatformView,我们可以在 Flutter 页面中直接嵌入这些原生组件,实现 UI 的混合渲染。

前置条件

  1. 已配置鸿蒙 DevEco Studio 和 Flutter 环境,确保两者版本兼容。
  2. 鸿蒙项目已开启ohos_abilityflutter的依赖支持。

步骤 1:鸿蒙原生端实现PlatformView

首先在鸿蒙原生项目中创建一个原生组件,并实现PlatformView接口,供 Flutter 端调用。我们以嵌入鸿蒙原生圆形进度条为例。

1. 创建原生进度条组件

在鸿蒙原生项目的entry > src > main > java > com > example > harmonyflutter > widget目录下,创建NativeProgressView.java

package com.example.harmonyflutter.widget;

import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.RoundProgressBar;
import ohos.agp.utils.Color;
import ohos.app.Context;

public class NativeProgressView extends RoundProgressBar {
    // 进度值
    private int progress = 0;

    public NativeProgressView(Context context) {
        super(context);
        initView();
    }

    public NativeProgressView(Context context, AttrSet attrSet) {
        super(context, attrSet);
        initView();
    }

    private void initView() {
        // 设置进度条样式
        this.setProgressColor(new Color(0xFF007DFF)); // 鸿蒙系统蓝
        this.setMaxValue(100);
        this.setRadius(50); // 圆形半径
        this.setProgressWidth(10); // 进度条宽度
        this.setProgress(progress);
    }

    // 提供设置进度的方法,供Flutter调用
    public void setProgressValue(int progress) {
        this.progress = progress;
        this.setProgress(progress);
    }

    // 提供获取当前进度的方法
    public int getProgressValue() {
        return this.progress;
    }
}
2. 实现PlatformViewPlatformViewFactory

在同一目录下创建ProgressViewFactory.java,实现PlatformViewFactoryPlatformView接口,负责创建和管理原生组件:

package com.example.harmonyflutter.widget;

import ohos.ace.ability.AceInternalAbility;
import ohos.ace.plugin.platformview.PlatformView;
import ohos.ace.plugin.platformview.PlatformViewFactory;
import ohos.app.Context;

import java.util.Map;

public class ProgressViewFactory extends PlatformViewFactory {
    private Context context;

    public ProgressViewFactory(Context context) {
        this.context = context;
    }

    @Override
    public PlatformView create(String viewType, String id, Map<String, Object> params) {
        // 匹配Flutter端传入的viewType
        if ("native_progress_view".equals(viewType)) {
            return new NativeProgressPlatformView(context, id, params);
        }
        return null;
    }

    // 实现PlatformView接口
    static class NativeProgressPlatformView implements PlatformView {
        private NativeProgressView progressView;
        private Context context;

        public NativeProgressPlatformView(Context context, String id, Map<String, Object> params) {
            this.context = context;
            this.progressView = new NativeProgressView(context);
            // 初始化进度(可选,从Flutter传入参数)
            if (params.containsKey("initProgress")) {
                int initProgress = (int) params.get("initProgress");
                progressView.setProgressValue(initProgress);
            }
        }

        @Override
        public Component getComponent() {
            // 返回原生组件
            return progressView;
        }

        @Override
        public void dispose() {
            // 组件销毁时释放资源
            progressView = null;
        }

        // 暴露方法给Flutter调用,更新进度
        public void updateProgress(int progress) {
            if (progressView != null) {
                progressView.setProgressValue(progress);
            }
        }
    }
}
3. 注册PlatformView到 Flutter 引擎

MainAbilitySlice.java中,将自定义PlatformViewFactory注册到 Flutter 引擎:

import com.example.harmonyflutter.widget.ProgressViewFactory;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugin.platform.PlatformViewRegistry;

@Override
public void onStart(Intent intent) {
    super.onStart(intent);
    FlutterEngine flutterEngine = new FlutterEngine(getContext());
    // 注册PlatformView
    PlatformViewRegistry registry = flutterEngine.getPlatformViewsController().getRegistry();
    registry.registerViewFactory("native_progress_view", new ProgressViewFactory(getContext()));
    // 执行Flutter入口
    flutterEngine.getDartExecutor().executeDartEntrypoint(
        DartExecutor.DartEntrypoint.createDefault()
    );
}

步骤 2:Flutter 端嵌入原生组件

在 Flutter 项目中,通过AndroidView(鸿蒙系统兼容 AndroidView API)来加载原生组件,并实现进度的双向通信。

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

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '鸿蒙原生组件嵌入Flutter',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: const NativeComponentPage(),
    );
  }
}

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

  @override
  State<NativeComponentPage> createState() => _NativeComponentPageState();
}

class _NativeComponentPageState extends State<NativeComponentPage> {
  // 进度值
  int _progress = 30;
  // 与原生通信的MethodChannel
  static const MethodChannel _channel = MethodChannel('com.example.progress_view');

  // 更新进度
  void _updateProgress() {
    setState(() {
      _progress = (_progress + 10) % 100;
    });
    // 调用原生方法更新进度
    _channel.invokeMethod('updateProgress', {'progress': _progress});
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('嵌入鸿蒙原生进度条')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            // 嵌入鸿蒙原生进度条组件
            SizedBox(
              width: 100,
              height: 100,
              child: AndroidView(
                // 与原生注册的viewType一致
                viewType: 'native_progress_view',
                // 初始化参数
                creationParams: {'initProgress': _progress},
                creationParamsCodec: const StandardMessageCodec(),
              ),
            ),
            const SizedBox(height: 30),
            Text('当前进度:$_progress%', style: const TextStyle(fontSize: 18)),
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: _updateProgress,
              child: const Text('增加进度'),
            )
          ],
        ),
      ),
    );
  }
}

案例说明

  1. 原生端:通过实现PlatformView接口,将鸿蒙原生RoundProgressBar封装为可被 Flutter 调用的视图,并注册到 Flutter 引擎。
  2. Flutter 端:使用AndroidView加载原生组件,通过MethodChannel实现进度值的双向传递,点击按钮即可同步更新原生进度条的显示。
  3. 核心优势:无需重构 Flutter 页面,直接复用鸿蒙原生 UI 组件,保留系统级交互体验。

三、案例 2:鸿蒙 Flutter 自定义插件开发

对于需要在多个 Flutter 项目中复用的鸿蒙原生能力,最佳方式是封装为Flutter 插件。我们以开发一个鸿蒙系统信息获取插件为例,演示插件的完整开发流程。

步骤 1:创建 Flutter 插件项目

在终端执行以下命令,创建一个 Flutter 插件项目:

flutter create --template=plugin harmony_system_info
cd harmony_system_info

插件项目结构中,android目录替换为鸿蒙原生代码目录(harmony),用于存放鸿蒙原生实现。

步骤 2:鸿蒙原生端实现插件逻辑

harmony > src > main > java > com > example > harmonysysteminfo目录下,创建HarmonySystemInfoPlugin.java,实现插件的核心功能(获取系统版本、设备型号):

package com.example.harmonysysteminfo;

import ohos.system.DeviceInfo;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;
import io.flutter.plugin.common.PluginRegistry.Registrar;

public class HarmonySystemInfoPlugin implements MethodCallHandler {
  private Registrar registrar;

  public HarmonySystemInfoPlugin(Registrar registrar) {
    this.registrar = registrar;
  }

  public static void registerWith(Registrar registrar) {
    final MethodChannel channel = new MethodChannel(registrar.messenger(), "harmony_system_info");
    channel.setMethodCallHandler(new HarmonySystemInfoPlugin(registrar));
  }

  @Override
  public void onMethodCall(MethodCall call, Result result) {
    switch (call.method) {
      case "getSystemVersion":
        // 获取鸿蒙系统版本
        String version = DeviceInfo.getOsFullName();
        result.success(version);
        break;
      case "getDeviceModel":
        // 获取设备型号
        String model = DeviceInfo.getDeviceModel();
        result.success(model);
        break;
      default:
        result.notImplemented();
        break;
    }
  }
}

步骤 3:Flutter 端封装插件 API

lib/harmony_system_info.dart中,封装插件的调用方法,供 Flutter 项目直接使用:、

import 'dart:async';
import 'package:flutter/services.dart';

class HarmonySystemInfo {
  static const MethodChannel _channel = MethodChannel('harmony_system_info');

  /// 获取鸿蒙系统版本号
  static Future<String?> get systemVersion async {
    final String? version = await _channel.invokeMethod('getSystemVersion');
    return version;
  }

  /// 获取设备型号
  static Future<String?> get deviceModel async {
    final String? model = await _channel.invokeMethod('getDeviceModel');
    return model;
  }
}

步骤 4:在 Flutter 项目中使用插件

在 Flutter 项目的pubspec.yaml中添加插件依赖:

dependencies:
  flutter:
    sdk: flutter
  harmony_system_info:
    path: ../harmony_system_info # 插件本地路径

然后在 Flutter 页面中调用插件 API:

import 'package:flutter/material.dart';
import 'package:harmony_system_info/harmony_system_info.dart';

void main() {
  runApp(const MyPluginApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '鸿蒙Flutter插件实战',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: const PluginTestPage(),
    );
  }
}

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

  @override
  State<PluginTestPage> createState() => _PluginTestPageState();
}

class _PluginTestPageState extends State<PluginTestPage> {
  String _systemVersion = '未知';
  String _deviceModel = '未知';

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

  // 调用插件获取系统信息
  Future<void> _getSystemInfo() async {
    try {
      final String? version = await HarmonySystemInfo.systemVersion;
      final String? model = await HarmonySystemInfo.deviceModel;
      setState(() {
        _systemVersion = version ?? '获取失败';
        _deviceModel = model ?? '获取失败';
      });
    } catch (e) {
      setState(() {
        _systemVersion = '获取异常:$e';
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('鸿蒙系统信息插件')),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text('鸿蒙系统版本:$_systemVersion', style: const TextStyle(fontSize: 16)),
            const SizedBox(height: 10),
            Text('设备型号:$_deviceModel', style: const TextStyle(fontSize: 16)),
          ],
        ),
      ),
    );
  }
}

案例说明

  1. 插件封装:将鸿蒙原生的DeviceInfo API 封装为 Flutter 插件,实现了代码的复用性和可维护性。
  2. 调用方式:Flutter 项目通过简单的 API 调用,即可获取鸿蒙系统信息,无需关注原生端的实现细节。
  3. 扩展场景:此插件可进一步扩展,添加获取电池状态、网络信息等鸿蒙原生能力。

四、鸿蒙 Flutter 混合开发注意事项

  1. 版本兼容:确保 DevEco Studio、鸿蒙 SDK、Flutter SDK 的版本兼容,避免出现插件注册失败、视图渲染异常等问题。
  2. 性能优化:嵌入原生组件时,尽量减少视图层级嵌套,避免频繁的跨端通信导致性能损耗。
  3. 权限申请:调用鸿蒙系统级 API(如设备信息、相机)时,需在config.json中声明对应权限。
  4. 调试技巧:使用 DevEco Studio 调试原生代码,使用 Flutter DevTools 调试 Flutter 代码,双端联调提高开发效率。

五、总结

鸿蒙 Flutter 混合开发是打通跨端开发与鸿蒙原生能力的关键技术,通过PlatformView实现原生组件嵌入、通过插件化开发实现能力复用,能够充分发挥 Flutter 的跨端优势和鸿蒙的系统级特性。本文的两个案例覆盖了混合开发的核心场景,开发者可根据实际业务需求,进一步探索方舟卡片、分布式任务等高级功能的混合开发方案。

随着鸿蒙生态的不断完善,Flutter 与鸿蒙的融合将更加深入,未来会有更多的原生能力和开发工具支持混合开发,为开发者带来更高效的开发体验

欢迎大家加入[开源鸿蒙跨平台开发者社区](https://openharmonycrossplatform.csdn.net),一起共建开源鸿蒙跨平台生态。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐