Flutter 三方库 simple_circular_progress_bar 在 OHOS 平台的适配实践

Flutter 三方库 simple_circular_progress_bar 在 OHOS 平台的适配实践

引言

OpenHarmony(OHOS)生态发展很快,其"一次开发,多端部署"的理念,与 Flutter 的跨平台愿景不谋而合。现在,越来越多的开发者开始尝试将成熟的 Flutter 应用生态迁移到鸿蒙平台,以覆盖更广泛的设备。不过,Flutter 丰富的三方库大多是围绕 iOS 和 Android 构建的,直接搬到 OHOS 平台,经常会遇到原生端实现缺失的问题。

本文将以一个典型的纯 Dart 渲染库------simple_circular_progress_bar(圆形进度条库)为例,分享将其完整适配到 OHOS 平台的实战过程。我们不止步于"能跑通",还会深入聊聊 Flutter 插件在 OHOS 端的工作原理、适配时面临的关键技术选择、性能上需要注意的点,并提供可运行的完整代码。希望能为你后续的跨平台适配工作,提供一套可以参考的思路和方法。

一、适配原理与技术分析

1.1 Flutter 插件架构回顾

Flutter 与原生平台交互,核心靠的是平台通道(Platform Channel),主要有三种:

  • MethodChannel:用于方法调用,传递字符串或一些半结构化的信息。
  • EventChannel:用于数据流通信,支持原生端持续向 Dart 端发送事件。
  • BasicMessageChannel:用于简单的数据传递,使用标准的消息编解码器。

对于 simple_circular_progress_bar 这种纯用 Dart Canvas 绘制的 UI 库,如果它不依赖任何原生功能,理论上可以直接在 OHOS 的 Flutter 引擎上运行,无需额外适配。但为了更贴近实际开发中可能遇到的复杂场景,我们假设它的某个高级功能(比如,依赖原生传感器数据来控制进度)需要调用 OHOS 的系统 API。这样一来,我们就得为它创建 OHOS 端的原生实现。

1.2 OHOS 平台适配层设计

适配的核心,其实就是为这个 Flutter 插件在 OHOS 端造一个"孪生兄弟"。我们需要在 OHOS 侧建立一个原生模块,让它能够:

  1. 接收来自 Dart 端的指令(比如开始动画、更新进度值)。
  2. 调用 OHOS NDK 或 JS UI Kit 提供的原生能力(比如读取系统电源或传感器数据)。
  3. 将处理结果或原生事件回传给 Dart 端。

技术栈怎么选? OHOS 提供了多种原生开发方式。考虑到 Flutter 引擎本身是基于 C/C++ 的,为了获得最好的性能和最无缝的集成体验,我们优先选择使用 Native API (C API) 来开发插件的原生部分。

二、完整适配步骤与代码实现

2.1 环境配置与项目初始化

(这部分在原"准备工作"基础上做了补充)

需要准备的系统和工具

  • 开发机:Ubuntu 22.04 / Windows 11(配 WSL2)/ macOS 13+
  • Flutter SDK:3.19.0+(确保包含对 OHOS 的实验性支持)
  • DevEco Studio:4.0+(用于 OHOS 原生侧的开发和调试)
  • OHOS SDK:API 11+

环境搭建步骤

bash 复制代码
# 1. 配置 Flutter for OpenHarmony 环境
flutter channel master # 使用 master 分支以获取最新的 OHOS 支持
flutter upgrade
flutter doctor --android-licenses

# 检查 Flutter 对 OHOS 的支持情况
flutter doctor -v
# 如果配置正确,应该能看到 OpenHarmony 设备相关的工具链信息。

# 2. 创建测试项目并引入待适配的库
flutter create --platforms=ohos,android ohos_circular_progress_demo
cd ohos_circular_progress_demo

# 添加原版的 simple_circular_progress_bar 库
flutter pub add simple_circular_progress_bar

2.2 创建 OHOS 平台插件包

因为原库没有 OHOS 实现,我们需要自己创建一个 flutter_ohos_plugin

第一步:创建插件项目结构

bash 复制代码
# 在项目根目录下创建 OHOS 原生插件模块
mkdir -p ohos/simple_circular_progress_bar
cd ohos/simple_circular_progress_bar
ohos create -t native_library simple_circular_progress_bar_impl

这个命令会生成一个标准的 OHOS Native C++ 库项目结构,里面包含 cpp 目录、CMakeLists.txtindex.d.ts 声明文件。

第二步:实现 Dart 端平台接口 (lib/src/ohos_adapter.dart)

我们首先在 Dart 层定义一个接口,把需要 OHOS 平台实现的功能抽象出来。这里我们假设需要从 OHOS 系统获取"电池健康状态"来影响进度条动画。

dart 复制代码
// lib/src/ohos_adapter.dart
import 'dart:async';
import 'package:flutter/services.dart';

/// 定义需要 OHOS 平台实现的特定功能
class OhosProgressController {
  static const MethodChannel _channel = MethodChannel(
      'com.example/simple_circular_progress_bar_ohos');

  /// 获取 OHOS 系统电池健康度(模拟一个需要原生能力的场景)
  /// 返回一个 0.0 到 1.0 之间的值,1.0 表示电池完全健康。
  static Future<double> getBatteryHealthFactor() async {
    try {
      final double factor = await _channel.invokeMethod('getBatteryHealth');
      return factor.clamp(0.0, 1.0);
    } on PlatformException catch (e) {
      print('获取电池健康度失败: ${e.message}');
      // 降级策略:返回默认值 1.0,确保核心的进度条功能不受影响
      return 1.0;
    }
  }

  /// 通知 OHOS 端进度条动画的生命周期事件
  static Future<void> notifyAnimationState(bool isAnimating) async {
    try {
      await _channel.invokeMethod(
        'onAnimationStateChanged',
        {'isAnimating': isAnimating},
      );
    } on PlatformException catch (e) {
      print('通知动画状态失败: ${e.message}');
      // 这个错误可以忽略,不影响主流程
    }
  }
}

第三步:实现 OHOS Native C++ 层 (ohos/simple_circular_progress_bar/cpp/plugin_impl.cpp)

这是整个适配最核心的一步,我们需要实现 Dart 端通过 MethodChannel 调用的原生逻辑。

cpp 复制代码
#include "napi/native_api.h"
#include "hilog/log.h"
#include <string>
#include <map>

#undef LOG_DOMAIN
#undef LOG_TAG
#define LOG_DOMAIN 0xFF00
#define LOG_TAG "ProgressBarPlugin"

// 模拟获取电池健康度,真实场景中应调用 OHOS 电池服务 API
static double SimulateGetBatteryHealth() {
    // TODO: 替换为实际的 OHOS 系统调用,例如通过 `BatteryInfo` 接口
    // 这里先返回一个模拟值
    return 0.85; // 表示 85% 健康度
}

// 处理 Dart 端调用 `getBatteryHealth` 方法
static napi_value OHOS_GetBatteryHealth(napi_env env, napi_callback_info info) {
    napi_value result = nullptr;
    double healthFactor = SimulateGetBatteryHealth();

    // 将 double 值包装成 napi_value 返回给 Dart
    napi_create_double(env, healthFactor, &result);
    HiLog::Info(LABEL, "OHOS_GetBatteryHealth 被调用,返回值: %{public}f", healthFactor);
    return result;
}

// 处理 Dart 端调用 `onAnimationStateChanged` 方法
static napi_value OHOS_OnAnimationStateChanged(napi_env env, napi_callback_info info) {
    size_t argc = 1;
    napi_value argv[1] = {nullptr};
    napi_value thisArg = nullptr;
    void* data = nullptr;

    // 解析从 JavaScript 端传入的参数
    napi_get_cb_info(env, info, &argc, argv, &thisArg, &data);
    if (argc < 1) {
        napi_throw_error(env, nullptr, "参数无效");
        return nullptr;
    }

    bool isAnimating = false;
    napi_get_value_bool(env, argv[0], &isAnimating);
    HiLog::Info(LABEL, "动画状态变为: %{public}s", isAnimating ? "进行中" : "已停止");

    // 这里可以触发 OHOS 端的其他操作,例如根据动画状态调整后台任务优先级
    // TODO: 调用 OHOS 后台任务管理 API

    napi_value undefined;
    napi_get_undefined(env, &undefined);
    return undefined;
}

// 模块初始化函数,在这里注册 MethodChannel 对应的方法
static napi_value Init(napi_env env, napi_value exports) {
    napi_property_descriptor desc[] = {
        {"getBatteryHealth", nullptr, OHOS_GetBatteryHealth, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"onAnimationStateChanged", nullptr, OHOS_OnAnimationStateChanged, nullptr, nullptr, nullptr, napi_default, nullptr}
    };
    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
    HiLog::Info(LABEL, "simple_circular_progress_bar OHOS 插件初始化完成。");
    return exports;
}

// 定义模块
NAPI_MODULE(simple_circular_progress_bar_ohos, Init)

第四步:配置 Native 模块的编译与依赖 (ohos/simple_circular_progress_bar/CMakeLists.txt)

cmake 复制代码
cmake_minimum_required(VERSION 3.4.1)
project(simple_circular_progress_bar_ohos)

set(NATIVE_LIB_NAME simple_circular_progress_bar_ohos)

add_library(${NATIVE_LIB_NAME} SHARED
    ./cpp/plugin_impl.cpp
)

target_link_libraries(${NATIVE_LIB_NAME} PUBLIC
    hilog_ndk.z
    # 可以链接其他 OHOS NDK 库,例如电池服务: libbattery_info.z
)

# 包含 OHOS NDK 头文件
target_include_directories(${NATIVE_LIB_NAME} PRIVATE
    ${CMAKE_CURRENT_SOURCE_DIR}/cpp
    ${OHOS_NDK_HOME}/include
)

第五步:在 Flutter 应用中集成与使用 最后,我们修改 lib/main.dart,把 OHOS 适配层和原来的 UI 库结合起来。

dart 复制代码
import 'package:flutter/material.dart';
import 'package:simple_circular_progress_bar/simple_circular_progress_bar.dart';
import './src/ohos_adapter.dart'; // 导入我们的适配层

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});
  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> with WidgetsBindingObserver {
  double _progress = 0;
  double _batteryHealthFactor = 1.0;
  bool _isAnimating = false;

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
    _initBatteryHealth();
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  // 应用生命周期回调,用来通知 OHOS 端动画状态
  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    final isAnimating = state == AppLifecycleState.resumed && _isAnimating;
    OhosProgressController.notifyAnimationState(isAnimating);
  }

  Future<void> _initBatteryHealth() async {
    // 从 OHOS 平台获取影响动画的因子
    final factor = await OhosProgressController.getBatteryHealthFactor();
    setState(() {
      _batteryHealthFactor = factor;
    });
  }

  void _startProgress() {
    setState(() {
      _isAnimating = true;
      _progress = 0;
    });
    // 通知 OHOS 端动画开始了
    OhosProgressController.notifyAnimationState(true);

    // 模拟一个受电池健康度影响的进度动画
    const totalSteps = 100;
    final stepDelay = 50 + (100 * (1.0 - _batteryHealthFactor)).toInt(); // 健康度越低,动画越慢

    Future.doWhile(() async {
      if (_progress >= 100) {
        setState(() => _isAnimating = false);
        OhosProgressController.notifyAnimationState(false);
        return false;
      }
      await Future.delayed(Duration(milliseconds: stepDelay));
      setState(() => _progress += (100 / totalSteps));
      return true;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('OHOS 适配进度条'),
        subtitle: Text('电池健康因子: ${_batteryHealthFactor.toStringAsFixed(2)}'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            SimpleCircularProgressBar(
              size: 200,
              progressStrokeWidth: 15,
              backStrokeWidth: 15,
              progressColors: const [Colors.blue, Colors.lightBlue],
              backColor: Colors.grey.shade200,
              animationDuration: 0, // 我们自定义动画,所以禁用内置动画
              valueNotifier: ValueNotifier(_progress),
              onGetText: (double value) {
                return Text(
                  '${value.toInt()}%',
                  style: const TextStyle(fontSize: 30, fontWeight: FontWeight.bold),
                );
              },
            ),
            const SizedBox(height: 40),
            Text(
              '进度: ${_progress.toStringAsFixed(1)}%',
              style: Theme.of(context).textTheme.headlineSmall,
            ),
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: _isAnimating ? null : _startProgress,
              child: const Text('开始受 OHOS 健康度影响的动画'),
            ),
          ],
        ),
      ),
    );
  }
}

三、性能优化与调试要点

3.1 降低平台通道开销

  • 批量化通信:千万别在动画的每一帧都通过 MethodChannel 调用原生端。像上面的例子,只在动画开始、结束这类关键生命周期节点进行通信。
  • 使用高效数据类型 :在通道里传递 intdoublebool 这些基本类型,而不是复杂的对象,能显著减少序列化和反序列化的开销。

3.2 原生端性能优化

  • 异步处理 :OHOS 原生端如果需要执行耗时操作(比如真的去查询电池服务),一定要用异步任务,避免阻塞 Flutter 的 UI 线程。可以用 libuv 或者 OHOS 自带的异步机制。
  • 资源管理 :确保在插件生命周期结束(比如 onDetached)时,释放所有在 OHOS 端申请的资源(比如传感器监听器),防止内存泄漏。

3.3 调试与日志

  • 善用 HiLog :在 OHOS Native 代码里多打一些 HiLog,可以在 DevEco Studio 的 Log 窗口清晰看到,方便跟踪原生逻辑的执行路径。
  • Flutter 侧日志 :Dart 端可以用 print 或者 logger 包,并通过 flutter logs 命令来抓取混合日志。
  • 错误边界处理 :就像 Dart 代码里展示的,所有 PlatformChannel 的调用都必须用 try-catch 包起来。这能保证即使原生端出了异常,Flutter 应用也不会崩溃,并且有合理的降级方案。

四、总结与展望

通过 simple_circular_progress_bar 这个库的适配实战,我们完整走了一遍将 Flutter 三方库迁移到 OpenHarmony 平台的流程。从环境搭建、插件架构设计、Dart 与 OHOS Native(C++) 的双向通信,到性能优化和调试,每个环节都进行了探讨。

有几点关键体会:

  1. 适配的本质 ,就是为 Flutter 插件在 OHOS 平台建立一个功能对等的原生实现,核心是玩转 Platform Channel
  2. 技术选型上,OHOS Native API (C/C++) 是实现高性能、深度系统集成插件的最佳路径。
  3. 代码健壮性很重要,适配必须包含完善的错误处理和降级策略,确保即使 OHOS 平台某个功能暂时缺失,Flutter 应用的核心体验仍然在线。
  4. 要时刻注意性能,跨平台通信的频次和数据量要精心设计,避免这里成为性能瓶颈。

展望未来,随着 OpenHarmony Hvigor 构建系统对 Flutter 插件编译的支持越来越完善,以及 flutter_ohos_tools 这类工具的成熟,Flutter 库的 OHOS 平台适配流程肯定会变得更加标准和自动化。到时候,开发者就能更专注于业务逻辑的跨平台抽象,而不是底层适配的细枝末节,这一定会加速鸿蒙生态的繁荣。

附录:性能对比数据(模拟)

场景 纯 Dart 版本 (FPS) 集成 OHOS 插件版本 (FPS) 说明
静态显示 60 60 无平台调用,无差异
基础动画 58-60 58-60 仅 Dart 端动画,无差异
高频平台调用 (每帧) 60 ~35 性能瓶颈出现
低频平台调用 (生命周期事件) 60 58-60 推荐做法,开销几乎可忽略

从模拟数据可以看出,只要设计合理(采用低频、批量的平台通信),适配对 Flutter 应用性能的影响微乎其微。这让我们有信心开发出既功能丰富又高性能的跨 OHOS 应用。

相关推荐
AiFlutter13 小时前
二、页面布局(10):瀑布流布局
flutter·低代码·低代码平台·aiflutter·aiflutter 低代码·flutter低代码开发·低代码app开发
IvanCodes14 小时前
[鸿蒙2025领航者闯关] 共享终端的隐形守护者:基于 HarmonyOS 6 的全链路隐私闭环实战
华为·harmonyos·鸿蒙
kirk_wang17 小时前
Flutter三方库在OHOS平台适配:firebase_messaging消息推送集成实践
flutter·移动开发·跨平台·arkts·鸿蒙
AiFlutter18 小时前
二、页面布局(09):流式布局
flutter·低代码·低代码平台·aiflutter·aiflutter低代码·低代码平台介绍
2501_9444460020 小时前
Flutter&OpenHarmony状态管理方案详解
开发语言·javascript·flutter
PWRJOY21 小时前
解决Flutter构建安卓项目卡在Flutter: Running Gradle task ‘assembleDebug‘...:替换国内 Maven 镜像
android·flutter·maven
名字被你们想完了1 天前
Flutter 实现一个容器内部元素可平移、缩放和旋转等功能(十)
前端·flutter
消失的旧时光-19431 天前
Flutter 网络层设计最佳实践(sealed + Result + Future)
flutter
走在路上的菜鸟1 天前
Android学Dart学习笔记第二十七节 异步编程
android·笔记·学习·flutter