HarmonyOS NEXT-Flutter混合开发之鸿蒙-代码实践

在 Flutter 三端分离模式下完成纯血鸿蒙混入的过程中,虽然官方文档提供了一定的指导,但实际操作中可能会遇到一些坑。以下是我在适配过程中的一些经验总结,供各位开发者参考 😄 如果有帮助点个赞。

在混入过程中是基于咸鱼团队 flutter_boost(这里不讨论和其他方案的差别) 和自定义 FlutterPlugin 实现的。

主要涉及内容:

环境搭建

Flutter module 创建

Futter 引入 flutter_boost

Harmony 引入 flutter_boost

Flutter 与鸿蒙侧通信

Flutter 调用鸿蒙原生

环境搭建

Fluter 环境

准备支持鸿蒙的 Flutter 开发环境,flutter_fluter 仓库基于 Flutter SDK 对于OpenHarmony平台的兼容拓展,可支持 IDE 或者终端使用 Flutter Tools 指令编译和构建 OpenHarmony 应用程序。

Harmonyos NEXT 环境

不再赘述,上链接。

Flutter module 创建

创建 Fluter 项目

flutter create -t module --org xyz.zhousg demo_fluter

1

打包 Fluter 项目

flutter build har --debug

1

Fluter 引入 flutter_boost

安装依赖

dependencies:

flutter:

sdk: flutter

# The following adds the Cupertino Icons font to your application.

# Use with the CupertinoIcons class for iOS style icons.

cupertino_icons: ^1.0.2

fl_chart: ^0.62.0

flutter_boost:

+ git:

+ url: 'https://github.com/alibaba/flutter_boost.git'

+ ref: '4.6.5'

1

2

3

4

5

6

7

8

9

10

11

12

配置路由表

import 'package:flutter/cupertino.dart';

import 'package:flutter/material.dart';

import 'package:flutter_boost/flutter_boost.dart';

// 1. 创建一个自定义的Binding,继承和with的关系如下,里面什么都不用写

class CustomFlutterBinding extends WidgetsFlutterBinding

with BoostFlutterBinding {}

void main() {

// 2. 这里的CustomFlutterBinding调用务必不可缺少,用于控制Boost状态的resume和pause

CustomFlutterBinding();

runApp(const MyApp());

}

class MyApp extends StatefulWidget {

const MyApp({super.key});

@override

State<MyApp> createState() => _MyAppState();

}

class _MyAppState extends State<MyApp> {

// 3. 路由表

Map<String, FlutterBoostRouteFactory> routerMap = {

'SettingsPage': (settings, isContainerPage, uniqueId) {

return CupertinoPageRoute(

settings: settings,

builder: (BuildContext ctx) {

return const Placeholder();

},

);

},

'DeviceStoragePage': (settings, isContainerPage, uniqueId) {

return CupertinoPageRoute(

settings: settings,

builder: (BuildContext ctx) {

return const Placeholder();

},

);

},

'AboutPage': (settings, isContainerPage, uniqueId) {

return CupertinoPageRoute(

settings: settings,

builder: (BuildContext ctx) {

return const Placeholder();

},

);

},

'/': (settings, isContainerPage, uniqueId) {

return CupertinoPageRoute(

settings: settings,

builder: (BuildContext ctx) {

return const Placeholder();

},

);

},

};

// 路由工厂函数

Route<dynamic> routeFactory(

RouteSettings settings, bool isContainerPage, String? uniqueId) {

FlutterBoostRouteFactory? fn = routerMap[settings.name];

if (fn == null) {

throw FlutterError(

'Route "${settings.toString()}" is not defined in routerMap.');

}

return fn(settings, isContainerPage, uniqueId)!;

}

@override

Widget build(BuildContext context) {

// flutter_boost 接管

return FlutterBoostApp(

routeFactory,

// Flutter 侧直接预览需要,需要使用 Deveco Studio 导入 .ohos 项目进行自动签名预览至鸿蒙设备

initialRoute: 'SettingsPage',

appBuilder: (home) {

return MaterialApp(

builder: (context, child) => home,

);

},

);

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

Harmony 引入 flutter_boost

这里使用的是 router + FlutterPage 方式展示 Flutter 界面, Navigation 后续再说吧~

a. 先打包 Fluter 项目,会生成三个产物

.ohos

|--har

|-- fluter_boost.har

|-- fluter_module.har

|-- fluter.har

1

2

3

4

5

b. 在鸿蒙项目中,引入依赖

oh-package.json5

"dependencies": {

"@ohos/flutter_module": "file:../demo_flutter/.ohos/har/flutter_module.har",

// 下面两个依赖我直接拷贝到了 libs 下

"@ohos/flutter_ohos": "file:./libs/flutter.har",

"flutter_boost": "file:./libs/flutter_boost.har"

},

"overrides": {

"@ohos/flutter_ohos": "file:./libs/flutter.har",

"flutter_boost": "file:./libs/flutter_boost.har"

},

1

2

3

4

5

6

7

8

9

10

c. 初始化 flutter_boost

entryAbility.ets

import { AbilityConstant, ConfigurationConstant, UIAbility, Want } from **********';

import { router } from **********';

import { FlutterManager } from '@ohos/flutter_ohos';

import { FlutterBoostDelegate, FlutterBoostRouteOptions, FlutterBoost, FlutterBoostSetupOptionsBuilder } from 'flutter_boost';

import { GeneratedPluginRegistrant } from '@ohos/flutter_module';

export default class EntryAbility extends UIAbility implements FlutterBoostDelegate{

pushNativeRoute(options: FlutterBoostRouteOptions): void {

// throw new Error('Method not implemented.');

}

pushFlutterRoute(options: FlutterBoostRouteOptions,): void {

// throw new Error('Method not implemented.');

}

popRoute(options: FlutterBoostRouteOptions): boolean {

// throw new Error('Method not implemented.');

router.back()

return true

}

async onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {

FlutterManager.getInstance().pushUIAbility(this);

}

onDestroy(): void {

FlutterManager.getInstance().popUIAbility(this);

}

onWindowStageCreate(windowStage: window.WindowStage): void {

// Flutter bind in UIAbility

FlutterManager.getInstance().pushWindowStage(this, windowStage);

// Initial FlutterBoost

const optionsBuilder: FlutterBoostSetupOptionsBuilder = new FlutterBoostSetupOptionsBuilder()

FlutterBoost.getInstance().setup(this, this.context, (engine) => {

GeneratedPluginRegistrant.registerWith(engine)

}, optionsBuilder.build())

windowStage.loadContent('pages/Index');

}

onWindowStageDestroy(): void {

FlutterManager.getInstance().popWindowStage(this);

}

onForeground(): void {

logger.info('Ability onForeground');

}

onBackground(): void {

logger.info('Ability onBackground');

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

这里部分代码省略了 ~ pushNativeRoute pushFlutterRoute popRoute 具体实现还是参考官方仓库

d. Flutter容器与跳转

FlutterPage 承载Flutter的页面

pages/FluterPage.ets

import { FlutterEntry, FlutterPage, FlutterView} from '@ohos/flutter_ohos';

import { FlutterBoost, FlutterBoostEntry } from 'flutter_boost';

import { router } from **********';

@Entry

@Component

struct SettingsPage {

private flutterEntry?: FlutterEntry;

private flutterView?: FlutterView

aboutToAppear() {

this.flutterEntry = new FlutterBoostEntry(getContext(this), router.getParams())

this.flutterEntry?.aboutToAppear()

this.flutterView = this.flutterEntry?.getFlutterView()

}

aboutToDisappear() {

this.flutterEntry?.aboutToDisappear()

}

onPageShow() {

this.flutterEntry?.onPageShow()

}

onPageHide() {

this.flutterEntry?.onPageHide()

}

onBackPress(): boolean | void {

FlutterBoost.getInstance()

.getPlugin()?.onBackPressed();

return true;

}

build() {

Column() {

FlutterPage({ viewId: this.flutterView?.getId() })

.width('100%')

.height('100%')

}

.width('100%')

.height('100%')

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

在 Index 中的跳转

pages/Index.ets

// uri Flutter Module 中的路由表 KEY params 是传参

router.pushUrl({ url: 'pages/FlutterPage', params: { uri: 'DeviceStoragePage', params: {} } })

router.pushUrl({ url: 'pages/FlutterPage', params: { uri: 'SettingPage', params: {} } })

router.pushUrl({ url: 'pages/FlutterPage', params: { uri: 'AboutPage', params: {} } })

1

2

3

4

Flutter 与鸿蒙侧通信

Flutter侧

TextButton(

onPressed: () {

Map<String, String> data = {'name': 'jack'};

BoostChannel.instance.sendEventToNative('updateUser', data);

},

child: const Text('发送消息'),

),

1

2

3

4

5

6

7

鸿蒙侧

pages/FlutterPage.ets

aboutToAppear() {

this.flutterEntry = new FlutterBoostEntry(getContext(this), router.getParams())

this.flutterEntry?.aboutToAppear()

this.flutterView = this.flutterEntry?.getFlutterView()

const plugin = FlutterBoost.getInstance()

.getPlugin()

if (plugin) {

// 通信

plugin.addEventListener('updateUser', {

onEvent: (key, args) => {

// logger.debug(`事件名称 ${key}`, JSON.stringify(args))

promptAction.showToast({ message: 'Flutter Data: ' + JSON.stringify(args) })

}

})

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

Flutter 调用鸿蒙原生

Flutter侧

final _platform = const MethodChannel('xyz.zhousg.interview_success_project');

1

TextButton(

onPressed: () {

_platform.invokeMethod('openCamera').then(

(value) => setState(() {

// value 鸿蒙侧回传数据

}),

);

},

child: Text('打开相机),

),

1

2

3

4

5

6

7

8

9

10

鸿蒙侧

定义 Flutter 插件

NativePlugin.ets

import { FlutterPlugin, FlutterPluginBinding, MethodChannel, MethodResult } from '@ohos/flutter_ohos';

export class NativePlugin implements FlutterPlugin {

private channel?: MethodChannel;

getUniqueClassName(): string {

return 'CameraPlugin'

}

onAttachedToEngine(binding: FlutterPluginBinding): void {

this.channel = new MethodChannel(binding.getBinaryMessenger(), 'xyz.zhousg.interview_success_project')

this.channel.setMethodCallHandler({

onMethodCall: (call, result) => {

switch (call.method) {

case "openCamera":

this.openCamera(result)

break;

default:

result.notImplemented()

break;

}

}

})

}

onDetachedFromEngine(binding: FlutterPluginBinding): void {

this.channel?.setMethodCallHandler(null);

}

// native api

openCamera (result: MethodResult) {

// 回传数据给 Flutter

result.success('http://test.png')

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

注册插件

entryAbility.ets

// Initial FlutterBoost

const optionsBuilder: FlutterBoostSetupOptionsBuilder = new FlutterBoostSetupOptionsBuilder()

FlutterBoost.getInstance().setup(this, this.context, (engine) => {

GeneratedPluginRegistrant.registerWith(engine)

// 打开相机

engine.getPlugins()?.add(new NativePlugin())

}, optionsBuilder.build())

1

2

3

4

5

6

7

总结

使用 flutter_boost 开发 Flutter混合项目,在鸿蒙这边和混合 Web 组件进行混合开发很相似,搞定Flutter页面栈+鸿蒙页面栈跳转,搞定数据通信和原生调用,基本可以满足一般的开发,以上这里仅供参考哈~

————————————————

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/zsgzsgzsgzsgzsg/article/details/146200522

全部评论

相关推荐

评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务