Flutter-OH 插件适配 HarmonyOS 实战:以屏幕方向控制为例

Flutter-OH 插件适配 HarmonyOS 实战:以屏幕方向控制为例

欢迎大家加入开源鸿蒙跨平台社区

前言

随着 HarmonyOS 生态的快速发展,越来越多的 Flutter 开发者希望将自己的插件适配到 HarmonyOS 平台。本文将以 flutter_orientation 插件为例,详细介绍如何将 Android 平台的 Flutter 插件适配到 HarmonyOS,帮助开发者快速掌握适配技巧。

效果

一、背景介绍

flutter_orientation 是一个用于控制设备屏幕方向的 Flutter 插件,支持 Android、iOS 和 HarmonyOS 平台。本文重点介绍如何参考 Android 实现,完成 HarmonyOS 平台的适配工作。

插件功能

  • 设置设备屏幕方向(竖屏、横屏、倒置竖屏、倒置横屏)
  • 跨平台统一的 API 接口
  • 支持动态切换屏幕方向

二、HarmonyOS Flutter 插件架构

2.1 插件生命周期

HarmonyOS Flutter 插件需要实现以下接口:

  • FlutterPlugin: 插件基础接口,管理插件与 Flutter Engine 的绑定
  • MethodCallHandler: 处理方法调用
  • AbilityAware: 获取 UIAbility 生命周期(类似 Android 的 ActivityAware)

2.2 关键接口说明

typescript 复制代码
// 插件基础接口
interface FlutterPlugin {
  onAttachedToEngine(binding: FlutterPluginBinding): void;
  onDetachedFromEngine(binding: FlutterPluginBinding): void;
}

// 方法调用处理接口
interface MethodCallHandler {
  onMethodCall(call: MethodCall, result: MethodResult): void;
}

// Ability 生命周期接口
interface AbilityAware {
  onAttachedToAbility(binding: AbilityPluginBinding): void;
  onDetachedFromAbility(): void;
}

三、Android vs HarmonyOS 实现对比

3.1 架构对比

特性 Android HarmonyOS
生命周期接口 ActivityAware AbilityAware
上下文对象 Activity UIAbility
窗口管理 Activity.setRequestedOrientation() Window.setPreferredOrientation()
编程语言 Java/Kotlin TypeScript (ETS)

3.2 代码结构对比

Android 实现
java 复制代码
public class FlutterOrientationPlugin implements 
    FlutterPlugin, MethodCallHandler, ActivityAware {
    
    private MethodChannel channel;
    private Activity activity;
    
    @Override
    public void onAttachedToActivity(ActivityPluginBinding binding) {
        activity = binding.getActivity();
    }
    
    @Override
    public void onMethodCall(MethodCall call, Result result) {
        if (call.method.equals("setOrientation")) {
            String orientation = (String) call.arguments;
            // 设置屏幕方向
            activity.setRequestedOrientation(getOrientation(orientation));
        }
    }
}
HarmonyOS 实现
typescript 复制代码
export default class FlutterOrientationPlugin implements 
    FlutterPlugin, MethodCallHandler, AbilityAware {
    
    private channel: MethodChannel | null = null;
    private ability: UIAbility | null = null;
    
    onAttachedToAbility(binding: AbilityPluginBinding): void {
        this.ability = binding.getAbility();
    }
    
    onMethodCall(call: MethodCall, result: MethodResult): void {
        if (call.method == "setOrientation") {
            this.setOrientation(call, result);
        }
    }
}

四、核心实现详解

4.1 完整的 HarmonyOS 实现

typescript 复制代码
import {
  FlutterPlugin,
  FlutterPluginBinding,
  MethodCall,
  MethodCallHandler,
  MethodChannel,
  MethodResult,
  AbilityAware,
  AbilityPluginBinding,
} from '@ohos/flutter_ohos';
import { window } from '@kit.ArkUI';
import { UIAbility } from '@kit.AbilityKit';

export default class FlutterOrientationPlugin 
    implements FlutterPlugin, MethodCallHandler, AbilityAware {
    
  private channel: MethodChannel | null = null;
  private ability: UIAbility | null = null;

  getUniqueClassName(): string {
    return "FlutterOrientationPlugin"
  }

  // 1. 绑定到 Flutter Engine
  onAttachedToEngine(binding: FlutterPluginBinding): void {
    this.channel = new MethodChannel(
      binding.getBinaryMessenger(), 
      "chavesgu/orientation"
    );
    this.channel.setMethodCallHandler(this);
  }

  // 2. 解绑 Flutter Engine
  onDetachedFromEngine(binding: FlutterPluginBinding): void {
    if (this.channel != null) {
      this.channel.setMethodCallHandler(null);
    }
  }

  // 3. 绑定到 UIAbility(关键步骤)
  onAttachedToAbility(binding: AbilityPluginBinding): void {
    this.ability = binding.getAbility();
  }

  // 4. 解绑 UIAbility
  onDetachedFromAbility(): void {
    this.ability = null;
  }

  // 5. 处理方法调用
  onMethodCall(call: MethodCall, result: MethodResult): void {
    if (call.method == "setOrientation") {
      this.setOrientation(call, result);
    } else {
      result.notImplemented();
    }
  }

  // 6. 设置屏幕方向的核心方法
  private setOrientation(call: MethodCall, result: MethodResult): void {
    const orientation = call.args as string;
    let targetOrientation: window.Orientation;

    // 方向映射:Flutter -> HarmonyOS
    if (orientation === "DeviceOrientation.portraitUp") {
      targetOrientation = window.Orientation.PORTRAIT;
    } else if (orientation === "DeviceOrientation.portraitDown") {
      targetOrientation = window.Orientation.PORTRAIT_INVERTED;
    } else if (orientation === "DeviceOrientation.landscapeLeft") {
      targetOrientation = window.Orientation.LANDSCAPE_INVERTED;
    } else if (orientation === "DeviceOrientation.landscapeRight") {
      targetOrientation = window.Orientation.LANDSCAPE;
    } else {
      targetOrientation = window.Orientation.UNSPECIFIED;
    }

    // 检查 ability 是否可用
    if (!this.ability) {
      result.error("NO_ABILITY", "Ability is null", null);
      return;
    }

    // 获取窗口并设置方向
    window.getLastWindow(this.ability.context).then((windowClass) => {
      windowClass.setPreferredOrientation(targetOrientation).then(() => {
        result.success(null);
      }).catch((err: Error) => {
        result.error(
          "SET_ORIENTATION_ERROR", 
          `Failed to set orientation: ${err.message}`, 
          null
        );
      });
    }).catch((err: Error) => {
      result.error(
        "GET_WINDOW_ERROR", 
        `Failed to get window: ${err.message}`, 
        null
      );
    });
  }
}

4.2 关键实现点解析

1. 实现 AbilityAware 接口

这是 HarmonyOS 适配的关键步骤。与 Android 的 ActivityAware 类似,AbilityAware 接口允许插件获取 UIAbility 实例,从而访问窗口和上下文信息。

typescript 复制代码
onAttachedToAbility(binding: AbilityPluginBinding): void {
  this.ability = binding.getAbility();
}
2. 方向映射关系

Flutter 的方向枚举需要映射到 HarmonyOS 的窗口方向:

Flutter 方向 HarmonyOS 方向
DeviceOrientation.portraitUp window.Orientation.PORTRAIT
DeviceOrientation.portraitDown window.Orientation.PORTRAIT_INVERTED
DeviceOrientation.landscapeLeft window.Orientation.LANDSCAPE_INVERTED
DeviceOrientation.landscapeRight window.Orientation.LANDSCAPE

注意landscapeLeft 对应 LANDSCAPE_INVERTEDlandscapeRight 对应 LANDSCAPE,这与 Android 的实现保持一致。

3. 获取窗口实例

HarmonyOS 需要通过 window.getLastWindow() 获取窗口实例,然后调用 setPreferredOrientation() 设置方向:

typescript 复制代码
window.getLastWindow(this.ability.context).then((windowClass) => {
  windowClass.setPreferredOrientation(targetOrientation);
});
4. 错误处理

完善的错误处理机制确保插件在各种情况下都能正常工作:

  • 检查 ability 是否为 null
  • 捕获窗口获取和方向设置的异常
  • 返回详细的错误信息

五、适配步骤总结

5.1 适配 Checklist

  • 实现 FlutterPlugin 接口
  • 实现 MethodCallHandler 接口
  • 实现 AbilityAware 接口(如需要访问窗口/上下文)
  • 创建 MethodChannel 并设置处理器
  • onAttachedToAbility 中保存 UIAbility 引用
  • 实现方法调用处理逻辑
  • 映射 Flutter 枚举到 HarmonyOS 枚举
  • 添加错误处理
  • pubspec.yaml 中注册插件

5.2 pubspec.yaml 配置

确保在 pubspec.yaml 中正确配置 HarmonyOS 平台:

yaml 复制代码
flutter:
  plugin:
    platforms:
      android:
        package: com.chavesgu.flutter_orientation
        pluginClass: FlutterOrientationPlugin
      ios:
        pluginClass: FlutterOrientationPlugin
      ohos:
        pluginClass: FlutterOrientationPlugin

六、常见问题与解决方案

6.1 问题:无法获取窗口实例

原因ability 为 null 或未正确绑定。

解决方案

  • 确保实现了 AbilityAware 接口
  • onAttachedToAbility 中正确保存 ability 引用
  • 在调用窗口 API 前检查 ability 是否为 null

6.2 问题:方向设置不生效

原因:方向映射错误或窗口获取失败。

解决方案

  • 检查方向映射是否正确
  • 确认 window.getLastWindow() 调用成功
  • 查看错误日志定位具体问题

6.3 问题:插件未注册

原因pubspec.yaml 配置错误或插件类名不匹配。

解决方案

  • 检查 pubspec.yaml 中的 pluginClass 是否与实现类名一致
  • 确保 getUniqueClassName() 返回正确的类名

七、最佳实践

7.1 代码组织

  • 将核心逻辑封装为私有方法,提高代码可读性
  • 使用 TypeScript 的类型系统增强代码安全性
  • 添加详细的注释说明关键步骤

7.2 错误处理

  • 始终检查必要对象是否为 null
  • 使用 Promise 的 catch 处理异步错误
  • 返回有意义的错误码和错误信息

7.3 性能优化

  • 避免在 onMethodCall 中执行耗时操作
  • 使用异步方法处理窗口操作
  • 及时清理资源,避免内存泄漏

八、总结

通过本文的介绍,我们了解了如何将 Android 平台的 Flutter 插件适配到 HarmonyOS。主要步骤包括:

  1. 理解架构差异:掌握 Android 和 HarmonyOS 在插件架构上的差异
  2. 实现生命周期接口 :正确实现 FlutterPluginMethodCallHandlerAbilityAware
  3. 映射平台 API:将 Android 的 API 映射到对应的 HarmonyOS API
  4. 处理异步操作:使用 Promise 处理窗口相关的异步操作
  5. 完善错误处理:添加完善的错误处理机制

希望本文能够帮助更多开发者快速完成 Flutter 插件到 HarmonyOS 的适配工作,共同推动 HarmonyOS 生态的发展。

参考资料


相关推荐
不爱吃糖的程序媛10 小时前
Flutter 3.32.4-ohos-0.0.2 版本发布
flutter
追梦的鱼儿11 小时前
Flutter 生命周期详解:Stateless 与 Stateful 完全对比
flutter
tangweiguo0305198711 小时前
Flutter 页面生命周期超全总结(附 addPostFrameCallback 详解)
flutter
国医中兴12 小时前
Flutter 三方库 dson 的鸿蒙化适配指南 - 极简的序列化魔法、在鸿蒙端实现反射式 JSON 映射实战
flutter·harmonyos·鸿蒙·openharmony
Lesile13 小时前
Flutter回顾#1:动画:
flutter
yuanlaile14 小时前
2026年全新版Flutter教程_Dart Flutter入门实战系列视频教程
flutter·flutter教程·dart教程·flutter必备基础
池央14 小时前
在鸿蒙上跑 AI Agent:JiuwenClaw-on-OpenHarmony 完整实战
人工智能·华为·harmonyos
互联网散修14 小时前
零基础鸿蒙应用开发第五节:基础数据类型与对象类型转换
华为·harmonyos·鸿蒙应用开发入门教程
弓.长.14 小时前
ReactNative for OpenHarmony项目鸿蒙化三方库:react-native-fast-image — 高性能图片加载组件
react native·react.js·harmonyos
互联网散修14 小时前
零基础鸿蒙应用开发第六节:复杂数据类型入门 —— 数组、元组与枚举
华为·log4j·harmonyos