Flutter Boost 混合开发框架

2025-05-29 0 71

一、flutter boost简介

众所周知,flutter是一个由c++实现的flutter engine和由dart实现的framework组成的跨平台技术框架。其中,flutter engine负责线程管理、dart vm状态管理以及dart代码加载等工作,而dart代码所实现的framework则负责上层业务开发,如flutter提供的组件等概念就是framework的范畴。

随着flutter的发展,国内越来越多的app开始接入flutter。为了降低风险,大部分app采用渐进式方式引入flutter,在app里选几个页面用flutter来编写,但都碰到了相同的问题,在原生页面和flutter页面共存的情况下,如何管理路由,以及原生页面与flutter页面之间的切换和通信都是混合开发中需要解决的问题。然而,官方没有提供明确的解决方案,只是在混合开发时,官方建议开发者,应该使用同一个引擎支持多窗口绘制的能力,至少在逻辑上做到flutterviewcontroller是共享同一个引擎里面的资源。换句话说,官方希望所有的绘制窗口共享同一个主isolate,而不是出现多个主isolate的情况。不过,对于现在已经出现的多引擎模式问题,flutter官方也没有提供好的解决方案。除了内存消耗严重外,多引擎模式还会带来如下一些问题

  • 冗余资源问题。多引擎模式下每个引擎的isolate是相互独立的,虽然在逻辑上这并没有什么坏处,但是每个引擎底层都维护了一套图片缓存等比较消耗内存的对象,因此设备的内存消耗是非常严重的。
  • 插件注册问题。在flutter插件中,消息传递需要依赖messenger,而messenger是由flutterviewcontroller去实现的。如果一个应用中同时存在多个flutterviewcontroller,那么插件的注册和通信将会变得混乱且难以维护。
  • flutter组件和原生页面的差异化问题。通常,flutter页面是由组件构成的,原生页面则是由viewcontroller或者activity构成的。逻辑上来说,我们希望消除flutter页面与原生页面的差异,否则在进行页面埋点和其它一些操作时增加一些额外的工作量。
  • 增加页面通信的复杂度。如果所有的dart代码都运行在同一个引擎实例中,那么它们会共享同一个isolate,可以用统一的框架完成组件之间的通信,但是如果存在多个引擎实例会让isolate的管理变得更加复杂。

如果不解决多引擎问题,那么混合项目的导航栈如下图所示。

Flutter Boost 混合开发框架

目前,对于原生工程混编flutter工程出现的多引擎模式问题,国内主要有两种解决方案,一种是字节跳动的修改flutter engine源码方案,另一种是闲鱼开源的flutterboost。由于字节跳动的混合开发的方案没有开源,所以现在能使用的就剩下flutterboost方案。

flutterboost是闲鱼技术团队开发的一个可复用页面的插件,旨在把flutter容器做成类似于浏览器的加载方案。为此,闲鱼技术团队为希望flutterboost能完成如下的基本功能:

  • 可复用的通用型混合开发方案。
  • 支持更加复杂的混合模式,比如支持tab切换的场景。
  • 无侵入性方案,使用时不再依赖修改flutter的方案。
  • 支持对页面生命周期进行统一的管理。
  • 具有统一明确的设计概念。

并且,最近flutter boost升级了3.0版本,并带来了如下的一些更新:

  • 不侵入引擎,兼容flutter的各种版本,flutter sdk的升级不需要再升级flutterboost,极大降低升级成本。
  • 不区分androidx和support分支。
  • 简化架构和接口,和flutterboost2.0比,代码减少了一半。
  • 双端统一,包括接口和设计上的统一。
  • 支持打开flutter页面,不再打开容器场景。
  • 页面生命周期变化通知更方便业务使用。
  • 解决了2.0中的遗留问题,例如,fragment接入困难、页面关闭后不能传递数据、dispose不执行,内存占用过高等。

二、flutter boost集成

在原生项目中集成flutter boost只需要将flutter boost看成是一个插件工程即可。和其他flutter插件的集成方式一样,使用flutterboost之前需要先添加依赖。使用android studio打开混合工程的flutter工程,在pubspec.yaml中添加flutterboost依赖插件,如下所示。

?

1

2

3

4
flutter_boost:

git:

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

ref: 'v3.0-hotfixes'

需要说明的是,此处的所依赖的flutterboost的版本与flutter的版本是对应的,如果不对应使用过程中会出现版本不匹配的错误。然后,使用flutter packages get命令将flutterboost插件拉取到本地。

2.1 android集成

使用android studio打开新建的原生android工程,在原生android工程的settings.gradle文件中添加如下代码。

?

1

2

3

4
setbinding(new binding([gradle: this]))

evaluate(new file(

settingsdir.parentfile,

'flutter_library/.android/include_flutter.groovy'))

然后,打开原生android工程app目录下的build.gradle文件,继续添加如下依赖脚本。

?

1

2

3

4
dependencies {

implementation project(':flutter_boost')

implementation project(':flutter')

}

重新编译构建原生android工程,如果没有任何错误则说明android成功了集成flutterboost。使用flutter boost 之前,需要先执行初始化。打开原生android工程,新建一个继承flutterapplication的application,然后在oncreate()方法中初始化flutterboost,代码如下。

?

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
public class myapplication extends flutterapplication {

@override

public void oncreate() {

super.oncreate();

flutterboost.instance().setup(this, new flutterboostdelegate() {

@override

public void pushnativeroute(string pagename, hashmap<string, string> arguments) {

intent intent = new intent(flutterboost.instance().currentactivity(), nativepageactivity.class);

flutterboost.instance().currentactivity().startactivity(intent);

}

@override

public void pushflutterroute(string pagename, hashmap<string, string> arguments) {

intent intent = new flutterboostactivity.cachedengineintentbuilder(flutterboostactivity.class, flutterboost.engine_id)

.backgroundmode(flutteractivitylaunchconfigs.backgroundmode.opaque)

.destroyenginewithactivity(false)

.url(pagename)

.urlparams(arguments)

.build(flutterboost.instance().currentactivity());

flutterboost.instance().currentactivity().startactivity(intent);

}

},engine->{

engine.getplugins();

} );

}

}

然后,打开原生android工程下的androidmanifest.xml文件,将application替换成自定义的myapplication,如下所示。

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23
<manifest xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:tools="http://schemas.android.com/tools"

package="com.idlefish.flutterboost.example">

<application

android:name="com.idlefish.flutterboost.example.myapplication"

android:label="flutter_boost_example"

android:icon="@mipmap/ic_launcher">

<activity

android:name="com.idlefish.flutterboost.containers.flutterboostactivity"

android:theme="@style/theme.appcompat"

android:configchanges="orientation|keyboardhidden|keyboard|screensize|locale|layoutdirection|fontscale|screenlayout|density"

android:hardwareaccelerated="true"

android:windowsoftinputmode="adjustresize" >

<meta-data android:name="io.flutter.embedding.android.splashscreendrawable" android:resource="@drawable/launch_background"/>

</activity>

<meta-data android:name="flutterembedding"

android:value="2">

</meta-data>

</application>

</manifest>

由于flutter boost 是以插件的方式集成到原生android项目的,所以我们可以在native 打开和关闭flutter模块的页面。

?

1

2
flutterboost.instance().open("flutterpage",params);

flutterboost.instance().close("uniqueid");

而flutter dart的使用如下。首先,我们可以在main.dart文件的程序入口main()方法中进行初始化。

?

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
void main() {

runapp(myapp());

}

class myapp extends statefulwidget {

@override

_myappstate createstate() => _myappstate();

}

class _myappstate extends state<myapp> {

static map<string, flutterboostroutefactory>

routermap = {

'/': (settings, uniqueid) {

return pageroutebuilder<dynamic>(

settings: settings, pagebuilder: (_, __, ___)

=> container());

},

'embedded': (settings, uniqueid) {

return pageroutebuilder<dynamic>(

settings: settings,

pagebuilder: (_, __, ___) =>

embeddedfirstroutewidget());

},

'presentflutterpage': (settings, uniqueid) {

return pageroutebuilder<dynamic>(

settings: settings,

pagebuilder: (_, __, ___) =>

flutterroutewidget(

params: settings.arguments,

uniqueid: uniqueid,

));

}};

route<dynamic> routefactory(routesettings settings, string uniqueid) {

flutterboostroutefactory func =routermap[settings.name];

if (func == null) {

return null;

}

return func(settings, uniqueid);

}

@override

void initstate() {

super.initstate();

}

@override

widget build(buildcontext context) {

return flutterboostapp(

routefactory

);

}

当然,还可以监听页面的生命周期,如下所示。

?

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

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107
class simplewidget extends statefulwidget {

final map params;

final string messages;

final string uniqueid;

const simplewidget(this.uniqueid, this.params, this.messages);

@override

_simplewidgetstate createstate() => _simplewidgetstate();

}

class _simplewidgetstate extends state<simplewidget>

with pagevisibilityobserver {

static const string _ktag = 'xlog';

@override

void didchangedependencies() {

super.didchangedependencies();

print('$_ktag#didchangedependencies, ${widget.uniqueid}, $this');

}

@override

void initstate() {

super.initstate();

pagevisibilitybinding.instance.addobserver(this, modalroute.of(context));

print('$_ktag#initstate, ${widget.uniqueid}, $this');

}

@override

void dispose() {

pagevisibilitybinding.instance.removeobserver(this);

print('$_ktag#dispose, ${widget.uniqueid}, $this');

super.dispose();

}

@override

void onforeground() {

print('$_ktag#onforeground, ${widget.uniqueid}, $this');

}

@override

void onbackground() {

print('$_ktag#onbackground, ${widget.uniqueid}, $this');

}

@override

void onappear(changereason reason) {

print('$_ktag#onappear, ${widget.uniqueid}, $reason, $this');

}

void ondisappear(changereason reason) {

print('$_ktag#ondisappear, ${widget.uniqueid}, $reason, $this');

}

@override

widget build(buildcontext context) {

return scaffold(

appbar: appbar(

title: text('tab_example'),

),

body: singlechildscrollview(

physics: bouncingscrollphysics(),

child: container(

child: column(

crossaxisalignment: crossaxisalignment.start,

children: <widget>[

container(

margin: const edgeinsets.only(top: 80.0),

child: text(

widget.messages,

style: textstyle(fontsize: 28.0, color: colors.blue),

),

alignment: alignmentdirectional.center,

),

container(

margin: const edgeinsets.only(top: 32.0),

child: text(

widget.uniqueid,

style: textstyle(fontsize: 22.0, color: colors.red),

),

alignment: alignmentdirectional.center,

),

inkwell(

child: container(

padding: const edgeinsets.all(8.0),

margin: const edgeinsets.all(30.0),

color: colors.yellow,

child: text(

'open flutter page',

style: textstyle(fontsize: 22.0, color: colors.black),

)),

ontap: () => boostnavigator.of().push("flutterpage",

arguments: <string, string>{'from': widget.uniqueid}),

)

container(

height: 300,

width: 200,

child: text(

'',

style: textstyle(fontsize: 22.0, color: colors.black),

),

)

],

))),

);

}

}

然后,运行项目,就可以从原生页面跳转到flutter页面,如下图所示效果。

Flutter Boost 混合开发框架

2.2 ios集成

和android的集成步骤一样,使用xcode打开原生ios工程,然后在ios的appdelegate文件中初始化flutter boost ,如下所示。

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15
@interface appdelegate ()

@end

@implementation appdelegate

- (bool)application:(uiapplication *)application didfinishlaunchingwithoptions:(nsdictionary *)launchoptions

{

myflutterboostdelegate* delegate=[[myflutterboostdelegate alloc ] init];

[[flutterboost instance] setup:application delegate:delegate callback:^(flutterengine *engine) {

} ];

return yes;

}

@end

下面是自定义的flutterboostdelegate的代码,如下所示。

?

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
@interface myflutterboostdelegate : nsobject<flutterboostdelegate>

@property (nonatomic,strong) uinavigationcontroller *navigationcontroller;

@end

@implementation myflutterboostdelegate

- (void) pushnativeroute:(fbcommonparams*) params{

bool animated = [params.arguments[@"animated"] boolvalue];

bool present= [params.arguments[@"present"] boolvalue];

uiviewcontrollerdemo *nvc = [[uiviewcontrollerdemo alloc] initwithnibname:@"uiviewcontrollerdemo" bundle:[nsbundle mainbundle]];

if(present){

[self.navigationcontroller presentviewcontroller:nvc animated:animated completion:^{

}];

}else{

[self.navigationcontroller pushviewcontroller:nvc animated:animated];

}

}

- (void) pushflutterroute:(fbcommonparams*)params {

flutterengine* engine = [[flutterboost instance ] getengine];

engine.viewcontroller = nil;

fbflutterviewcontainer *vc = fbflutterviewcontainer.new ;

[vc setname:params.pagename params:params.arguments];

bool animated = [params.arguments[@"animated"] boolvalue];

bool present= [params.arguments[@"present"] boolvalue];

if(present){

[self.navigationcontroller presentviewcontroller:vc animated:animated completion:^{

}];

}else{

[self.navigationcontroller pushviewcontroller:vc animated:animated];

}

}

- (void) poproute:(fbcommonparams*)params

result:(nsdictionary *)result{

fbflutterviewcontainer *vc = (id)self.navigationcontroller.presentedviewcontroller;

if([vc iskindofclass:fbflutterviewcontainer.class] && [vc.uniqueidstring isequal: params.uniqueid]){

[vc dismissviewcontrolleranimated:yes completion:^{}];

}else{

[self.navigationcontroller popviewcontrolleranimated:yes];

}

}

@end

如果要在原生ios代码中打开或关闭flutter页面,可以使用下面的方式。

?

1

2
[[flutterboost instance] open:@"flutterpage" arguments:@{@"animated":@(yes)} ];

[[flutterboost instance] open:@"secondstateful" arguments:@{@"present":@(yes)}];

三、flutter boost架构

对于混合工程来说,原生端和flutter端对于页面的定义是不一样的。对于原生端而言,页面通常指的是一个viewcontroller或者activity,而对于flutter来说,页面通常指的是flutter组件。flutterboost框架所要做的就是统一混合工程中页面的概念,或者说弱化flutter组件对应容器页面的概念。换句话说,当有一个原生页面存在的时候,flutteboost就能保证一定有一个对应的flutter的容器页面存在。

flutterboost框架其实就是由原生容器通过消息驱动flutter页面容器,从而达到原生容器与flutter容器同步的目的,而flutter渲染的内容是由原生容器去驱动的,下面是flutter boost 给的一个flutter boost 的架构示意图。

Flutter Boost 混合开发框架

可以看到,flutter boost插件分为平台和dart两端,中间通过message channel连接。平台侧提供了flutter引擎的配置和管理、native容器的创建/销毁、页面可见性变化通知,以及flutter页面的打开/关闭接口等。而dart侧除了提供类似原生navigator的页面导航接口的能力外,还负责flutter页面的路由管理。

总的来说,正是基于共享同一个引擎的方案,使得flutterboost框架有效的解决了多引擎的问题。简单来说,flutterboost在dart端引入了容器的概念,当存在多个flutter页面时,flutterboost不需要再用栈的结构去维护现有页面,而是使用扁平化键值对映射的形式去维护当前所有的页面,并且每个页面拥有一个唯一的id

四、flutterboost3.0更新

4.1 不入侵引擎

为了解决官方引擎复用引起的问题,flutterboost2.0拷贝了flutter引擎embedding层的一些代码进行改造,这使得后期的升级成本极高。而flutterboost3.0采用继承的方式扩展flutteractivity/flutterfragment等组件的能力,并且通过在适当时机给dart侧发送appisresumed消息解决引擎复用时生命周期事件错乱导致的页面卡死问题,并且,flutterboost 3.0 也兼容最新的官方发布的 flutter 2.0。

4.2 不区分androidx和support分支

flutterboost2.0通过自己实现flutteractivityandfragmentdelegate.host接口来扩展flutteractivity和flutterfragment的能力,而getlifecycle是必须实现的接口,这就导致对androidx的依赖。这也是为什么flutterboostview的实现没有被放入flutterboost3.0插件中的原因。而flutterboost3.0通过继承的方式扩展flutteractivity/flutterfragment的能力的额外收益就是,可以做到不依赖androidx。

4.3 双端设计统一,接口统一

很多flutter开发者只会一端,只会android 或者只会ios,但他需要接入双端,所以双端统一能降低他的 学习成本和接入成本。flutterboost3.0,在设计上 android和ios都做了对齐,特别接口上做到了参数级的对齐。

4.4 支持 【打开flutter页面不再打开容器】 场景

在flutter模块内部,flutter 页面跳转flutter 页面是可以不需要再打开flutter容器的,不打开容器,能节省内存开销。在flutterboost3.0上,打开容器和不打开容器的区别表现在用户接口上仅仅是withcontainer参数是否为true就好。

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21
inkwell(

child: container(

color: colors.yellow,

child: text(

'打开外部路由',

style: textstyle(fontsize: 22.0, color: colors.black),

)),

ontap: () => boostnavigator.of().push("flutterpage",

arguments: <string, string>{'from': widget.uniqueid}),

),

inkwell(

child: container(

color: colors.yellow,

child: text(

'打开内部路由',

style: textstyle(fontsize: 22.0, color: colors.black),

)),

ontap: () => boostnavigator.of().push("flutterpage",

withcontainer: true,

arguments: <string, string>{'from': widget.uniqueid}),

)

4.5 生命周期的精准通知

在flutterboost2.0上,每个页面都会收到页面生命周期通知,而flutterboost3.0只会通知页面可见性实际发生了变化的页面,接口也更符合flutter的设计。

4.6 其他issue

除了上面的一些特性外,flutter boost 3.0版本还解决了如下一些问题:

  • 页面关闭后参数的传递,之前只有ios支持,android不支持,目前在dart侧实现,ios 和android 都支持。
  • 解决了android 状态栏字体和颜色问题。
  • 解决了页面回退willpopscope不起作用问题。
  • 解决了不在栈顶的页面也收到生命周期回调的问题
  • 解决了多次setstate耗性能问题。
  • 提供了framgent 多种接入方式的demo,方便tab 场景的接入。
  • 生命周期的回调代码,可以用户代码里面with的方式接入,使用更简单。
  • 全面简化了,接入成本,包括 dart侧,android侧和ios
  • 丰富了demo,包含了基本场景,方便用户接入 和测试回归

到此这篇关于flutter boost 混合开发框架的文章就介绍到这了,更多相关flutter boost内容请搜索快网idc以前的文章或继续浏览下面的相关文章希望大家以后多多支持快网idc!,希望大家以后多多支持快网idc!

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。

快网idc优惠网 建站教程 Flutter Boost 混合开发框架 https://www.kuaiidc.com/89005.html

相关文章

发表评论
暂无评论