1.鸿蒙OHOS项目目录下引用tuanjieLib

        先把Unity导出的鸿蒙项目中的tuanjieLib整个文件夹考到OHOS项目根目录下,然后在根目录下的build-profile.json5文件添加modules

"modules": [
    {
      "name": "entry",
      "srcPath": "./entry",
      "targets": [
        {
          "name": "default",
          "applyToProducts": [
            "default"
          ]
        }
      ]
    },
    {
      "name": "tuanjieLib",
      "srcPath": "./tuanjieLib",
      "targets": [
        {
          "name": "default",
          "applyToProducts": [
            "default"
          ]
        }
      ]
    }
  ]

2.entry主库添加配置

2.1.oh-package.json5配置

{
  "name": "entry",
  "version": "1.0.0",
  "description": "Please describe the basic information.",
  "main": "",
  "author": "",
  "license": "",
  "dependencies": {
    "tuanjieLib": "file:../tuanjieLib"
  },
}

 2.2.src/module.json5配置

unity的权限添加

"requestPermissions": [
      {
        "name" :  "ohos.permission.INTERNET",
        "usedScene": {
          "abilities": [
            "EntryAbility"
          ],
          "when": "always"
        },
        "reason": "$string:default_reason"
      },
      {
        "name": "ohos.permission.ACCELEROMETER",
        "usedScene": {
          "abilities": [
            "EntryAbility"
          ],
          "when": "always"
        },
        "reason": "$string:default_reason"
      }
    ]

3.代码添加

3.1.src/main/ets/plugins添加UnityNativeViewPlugin.ets代码

import  { FlutterPlugin,FlutterPluginBinding } from '@ohos/flutter_ohos/src/main/ets/embedding/engine/plugins/FlutterPlugin';
import StandardMessageCodec from '@ohos/flutter_ohos/src/main/ets/plugin/common/StandardMessageCodec';
import { CustomUnityFactory } from '../unity/CustomUnityFactory';
import common from '@ohos.app.ability.common';
import EntryAbility from '../entryability/EntryAbility';

export class UnityNativeViewPlugin implements FlutterPlugin {
  private myContext: common.Context;
  private myAb:EntryAbility;
  constructor(context: common.Context,ab:EntryAbility) {
    this.myContext = context;
    this.myAb = ab;
  }

  getUniqueClassName(): string {
    return 'UnityNativeViewPlugin';
  }

  onAttachedToEngine(binding: FlutterPluginBinding): void {
    binding.getPlatformViewRegistry()?.registerViewFactory(
      'com.xxx.xxx.ohos/customUnityView',
      new CustomUnityFactory(binding.getBinaryMessenger(), StandardMessageCodec.INSTANCE, this.myAb)
    );
  }

  onDetachedFromEngine(binding: FlutterPluginBinding): void {}
}

3.2src/main/ets/unity添加CustomUnityFactory 和 CustomUnityView

import MethodChannel, {
  MethodCallHandler,
  MethodResult
} from '@ohos/flutter_ohos/src/main/ets/plugin/common/MethodChannel';
import PlatformView, { Params } from '@ohos/flutter_ohos/src/main/ets/plugin/platform/PlatformView';
import common from '@ohos.app.ability.common';
import { BinaryMessenger } from '@ohos/flutter_ohos/src/main/ets/plugin/common/BinaryMessenger';
import StandardMethodCodec from '@ohos/flutter_ohos/src/main/ets/plugin/common/StandardMethodCodec';
import MethodCall from '@ohos/flutter_ohos/src/main/ets/plugin/common/MethodCall';
import {APP_KEY_SAFEAREA_RECT} from 'tuanjieLib';
import { TuanjiePlayerView } from 'tuanjieLib';
import { window } from '@kit.ArkUI';
import { Tuanjie } from 'tuanjieLib/src/main/ets/utils/TuanjieNative';
import { TuanjieLog } from 'tuanjieLib/src/main/ets/common/TuanjieLog';
import { POST_MESSAGE } from 'tuanjieLib/src/main/ets/workers/WorkerProxy';
import { DisplayInfoManager } from 'tuanjieLib/src/main/ets/utils/DisplayInfoManager';
import { VideoPlayerProxy } from 'tuanjieLib/src/main/ets/utils/VideoPlayerProxy';
import { WindowUtils } from 'tuanjieLib/src/main/ets/utils/WindowUtils';
import { GetFromGlobalThis, SetToGlobalThis } from 'tuanjieLib/src/main/ets/common/GlobalThisUtil';

@Entry
@Component
struct CustomUnityComponent {
  @State displayIndex0:number = 0;
  @StorageProp(APP_KEY_SAFEAREA_RECT) safeAreaRect:window.Rect = {top:0,left:0,width:0,height:0};

  build() {
    Column() {
      TuanjiePlayerView({displayIndex:this.displayIndex0}).width('100%').height('100%');
    }
    .width('100%')
    .height('100%')
  }
}

@Builder
function UnityBuilder(params: Params) {
  CustomUnityComponent().backgroundColor(Color.White)
}

@Observed
export class CustomUnityView extends PlatformView implements MethodCallHandler{
  channel: MethodChannel;
  private intervalId: number | undefined;
  private static isInitialized = false; // 防止重复初始化

  constructor(context: common.Context, viewId: number, args: ESObject, message: BinaryMessenger ,windowStage: window.WindowStage) {
    super();
    // 注册消息通道
    this.channel = new MethodChannel(message, 'com.xxx.xxx.ohos/unity_channel');
    this.channel.setMethodCallHandler(this);

    this.initializeOnce(context,windowStage);

    this.intervalId = setInterval(() => {
      //this.sendEventToFlutter('onUnityEvent','bbbb');
    }, 2000);
  }

  onMethodCall(call: MethodCall, result: MethodResult): void {
    switch (call.method) {
      case 'fromFlutter':
        console.log('Received from Flutter: ' + JSON.stringify(call.method));
        // 执行原生操作,例如更新 Unity 视图
        // 可以回复数据:return 'some result';
        break;
      case 'onResume':
        this.onResume();
        break;
      case 'onPause':
        this.onPause()
        break;
      case 'onDestroy':
        this.onDestroy()
        break;
      default:
        throw new Error('Unknown method ' + call.method);
    }
  }

  /**
   * 一次性初始化:原生模块、Worker、显示信息、全局变量
   */
  private initializeOnce(context: common.Context,windowStage: window.WindowStage): void {
    TuanjieLog.info('%{public}s', 'TuanjiePlayerAbility onCreate, bundleCodeDir:' + context.bundleCodeDir);

    Tuanjie.nativeOnCreate();
    globalThis.CommandLineArguments = 'undefined';
    POST_MESSAGE({
      type: 'SetGlobalThisContext', data: context
    });
    globalThis.AbilityContext = context;
    DisplayInfoManager.getInstance().initialize();
    AppStorage.setOrCreate("backButtonLeavesApp", false);
    CustomUnityView.isInitialized = true;

    if(windowStage){
      let windowClass = windowStage.getMainWindowSync();
      try {
        SetToGlobalThis(WindowUtils.getInstance().MainWindowKey, windowClass);
        WindowUtils.getInstance().setWindowSizeChangeCallback();
        WindowUtils.getInstance().setWindowAvoidAreaChangeCallBack();
        windowClass.setWindowLayoutFullScreen(true, (err1, data1) => {
          if (err1.code) {
            console.error('Failed to enable the full-screen mode. Cause: ' + JSON.stringify(err1));
            return;
          }
          console.info('Succeeded in enabling the full-screen mode. Data: ' + JSON.stringify(data1));
        });
      } catch (err) {
        console.error('Failed to obtain the main window. Cause: ' + JSON.stringify(err));
      }
    }

    this.onResume();
  }

  public sendEventToFlutter(eventName: string, data: String): void {
    this.channel.invokeMethod(eventName, {'data': data});
  }

  getView(): WrappedBuilder<[Params]> {
    return new WrappedBuilder(UnityBuilder);
  }

  private onResume(): void {
    if (VideoPlayerProxy.getInstance().IsPlaying()) {
      VideoPlayerProxy.getInstance().OnResume();
    }
    else {
      Tuanjie.nativeOnResume();
    }
    POST_MESSAGE({
      type: 'InternetSubscribe', data: null
    });
  }

  private onPause(): void {
    if (VideoPlayerProxy.getInstance().IsPlaying()) {
      VideoPlayerProxy.getInstance().OnPause();
    }
    else {
      Tuanjie.nativeOnPause();
    }
  }

  private onDestroy(): void {
    TuanjieLog.info('%{public}s', 'TuanjiePlayerAbility onDestroy');
    Tuanjie.nativeOnDestroy();
  }

  dispose(): void {
    // 清理资源,可取消窗口监听等
    if (this.intervalId !== undefined) {
      clearInterval(this.intervalId);
      this.intervalId = undefined;
    }
  }
}
import { BinaryMessenger } from '@ohos/flutter_ohos/src/main/ets/plugin/common/BinaryMessenger';
import MessageCodec from '@ohos/flutter_ohos/src/main/ets/plugin/common/MessageCodec';
import PlatformViewFactory from '@ohos/flutter_ohos/src/main/ets/plugin/platform/PlatformViewFactory';
import common from '@ohos.app.ability.common';
import PlatformView from '@ohos/flutter_ohos/src/main/ets/plugin/platform/PlatformView';
import { CustomUnityView } from './CustomUnityView';
import EntryAbility from '../entryability/EntryAbility';


export class CustomUnityFactory extends PlatformViewFactory {
  message: BinaryMessenger;
  ability:EntryAbility;
  constructor(message: BinaryMessenger, createArgsCodes: MessageCodec<Object>,myAb:EntryAbility) {
    super(createArgsCodes);
    this.message = message;
    this.ability = myAb;
  }

  public create(context: common.Context, viewId: number, args: Object): PlatformView {
    let windowStage = this.ability.getWindowStage();
    //let windowStage: window.WindowStage | undefined;
    return new CustomUnityView(context, viewId, args, this.message, windowStage!);
  }
}

3.3src/main/ets/entryability/EntryAbility代码修改

import { FlutterAbility, FlutterEngine } from '@ohos/flutter_ohos';
import { GeneratedPluginRegistrant } from '../plugins/GeneratedPluginRegistrant';
import { UnityNativeViewPlugin } from '../plugins/UnityNativeViewPlugin';
import { window } from '@kit.ArkUI';
import { AbilityConstant, Want } from '@kit.AbilityKit';

export default class EntryAbility extends FlutterAbility {

  private windowStage: window.WindowStage | null = null;

  onWindowStageCreate(windowStage: window.WindowStage): void {
    super.onWindowStageCreate(windowStage);
    this.windowStage = windowStage;
  }

  // 提供一个获取 WindowStage 的方法
  getWindowStage(): window.WindowStage | null {
    return this.windowStage;
  }

  configureFlutterEngine(flutterEngine: FlutterEngine) {
    super.configureFlutterEngine(flutterEngine)
    GeneratedPluginRegistrant.registerWith(flutterEngine)

    this.addPlugin(new UnityNativeViewPlugin(this.context,this))
  }
}

4.flutter端使用控件


import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

class UnityView extends StatefulWidget {
  const UnityView({super.key});

  @override
  State<UnityView> createState() => _UnityViewState();
}

class _UnityViewState extends State<UnityView> with WidgetsBindingObserver{
  // 方法通道,用于与原生通信
  static const String _channelName = 'com.xxx.xxx.ohos/unity_channel';
  MethodChannel? _channel;

  @override
  void initState() {
    super.initState();
    // 监听来自原生的事件
    _channel = MethodChannel(_channelName);
    _channel?.setMethodCallHandler(_handleMethod);

    WidgetsBinding.instance.addObserver(this);
  }

  Future<void> _sendMessageToNative(String name,String message) async {
    try {
      await _channel?.invokeMethod(name, {'data': message});
    } on PlatformException catch (e) {
      print('Failed to send message: ${e.message}');
    }
  }

  // 处理从鸿蒙原生发来的消息,无用
  Future<dynamic> _handleMethod(MethodCall call) async {
    switch (call.method) {
      case 'onUnityEvent':
        // 例如接收到原生视图的点击事件
        print('Received from native: ${call.arguments}');
        // 可以回调给业务层
        break;
      default:
        throw UnimplementedError('Unknown method ${call.method}');
    }
  }

  @override
  Widget build(BuildContext context) {
    // 判断平台,仅在鸿蒙时使用 PlatformView
    if (Theme.of(context).platform == TargetPlatform.ohos) {
      return Column(
        children: [
          SizedBox(height: 50,),
          SizedBox(width: double.maxFinite,height: 200,
            child: BuildOhosView(),
          ),
          ElevatedButton(
              child: const Text('Press Me'),
              onPressed: () {
                _sendMessageToNative('fromFlutter','aaa');
              }
          )
        ],
      );
    }

    // 其他平台使用 Flutter 实现的占位控件
    return Container(
      color: Colors.grey[300],
      child: const Center(child: Text('非鸿蒙平台')),
    );
  }

  Widget BuildOhosView(){
    return const OhosView(
      viewType: 'com.xxx.xxx.ohos/customUnityView',  // 必须与鸿蒙端注册的 viewType 一致
      creationParams: {'initialText': 'Hello from Flutter'},
      creationParamsCodec: StandardMessageCodec(),
    );
  }

  void _onResume(){
    _sendMessageToNative('onResume','');
  }

  void _onPause(){
    _sendMessageToNative('onPause','');
  }

  void _onDestroy(){
    _sendMessageToNative('onDestroy','');
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    _onDestroy();
    super.dispose();
  }
  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    super.didChangeAppLifecycleState(state);
    switch (state) {
      case AppLifecycleState.resumed:
      // 应用从后台返回前台,或应用首次启动时可见 [citation:1][citation:4]
        print('应用进入前台');
        this._onResume();
        break;
      case AppLifecycleState.paused:
      // 应用进入后台,用户不可见 [citation:4][citation:7]
        print('应用进入后台');
        this._onPause();
        break;
      case AppLifecycleState.inactive:
      // 应用处于非活跃状态,例如来了电话、或系统弹窗覆盖时 [citation:2][citation:3]
        print('应用处于非活跃状态');
        break;
      case AppLifecycleState.hidden:
      // 应用的所有视图都不可见 (Flutter 3.13.0 之后新增) [citation:2]
        print('应用被隐藏');
        break;
      case AppLifecycleState.detached:
      // 应用已完全脱离引擎,通常是在销毁前 [citation:2][citation:3]
        print('应用已脱离');
        break;
    }
  }
}

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐