一、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工程出现的多引擎模式问题,国内主要有两种解决方案,一种是字节跳动的修改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页面,如下图所示效果。
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插件分为平台和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!
相关文章
- 64M VPS建站:怎样选择合适的域名和SSL证书? 2025-06-10
- 64M VPS建站:怎样优化以提高网站加载速度? 2025-06-10
- 64M VPS建站:是否适合初学者操作和管理? 2025-06-10
- ASP.NET自助建站系统中的用户注册和登录功能定制方法 2025-06-10
- ASP.NET自助建站系统的域名绑定与解析教程 2025-06-10
- 2025-07-10 怎样使用阿里云的安全工具进行服务器漏洞扫描和修复?
- 2025-07-10 怎样使用命令行工具优化Linux云服务器的Ping性能?
- 2025-07-10 怎样使用Xshell连接华为云服务器,实现高效远程管理?
- 2025-07-10 怎样利用云服务器D盘搭建稳定、高效的网站托管环境?
- 2025-07-10 怎样使用阿里云的安全组功能来增强服务器防火墙的安全性?
快网idc优惠网
QQ交流群
-
2025-05-27 97
-
2025-05-27 24
-
2025-06-04 100
-
2025-05-29 28
-
Ubuntu 20.04服务器安装配置phpMyAdmin教程
2025-05-25 64