CodeNewbie Community ๐ŸŒฑ

Cover image for HarmonyOS Flutter in action: 06 - Use ArkTs to develop Flutter HarmonyOS platform plugins
shaohushuo
shaohushuo

Posted on

HarmonyOS Flutter in action: 06 - Use ArkTs to develop Flutter HarmonyOS platform plugins

This article describes how to develop a Flutter HarmonyOS plugin, how to implement hybrid development of Flutter and HarmonyOS, and double-ended message communication.

On the 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});
  }

Enter fullscreen mode Exit fullscreen mode

The code lives a methodChannel and implements the call method for token errors.

On the ArkTs side, implement the 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};
Enter fullscreen mode Exit fullscreen mode

This file enables the native page to be configured with the Flutter engine and registered plugin when it loads. When Flutter initializes, the preference dataPreferences is 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";
  }
Enter fullscreen mode Exit fullscreen mode

The above code implements a plugin class, the core of which implements the onAttachedToEngine method in the FlutterPlugin, which is called after the Flutter engine is successfully loaded.

'onMethodCall', which receives a message call from Flutter, implements 'getPrefs' and 'setPrefs' two returns, respectively, where 'getPrefs' has a return value, through 'result.success(val); (see below) asynchronous return,
'setPrefs' does not return a value.

The following is a concrete implementation of 'ForestApi', which uses the preferences 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}.`);
    })
  }
}

Enter fullscreen mode Exit fullscreen mode

Precautions

  1. The name in the double-ended initialization methodChannel must be consistent, such as 'xxx.com/app'.
  2. 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.
  3. By default, only the basic data type is supported in communication, and complex types need to be serialized or encoded.
  4. The data received on the Dart side is of dymanic type, and the data type conversion needs to be performed.

References

Top comments (0)