Foreword
"Flutter a mixed-use development theme," we introduced the hybrid development solutions Flutter official offer, but there are some problems does not solve, such as a page overlay native and Flutter Jump Flutter Engine problems due to repeated surge create and lead to memory , Flutter use global variables in each individual page can not be shared problems, iOS platform problem of memory leaks and so on, the official did not spend too much time on developing hybrid solutions to improve optimization.
Many domestic manufacturers have begun to study in the last year Flutter, and completed the integration in existing projects, where Ali leisure fish earlier research team and investment effort is relatively large, busy fish APP in 2018, has been integrated Flutter, and initially use in the product details page opens up, pre-free fish in order to solve the problem of mixed-use development has developed a plug-in hybrid development hybridstackmanager, open-source project addresses https://github.com/alibaba-flutter/hybridstackmanager , a feature of the program is invasive, we need to modify the source code Flutter framework, and there are certain limitations in complex page scenario, therefore, free fish next team developed a new generation hybrid technology development program FlutterBoost, and open source in early March this year.
FlutterBoost Introduction
Click to view the free fish team article for details FlutterBoost
https://mp.weixin.qq.com/s/v-wwruadJntX1n-YuMPC7g
FlutterBoost integration
Since FlutterBoost packaged in a plug-in, so the integration is very simple, requiring only a small amount of code for access to the works. Below a Demo project as an example to learn more about access.
Examples of projects
We have a native Android project FBDemo
, needs-based project to introduce Flutter develop new page, we can create FBDemo same directory a Flutter module project, named flutter_boost_module
the Flutter module project to introduce integrated into the native project, integrated by reference " Flutter a mixed-use development topics. " Then we can develop new page in the Flutter Flutter module project. Here integrated manner step by step instructions FlutterBoost
Flutter module integration projects FlutterBoost
In flutter_boost_module
add-dependent plug-in configuration file pubspec.yaml project
dependencies:
flutter_boost: ^0.0.411
After the configuration execute flutter packages get
command to download the plug-ins rely on locally.
Andrews native integration project FlutterBoost
After Flutter module project introduced FlutterBoost plug-in synchronization native Android studio project, the synchronization is complete project is structured as follows
Then we can introduce FlutterBoost Android project code, add the following items depend on build.gradle under the app directory
dependencies {
...
implementation project(':flutter_boost')
}
Flutter module project uses FlutterBoost
Suppose we use Flutter create two pages Widget: FirstPage
and SecondPage
.
First, we need to run rootWidget in the main method of registering these two pages.
@override
void initState() {
super.initState();
FlutterBoost.singleton.registerPageBuilders({
'flutterbus://flutterFirstPage': (pageName, params, _) {
print("first flutterPage params:$params");
...
return FirstPage();
},
'flutterbus://flutterSecondPage': (pageName, params, _) {
print("second flutterPage params:$params");
...
return SecondPage();
},
});
FlutterBoost.handleOnStartPage();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Boost example',
builder: FlutterBoost.init(),
home: Container());
}
Andrews native projects use FlutterBoost
Flutter engine load and initialize FlutterBoostPlugin
First, according to our Application examples FlutterBoost original project needs to inherit FlutterApplication
, in fact, this is not necessary, FlutterApplication
mainly in the onCreate method to initialize load flutter.so library, which we can operate, where appropriate, to add to his own.
Secondly, FlutterBoost of example in the onCreate method Application initialization FlutterBoostPlugin custom, this we can be extracted into a separate class.
Based on the above two points I implemented a tool to initialize the class used to perform FlutterBoost
public class FlutterMediator {
public static void init(final Application app) {
//此处必须启动初始化,主要是载入Flutter引擎文件
FlutterMain.startInitialization(app);
FlutterBoostPlugin.init(new IPlatform() {
@Override
public Application getApplication() {
return app;
}
@Override
public Activity getMainActivity() {
return MainActivity.sRef.get();
}
@Override
public boolean isDebug() {
return true;
}
@Override
public boolean startActivity(Context context, String url, int requestCode) {
Debuger.log("startActivity url="+url);
return PageRouter.openPageByUrl(context,url,requestCode);
}
@Override
public Map getSettings() {
return null;
}
});
}
}
In this way, onCreate method in our custom Application of native projects only need to call the FlutterMediator.init(this);
method to complete the initialization of the FlutterBoost. Which MainActivity
should always existed bottom of the stack of Activity, generally our home page.
Native container corresponding pages Flutter
After FlutterBoost initialization is complete, for Flutter in FirstPage
and SecondPage
we need to create a page in the corresponding native Native container that FlutterBoost defined Container, can be Activity can also be a Fragment, here we use the Activity implementation,
// Flutter中FirstPage对应的Native container
public class FlutterFirstPageActivity extends BoostFlutterActivity {
private int id = 0;
private String name;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
if(intent != null) {
String url = intent.getStringExtra("url");
Map map = UrlUtil.parseParams(url);
id = Integer.parseInt(map.get("id").toString());
name = map.get("name").toString();
}
}
@Override
public String getContainerName() {
return PageRouter.FLUTTER_FIRST_PAGE_URL;
}
@Override
public Map getContainerParams() {
Map map = new HashMap();
map.put("id", id);
map.put("name", name);
return map;
}
@Override
public void onRegisterPlugins(PluginRegistry registry) {
GeneratedPluginRegistrant.registerWith(registry);
}
}
FlutterBoost have achieved our good Activity type container BoostFlutterActivity
, which implements the IFlutterViewContainer
interface, we custom container only needs to inherit the Activity and implement three methods to which
-
getContainerName
That is the name of the container, and a registration layer Flutter corresponding PageBuilder; -
getContainerParams
Parameters passed to the need for the container layer corresponding to the Widget Flutter, page jump parameter passed to the received page is here Flutter process, needs to pack data into the Map; -
onRegisterPlugins
Is a plug-registered for the page;
Jump routing page
We saw above FlutterBoostPlugin line of code in the initialization PageRouter.openPageByUrl(context,url,requestCode);
code of this code is used to process the page Flutter opening operation according to another page url. PageRouter
Routing is a page of our native layer defined
public class PageRouter {
public static final String NATIVE_FIRST_PAGE_URL = "flutterbus://nativeFirstPage";
public static final String NATIVE_SECOND_PAGE_URL = "flutterbus://nativeSecondPage";
public static final String FLUTTER_FIRST_PAGE_URL = "flutterbus://flutterFirstPage";
public static final String FLUTTER_SECOND_PAGE_URL = "flutterbus://flutterSecondPage";
public static boolean openPageByUrl(Context context, String url) {
return openPageByUrl(context, url, 0);
}
public static boolean openPageByUrl(Context context, String url, int requestCode) {
try {
Intent intent;
if (url.startsWith(NATIVE_FIRST_PAGE_URL)) {
intent = new Intent(context, FirstNativeActivity.class);
intent.putExtra("url", url);
context.startActivity(intent);
return true;
} else if (url.startsWith(NATIVE_SECOND_PAGE_URL)) {
intent = new Intent(context, SecondNativeActivity.class);
intent.putExtra("url", url);
if(context instanceof Activity) {
((Activity)context).startActivityForResult(intent, requestCode);
}
return true;
} else if(url.startsWith(FLUTTER_FIRST_PAGE_URL)) {
intent = new Intent(context, FlutterFirstPageActivity.class);
intent.putExtra("url", url);
context.startActivity(intent);
return true;
} else if (url.startsWith(FLUTTER_SECOND_PAGE_URL)) {
intent = new Intent(context, FlutterSecondPageActivity.class);
intent.putExtra("url", url);
if(context instanceof Activity) {
((Activity)context).startActivityForResult(intent, requestCode);
}
return true;
} else {
return false;
}
} catch (Throwable t) {
return false;
}
}
}
Flutter page and page jump Native
After more than a good preparation and page routing class definition, we can in the corresponding Flutter layer and layer calling Native method to execute the jump page url page jump operations, you can either jump Native Native page page, you can also Native Flutter page page jump, jump both Native Flutter page page, the page may jump Flutter Flutter page, the following specific examples
Native page jump Flutter page
Native Flutter page jump actually opens a page corresponding to a page Flutter Native container, we jump operation may be performed according to the route, such as the MainActivity
jump to the Flutter FirstWidget
page, the code to get a
PageRouter.openPageByUrl(this, PageRouter.FLUTTER_FIRST_PAGE_URL+"?id=123&name=bruce");
Examples of corresponding url parameters with the code above, when you do not need to pass a lot of parameters, where just an Flutter pass parameters to the page, that is, the values of id and name Flutter page spread use, specifically how we, we need to use the corresponding Flutter Native container FlutterFirstPageActivity
parsing the url parameter, and then packaged into Flutter end Map passed to cover by the following methods PlatformChannel
@Override
public Map getContainerParams() {
Map map = new HashMap();
map.put("id", id);
map.put("name", name);
return map;
}
Flutter page jump Native page
We only need to use the method FlutterBoost Flutter end of the jump can be provided, for example, I need the FirstWidget
jump to the FirstNativeActivity
page that corresponds to url " flutterbus: // nativeFirstPage ", we can execute the following code
FlutterBoost.singleton.openPage("flutterbus://nativeFirstPage", {
"query": {"description": "大家好,我来自First Flutter页面!!!!!!!!"}
});
Wherein the query is a parameter value corresponding to the next page to be transmitted, you need not or may not pass.
Flutter Flutter page jump page
This fact, there are two ways, if FlutterBoost defined page jumps, then you need to use the following method
FlutterBoost.singleton.openPage("flutterbus://flutterSecondPage", {});
Jump recommended integrated FlutterBoost With FlutterBoost defined pages to use openPage way.
In fact, you can also use the Navigator to jump Flutter in, as follows
Navigator.of(context).push(MaterialPageRoute(builder: (context){
return SecondPage(enterType: 1,);
}));
If both jump method mix will be some problems when the page is returned, because FlutterBoost provides a method to close the current page FlutterBoost.singleton.closePageForContext(context);
, and use the Navigator to jump if the method does not work, so we defined in the Widget page enterType to distinguish FlutterBoost default mode jump, jump if the Navigator Flutter Widget page, you need to pass enterType = 1, so that when returning the current page using a method for processing
void exitPage(BuildContext context) {
if (enterType == 0) {
FlutterBoost.singleton.closePageForContext(context);
} else {
Navigator.pop(context);
}
}
Page jump return value
Flutter is implemented in FlutterBoost Native Page page jump function and receiving the return value, the method is particularly
FlutterBoost.singleton.openPage(
"flutterbus://nativeSecondPage",
{
"query": {"requestCode":1000, "type": "second"}
},
resultHandler: (String key, Map<dynamic, dynamic> result) {
print("==============> key: $key, result: $result");
});
resultHandler parameters openPage method is to receive the return value of the callback function, but the bug has been tested existing method, there are two main rehabilitation program
A rehabilitation program
The program uses a conventional jump Activity and returns the result of the method, i.e. Flutter Native specified page jump jump manner using the following method openPageByUrl of native PageRouter
if(context instanceof Activity) {
((Activity)context).startActivityForResult(new Intent(context, NativePageActivity.class), requestCode);
}
The jump method FlutterBoost whole process has been achieved returns the result of processing, but you need to modify the following two bug to normal use.
1, type conversion error
flutter_boost.dart Method onPageResult
resultData parameter is Map<String, dynamic>
type, and by PlatformChannel pass over the parsed data type Map<dynamic, dynamic>
, it will be reported type conversion error, the console will print the phrase
... E/FlutterBoost#: onNativePageResult call error
To solve this problem simply onPageResult
resultData parameter type method Map<String, dynamic>
was changed Map<dynamic, dynamic>
to;
2, the error correction method corresponding to the key
For 1 issue, modify the source code Flutter_Boost after the above error is not reported, but the result of receiving a callback method or come to the stage, and upon inspection found that there is another bug, through our openPage
open page methods, will eventually callback function PageResultMediator
class setPageResultHandler
preservation method into a Map<String,PageResultHandler>
subject _handlers
, the PageResultMediator
class implements the following
typedef void PageResultHandler(String key , Map<dynamic,dynamic> result);
typedef VoidCallback = void Function();
class PageResultMediator{
Map<String,PageResultHandler> _handlers = Map();
void onPageResult(String key , Map<dynamic,dynamic> resultData){
if(key == null) return;
Logger.log("did receive page result $resultData for page key $key");
if(_handlers.containsKey(key)){
_handlers[key](key,resultData);
_handlers.remove(key);
}
}
VoidCallback setPageResultHandler(String key, PageResultHandler handler){
if(key == null || handler == null) return (){};
_handlers[key] = handler;
return (){
_handlers.remove(key);
};
}
}
Map of the key that is we jump page corresponding to url, i.e., in the code above flutterbus://nativeSecondPage
, after returning the processed data into the key by the method PlatformChannel native container page is Native corresponding uniqueId, specific code as follows
So, according to this last Flutter uniqueId callback method is not found before, and therefore did not come to the callback function. Then the source code of a simple Flutter_Boost been modified, the previous page url placed into a onResult
process Result
parameter, and then out of the box, as the modified code
The code block is located ContainerRecord.java class
@Override
public void onResult(Map Result) {
Map result = (Map) Result.get("result");
String key = result.get("currentUrl").toString();
NavigationService.onNativePageResult(
genResult("onNativePageResult"),
mUniqueId,
key,
Result,
mContainer.getContainerParams()
);
}
Repair Option II
The program uses the results of treatment FlutterBoost returns implemented without having to use native jump page acquisition result of the ordinary jump Jump Activity page, i.e.,
context.startActivity(new Intent(context, NativePageActivity.class));
The program also has a corresponding implementation FlutterBoost, but still there are two bug.
1, type conversion error
The same problem and a scheme of a problem, a modification to the reference program.
2, needResult parameters in the native client can not get the problem
When we found by studying FlutterBoost source, in the end Flutter jump page if passed in resultHandler parameters, it will be passed to add params native layer as a parameter to true needResult, open the requested page in the native layer processing will determining whether the first result data, the following code block
The class code is FlutterBoostPlugin.java
public static void openPage(Context context, String url, final Map params, int requestCode) {
...
//Handling page result.
if (needResult(params)){
sInstance.mMediator.setHandler(url, new PageResultHandler() {
@Override
public void onResult(String key, Map resultData) {
NavigationService.onNativePageResult(new MessageResult<Boolean>() {
...
},"no use",key,resultData,params);
}
});
}
sInstance.mPlatform.startActivity(ctx, concatUrl(url, params), requestCode);
}
private Boolean needResult(Map params){
if(params == null) return false;
final String key = "needResult";
if(params.containsKey(key)){
if(params.get(key) instanceof Boolean){
return (Boolean) params.get(key);
}
}
return false;
}
In the if statement of the method will detect whether it contains needResult params parameter and its value is true by needResult method, if true it will cache the results of a callback to mMediator object, but in fact this is not going to get to needResult parameters, because before the openPage incoming call has Flutter params made a deal, as follows
The method is in a class NavigationService_openPage.java
private boolean onCall(MessageResult<Boolean> result,String pageName,Map params,Boolean animated){
Map pageParams = null;
int requestCode = 0;
if(params != null && params.get("query") != null) {
pageParams = (Map)params.get("query");
}
if(params != null && params.get("requestCode") != null) {
requestCode = (int)params.get("requestCode");
}
FlutterBoostPlugin.openPage(null,pageName,pageParams,requestCode);
result.success(true);
return true;
}
By the above code we will find pageParams just removed from the params passed in the query parameters, while ignoring the inside needResult parameters, so we can not find needResult parameters from params in openPage method.
After the discovery of the problem, we only need to add a parameter in openPage method boolean needResult
to detect the presence or absence needResult parameters in onCall method, exists and is true, then it is passed openPage can be.
Fix the problem, then jump Native page if the results are returned to the Flutter page of it, just do the following to return to the page in the Native
Map map = new HashMap();
map.put("value", "bruce");
FlutterBoostPlugin.onPageResult(PageRouter.NATIVE_PAGE_URL, map);
After more than two solutions corresponding bug fixes, Flutter page jump Native page and get the return value of the function can be used normally, and look forward to free fish team can fix the problem in time.
For Native page jump Flutter page and return the result of functional data, currently Flutter_Boost has not been achieved, by reading the source code found in the relevant code, but not perfect, but also look forward to free the fish faster team improve this part of the function, after all page Jump return data that we often encounter scene.
Written in the last
These are integrated in the existing Flutter_Boost Andrews native project method steps, overall integration and use is still relatively simple, free fish team as much as possible to avoid the intrusion of the original code when integrating. Due to limited space, the text code stickers is not too perfect, if necessary, students can obtain a copy of the demo by micro letter public message number.
Watch "Flutter Programming Guide" micro-channel public number, the number public reply to the message interface "widget" "dart", "storage", "plug-ins" such as more accurate information, but also respond to "mixed-use development," acquisition of Ali, Tencent and other domestic manufacturers more Selected articles and more mixed development practices.
Reproduced in: https: //www.jianshu.com/p/4eee4ddb2b6a