一、前言
很多开发者在做 Flutter 跨端开发时,遇到鸿蒙平台需要调用原生能力(比如读写本地数据)的场景,就不知道该怎么下手了。这篇文章就带大家从零开始,用 ArkTs 开发一个 Flutter 鸿蒙插件,实现 Flutter 和鸿蒙原生的消息通信,甚至能直接调用鸿蒙的 “首选项 API” 完成 Token 的存储和读取 —— 全程大白话,小白也能跟着做!
二、核心思路
Flutter 和鸿蒙 ArkTs 之间的通信,核心靠MethodChannel(方法通道):
-
Flutter 侧:通过
MethodChannel发起方法调用(比如 “存 Token”“读 Token”); -
ArkTs 侧:监听这个通道,接收 Flutter 的调用请求,调用鸿蒙原生 API 处理,再把结果返回给 Flutter。
简单说,MethodChannel就是 Flutter 和 ArkTs 之间的 “传话员”,我们只需要让两边的 “传话员” 用同一个 “暗号”(通道名称),就能实现双向通信。
三、实战步骤
3.1 第一步:Flutter 侧编写 MethodChannel(发起调用)
首先在 Flutter 项目里,创建 MethodChannel 并封装 Token 的读写方法,代码如下(复制就能用,注释已经写满了):
import 'package:flutter/services.dart';
// 核心:创建MethodChannel,通道名称必须和ArkTs侧一致!
const MethodChannel _methodChannel = MethodChannel('xxx.com/app');
/// 读取本地存储的Token
static Future<dynamic> getToken() {
// 调用ArkTs侧的"getPrefs"方法,参数传"token"(要读取的键名)
return _methodChannel.invokeMethod("getPrefs", 'token');
}
/// 存储Token到鸿蒙本地
static Future<dynamic> setToken(String token) {
// 调用ArkTs侧的"setPrefs"方法,传键值对(key=token,value=传入的token值)
return _methodChannel.invokeMethod("setPrefs", {'key': 'token', 'value': token});
}
3.2 第二步:ArkTs 侧配置 EntryAbility(初始化环境)
在鸿蒙项目的src/main/ets/entryability/EntryAbility.ets文件中,完成 Flutter 引擎配置、插件注册,同时初始化鸿蒙的 “首选项”(用来存数据的本地存储):
import { FlutterAbility, FlutterEngine } from '@ohos/flutter_ohos';
import { GeneratedPluginRegistrant } from '../plugins/GeneratedPluginRegistrant';
// 后面要写的插件类,先引入
import ForestPlugin from './ForestPlugin';
import { BusinessError } from '@kit.BasicServicesKit';
import { window } from '@kit.ArkUI';
import { preferences } from '@kit.ArkData';
// 全局变量:保存首选项实例,方便后续读写数据
let dataPreferences: preferences.Preferences | null = null;
export default class EntryAbility extends FlutterAbility {
// 窗口创建时初始化首选项
onWindowStageCreate(windowStage: window.WindowStage): void {
super.onWindowStageCreate(windowStage);
// 获取鸿蒙首选项实例,名称叫"forestStore"(自定义,随便取)
preferences.getPreferences(this.context, 'forestStore', (err: BusinessError, val: preferences.Preferences) => {
if (err) {
console.error("读取首选项失败:" + err.message);
return;
}
// 初始化成功,把实例赋值给全局变量
dataPreferences = val;
console.info("首选项初始化成功!");
})
}
// 配置Flutter引擎,注册插件
configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
// 注册默认插件
GeneratedPluginRegistrant.registerWith(flutterEngine)
// 注册我们自己写的ForestPlugin插件
this.addPlugin(new ForestPlugin());
}
}
// 导出首选项实例,供插件类使用
export {dataPreferences};
3.3 第三步:ArkTs 侧编写 ForestPlugin(处理 Flutter 调用)
创建src/main/ets/entryability/ForestPlugin.ets文件,这是核心插件类,用来接收 Flutter 的调用并处理:
import { Any, FlutterPlugin, Log, MethodCall, MethodChannel, MethodResult } from '@ohos/flutter_ohos';
import { FlutterPluginBinding } from '@ohos/flutter_ohos/src/main/ets/embedding/engine/plugins/FlutterPlugin';
import { BusinessError } from '@kit.BasicServicesKit';
import { preferences } from '@kit.ArkData';
// 引入上面初始化的首选项实例
import {dataPreferences} from './EntryAbility';
// 日志标签,方便调试
const TAG = "[flutter][plugin][forest]";
// 实现FlutterPlugin接口,这是鸿蒙Flutter插件的标准写法
export default class ForestPlugin implements FlutterPlugin {
// 声明MethodChannel对象
private channel?: MethodChannel;
// 声明API类(后面封装读写逻辑)
private api = new ForestApi();
// Flutter引擎加载成功后调用,核心方法!
onAttachedToEngine(binding: FlutterPluginBinding): void {
// 创建MethodChannel,通道名称必须和Flutter侧一致!
this.channel = new MethodChannel(binding.getBinaryMessenger(), "xxx.com/app");
// 设置方法调用处理器,接收Flutter的指令
this.channel.setMethodCallHandler({
onMethodCall : (call: MethodCall, result: MethodResult) => {
console.log(`${TAG} 收到Flutter调用:${call.method},参数:${JSON.stringify(call.args)}`);
// 根据Flutter传的指令名,分情况处理
switch (call.method) {
// 处理"读取数据"指令
case "getPrefs":
this.api.getPrefs(String(call.args), result);
break;
// 处理"存储数据"指令
case "setPrefs":
let key = String(call.argument("key")); // 取参数里的key
let value = String(call.argument("value")); // 取参数里的value
this.api.setPrefs(key, value);
break;
// 未知指令,返回"未实现"
default:
result.notImplemented();
break;
}
}
})
}
// 插件销毁时清空处理器,避免内存泄漏
onDetachedFromEngine(binding: FlutterPluginBinding): void {
Log.i(TAG, "插件已销毁");
this.channel?.setMethodCallHandler(null);
}
// 插件唯一标识,自定义即可
getUniqueClassName(): string {
return "ForestPlugin";
}
}
// 封装鸿蒙首选项的读写逻辑
class ForestApi {
// 读取数据:key是要读的键名,result用来返回结果给Flutter
getPrefs(key: string, result: MethodResult) {
dataPreferences?.get(key, '', (err: BusinessError, val: preferences.ValueType) => {
if (err) {
console.error(`${TAG} 读取${key}失败:${err.message}`);
result.success(''); // 读取失败返回空字符串
return;
}
console.info(`${TAG} 读取${key}成功:${val}`);
result.success(val); // 读取成功,把值返回给Flutter
})
}
// 存储数据:key是键名,value是要存的值
setPrefs(key: string, value: string) {
dataPreferences?.put(key, value, (err: BusinessError) => {
if (err) {
console.error(`${TAG} 存储${key}失败:${err.message}`);
return;
}
console.info(`${TAG} 存储${key}成功`);
})
}
// 扩展:清空指定key的数据(可选)
clearPrefs(key: string) {
dataPreferences?.delete(key, (err: BusinessError) => {
if (err) {
console.error(`${TAG} 删除${key}失败:${err.message}`);
return;
}
console.info(`${TAG} 删除${key}成功`);
})
}
}
四、必看的注意事项(小白踩坑点)
MethodChannel 名称必须完全一致:Flutter 侧的xxx.com/app和 ArkTs 侧的通道名称要一字不差,否则通信会失败;
异步返回要加 await:ArkTs 侧用result.success()返回数据是异步的,Flutter 侧调用getToken()时要加await,比如:
String token = await getToken();
数据类型只支持基础类型:Flutter 和 ArkTs 通信只能传字符串、数字、布尔等基础类型,复杂对象(比如自定义类)要先序列化(转 JSON 字符串);
Flutter 侧接收数据要转类型:Flutter 侧getToken()返回的是dynamic类型,要手动转成字符串 / 数字等,避免类型错误;
首选项初始化要提前:一定要在onWindowStageCreate里初始化首选项,否则读写时会报null错误。
五、总结
其实用 ArkTs 开发 Flutter 鸿蒙插件的核心就两点:
-
两边用同一个
MethodChannel名称建立通信; -
ArkTs 侧监听方法调用,调用鸿蒙原生 API 处理,再把结果返回。
这篇教程实现了 Token 的读写,你可以照着这个思路扩展 —— 比如调用鸿蒙的电池 API、路由 API 等,都是换汤不换药,只需要在onMethodCall里加新的case即可。
六、扩展小技巧
如果想实现更复杂的通信(比如实时消息推送),可以用EventChannel(事件通道),用法和MethodChannel类似,只是支持持续的消息推送,感兴趣的可以自己试试~
328

被折叠的 条评论
为什么被折叠?



