This article describes how to develop a Flutter Harmony plugin, how to achieve hybrid development of Flutter and Harmony, and two-way message communication.
Flutter side, write MethodChannel
const MethodChannel _methodChannel = MethodChannel('xxx.com/app');
/// Get token
static Future<dynamic> getToken() {
return _methodChannel.invokeMethod("getPrefs", 'token');
}
/// Set token
static Future<dynamic> setToken(String token) {
return _methodChannel
.invokeMethod("setPrefs", {'key': 'token', 'value': token});
}
The code declares a methodChannel and implements the calling method of token storage error.
ArkTs side, implementation call
Write src/main/ets/entryability/EntryAbility.ets
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);
preferences.getPreferences(this.context, 'forestStore', (err: BusinessError, val: preferences.Preferences) => {
if (err) {
console.error("Failed to get preferences. code =" + err.code + ", message =" + err.message);
return;
}
dataPreferences = val;
console.info("Succeeded in getting preferences1.");
})
}
configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
GeneratedPluginRegistrant.registerWith(flutterEngine)
this.addPlugin(new ForestPlugin());
}
}
export {dataPreferences};
This file configures Flutter when the native page is loaded. Engine, register plugins. When Flutter is initialized, the preferences dataPreferences are also initialized for later use.
Write src/main/ets/entryability/ForestPlugin.ets
import { Any, BasicMessageChannel, EventChannel, FlutterManager, FlutterPlugin, Log, MethodCall, MethodChannel, StandardMessageCodec} from '@ohos/flutter_ohos';
import { FlutterPluginBinding } from '@ohos/flutter_ohos/src/main/ets/embedding/engine/plugins/FlutterPlugin';
import { batteryInfo } from '@kit.BasicServicesKit';
import { MethodCallHandler, MethodResult } from '@ohos/flutter_ohos/src/main/ets/plugin/common/MethodChannel';
import { preferences } from '@kit.ArkData';
import { BusinessError } from '@kit.BasicServicesKit';
import {dataPreferences} from './EntryAbility';
import router from '@ohos.router'
import { webviewRouterParams } from '../pages/Webview';
const TAG = "[flutter][plugin][forest]";
export default class ForestPlugin implements FlutterPlugin {
private channel?: MethodChannel;
private basicChannel?: BasicMessageChannel<Any>;
private api = new ForestApi();
private dataPreferences: preferences.Preferences | null = null;
onAttachedToEngine(binding: FlutterPluginBinding): void {
this.channel = new MethodChannel(binding.getBinaryMessenger(), "xxx.com/app");
this.channel.setMethodCallHandler({ onMethodCall: (call: MethodCall, result: MethodResult) => {
console.log(`${TAG}-->[${call.method}]: ${JSON.stringify(call.args)}`);
switch (call.method) {
case "getPrefs":
this.api.getPrefs(String(call.args), result);
break;
case "setPrefs":
let key = String(call.argument("key"));
let value = String(call.argument("value"));
this.api.setPrefs(key, value);
default:
result.notImplemented();
break;
}
}
})
}
//···
onDetachedFromEngine(binding: FlutterPluginBinding): void {
Log.i(TAG, "onDetachedFromEngine"); this.channel?.setMethodCallHandler(null);
}
getUniqueClassName(): string {
return "ForestPlugin";
}
The above code implements a plug-in class, the core of which implements the onAttachedToEngine method in FlutterPlugin, which is called after the Flutter engine is loaded successfully.
onMethodCall
receives the message call from Flutter, and implements two callbacks, 'getPrefs' and 'setPrefs', respectively. Among them, getPrefs
has a return value, which is returned asynchronously through result.success(val);
(see below), and setPrefs
has no return value.
The following is the specific implementation of ForestApi
, which uses the preference API in HarmonyOS to set and read data.
class ForestApi {
getPrefs(key: string, result: MethodResult) {
dataPreferences?.get(key, '', (err: BusinessError, val: preferences.ValueType) => {
if (err) {
console.error(`${TAG} Failed to get value of ${key}. code =` + err.code + ", message =" + err.message);
result.success('');
}
console.info(`${TAG} Succeeded in getting value of ${key}:${val}.`);
result.success(val);
})
}
setPrefs(key: string, value: string) {
dataPreferences?.put(key, value, (err: BusinessError) => {
if (err) {
console.error(`${TAG} Failed to put value of ${key}. code =` + err.code + ", message =" + err.message);
return;
}
console.info(`${TAG} Succeeded in putting value of ${key}.`);
})
}
clearPrefs(key: string) {
dataPreferences?.delete(key, (err: BusinessError) => {
if (err) {
console.error("Failed to delete the key 'startup'. code =" + err.code + ", message =" + err.message);
return;
}
console.info(`Succeeded in deleting the key ${key}.`);
})
}
}
Notes
The name in the initialization methodChannel on both ends must be consistent, such as
xxx.com/app
.The arkTS side returns data through result.success(val), which is asynchronous, so you need to use await or callback function to get the value on the Dart side.
Only basic data types are supported by default in communication, and complex types need to be serialized or encoded and decoded.
The data received on the Dart side is of dymanic type, which requires data type conversion.
Top comments (1)
Some comments may only be visible to logged-in visitors. Sign in to view all comments.