github:
https://github.com/alibaba/flutter_boost
demo:
https://github.com/alibaba/flutter_boost/tree/master/example
调用入口
simple_page_widgets.dart
///后面的参数会在native的IPlatform.startActivity方法回调中拼接到url的query部分。
///例如:sample://nativePage?aaa=bbb
onTap: () => FlutterBoost.singleton
.open("sample://nativePage", urlParams: {
"query": {"aaa": "bbb"}
}),
flutter调用native方法openPage,通过MethodChannel 调用openPage
flutter_boost.dart
Future<Map<dynamic,dynamic>> open(String url,{Map<dynamic,dynamic> urlParams,Map<dynamic,dynamic> exts}){
Map<dynamic, dynamic> properties = new Map<dynamic, dynamic>();
properties["url"] = url;
properties["urlParams"] = urlParams;
properties["exts"] = exts;
print("invokeMethod open native page "+ url);
return channel.invokeMethod<Map<dynamic,dynamic>>(
'openPage', properties);
}
FlutterBoostPlugin.java
class BoostMethodHandler implements MethodChannel.MethodCallHandler {
@Override
public void onMethodCall(MethodCall methodCall, final MethodChannel.Result result) {
FlutterViewContainerManager mManager = (FlutterViewContainerManager) FlutterBoost.instance().containerManager();
switch (methodCall.method) {
case "pageOnStart": {
Map<String, Object> pageInfo = new HashMap<>();
try {
IContainerRecord record = mManager.getCurrentTopRecord();
if (record == null) {
record = mManager.getLastGenerateRecord();
}
if (record != null) {
pageInfo.put("name", record.getContainer().getContainerUrl());
pageInfo.put("params", record.getContainer().getContainerUrlParams());
pageInfo.put("uniqueId", record.uniqueId());
}
result.success(pageInfo);
FlutterBoost.instance().setFlutterPostFrameCallTime(new Date().getTime());
} catch (Throwable t) {
result.error("no flutter page found!", t.getMessage(), t);
}
}
break;
case "openPage": {
try {
Map<String, Object> params = methodCall.argument("urlParams");
Map<String, Object> exts = methodCall.argument("exts");
String url = methodCall.argument("url");
Log.i("openPageByUrl","BoostMethodHandler openPage " + url);
mManager.openContainer(url, params, exts, new FlutterViewContainerManager.OnResult() {
@Override
public void onResult(Map<String, Object> rlt) {
if (result != null) {
result.success(rlt);
}
}
});
} catch (Throwable t) {
result.error("open page error", t.getMessage(), t);
}
}
break;
case "closePage": {
try {
String uniqueId = methodCall.argument("uniqueId");
Map<String, Object> resultData = methodCall.argument("result");
Map<String, Object> exts = methodCall.argument("exts");
mManager.closeContainer(uniqueId, resultData, exts);
result.success(true);
} catch (Throwable t) {
result.error("close page error", t.getMessage(), t);
}
}
break;
case "onShownContainerChanged": {
try {
String newId = methodCall.argument("newName");
String oldId = methodCall.argument("oldName");
mManager.onShownContainerChanged(newId, oldId);
result.success(true);
} catch (Throwable t) {
result.error("onShownContainerChanged", t.getMessage(), t);
}
}
break;
default: {
result.notImplemented();
}
}
}
}
FlutterViewContainerManager.java
调用openContainer
void openContainer(String url, Map<String, Object> urlParams, Map<String, Object> exts,OnResult onResult) {
Context context = FlutterBoost.instance().currentActivity();
if(context == null) {
context = FlutterBoost.instance().platform().getApplication();
}
if(urlParams == null) {
urlParams = new HashMap<>();
}
int requestCode = 0;
final Object v = urlParams.remove("requestCode");
if(v != null) {
requestCode = Integer.valueOf(String.valueOf(v));
}
final String uniqueId = ContainerRecord.genUniqueId(url);
urlParams.put(IContainerRecord.UNIQ_KEY,uniqueId);
IContainerRecord currentTopRecord = getCurrentTopRecord();
if(onResult != null) {
mOnResults.put(currentTopRecord.uniqueId(),onResult);
}
FlutterBoost.instance().platform().openContainer(context,url,urlParams,requestCode,exts);
}
MyApplication.java
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
INativeRouter router =new INativeRouter() {
@Override
public void openContainer(Context context, String url, Map<String, Object> urlParams, int requestCode, Map<String, Object> exts) {
Log.i("openPageByUrl","openContainer url " + url);
String assembleUrl=Utils.assembleUrl(url,urlParams);
PageRouter.openPageByUrl(context,assembleUrl, urlParams);
}
};
FlutterBoost.BoostPluginsRegister pluginsRegister= new FlutterBoost.BoostPluginsRegister(){
@Override
public void registerPlugins(PluginRegistry mRegistry) {
GeneratedPluginRegistrant.registerWith(mRegistry);
TextPlatformViewPlugin.register(mRegistry.registrarFor("TextPlatformViewPlugin"));
}
};
Platform platform= new FlutterBoost
.ConfigBuilder(this,router)
.isDebug(true)
.whenEngineStart(FlutterBoost.ConfigBuilder.ANY_ACTIVITY_CREATED)
.renderMode(FlutterView.RenderMode.texture)
.pluginsRegister(pluginsRegister)
.build();
FlutterBoost.instance().init(platform);
}
}
进入native页面,调用native的 startActivity
PageRouter.java
public static boolean openPageByUrl(Context context, String url, Map params) {
return openPageByUrl(context, url, params, 0);
}
public static boolean openPageByUrl(Context context, String url, Map params, int requestCode) {
String path = url.split("\\?")[0];
Log.i("openPageByUrl",path);
try {
if (pageName.containsKey(path)) {
Intent intent = BoostFlutterActivity.withNewEngine().url(pageName.get(path)).params(params)
.backgroundMode(BoostFlutterActivity.BackgroundMode.opaque).build(context);
if(context instanceof Activity){
Activity activity=(Activity)context;
activity.startActivityForResult(intent,requestCode);
}else{
context.startActivity(intent);
}
return true;
} else if (url.startsWith(FLUTTER_FRAGMENT_PAGE_URL)) {
context.startActivity(new Intent(context, FlutterFragmentPageActivity.class));
return true;
} else if (url.startsWith(NATIVE_PAGE_URL)) {
context.startActivity(new Intent(context, NativePageActivity.class));
return true;
}
return false;
} catch (Throwable t) {
return false;
}
}
Flutter 页面的创建
1、打开flutter 入口
PageRouter.openPageByUrl(this, PageRouter.FLUTTER_PAGE_FIRST_URL,params);
2、PageRouter分发,设置NewEngineIntentBuilder,配置url和参数,打开BoostFlutterActivity
PageRouter.java
public static boolean openPageByUrl(Context context, String url, Map params) {
return openPageByUrl(context, url, params, 0);
}
public static boolean openPageByUrl(Context context, String url, Map params, int requestCode) {
String path = url.split("\\?")[0];
Log.i("openPageByUrl",path);
try {
if (pageName.containsKey(path)) {
Intent intent = BoostFlutterActivity.withNewEngine().url(pageName.get(path)).params(params)
.backgroundMode(BoostFlutterActivity.BackgroundMode.opaque).build(context);
if(context instanceof Activity){
Activity activity=(Activity)context;
activity.startActivityForResult(intent,requestCode);
}else{
context.startActivity(intent);
}
return true;
} else if (url.startsWith(FLUTTER_FRAGMENT_PAGE_URL)) {
context.startActivity(new Intent(context, FlutterFragmentPageActivity.class));
return true;
} else if (url.startsWith(NATIVE_PAGE_URL)) {
context.startActivity(new Intent(context, NativePageActivity.class));
return true;
}
return false;
} catch (Throwable t) {
return false;
}
}
3、BoostFlutterActivity 初始化FlutterActivityAndFragmentDelegate和createFlutterView
BoostFlutterActivity.java
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
switchLaunchThemeForNormalTheme();
super.onCreate(savedInstanceState);
Log.d(TAG, "BoostFlutterActivity onCreate");
lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
delegate = new FlutterActivityAndFragmentDelegate(this);
delegate.onAttach(this);
configureWindowForTransparency();
setContentView(createFlutterView());
configureStatusBarForFullscreenFlutterExperience();
}
@NonNull
protected View createFlutterView() {
return delegate.onCreateView(
null /* inflater */,
null /* container */,
null /* savedInstanceState */);
}
4、FlutterActivityAndFragmentDelegate attach 初始化flutter环境
FlutterActivityAndFragmentDelegate.java
void onAttach(@NonNull Context context) {
ensureAlive();
Log.d(TAG, "onAttach");
if (FlutterBoost.instance().platform().whenEngineStart() == FlutterBoost.ConfigBuilder.FLUTTER_ACTIVITY_CREATED) {
FlutterBoost.instance().doInitialFlutter();
FlutterBoost.instance().boostPluginRegistry();
}
// When "retain instance" is true, the FlutterEngine will survive configuration
// changes. Therefore, we create a new one only if one does not already exist.
if (flutterEngine == null) {
Log.d(TAG, "setupFlutterEngine");
setupFlutterEngine();
}
// Regardless of whether or not a FlutterEngine already existed, the PlatformPlugin
// is bound to a specific Activity. Therefore, it needs to be created and configured
// every time this Fragment attaches to a new Activity.
// TODO(mattcarroll): the PlatformPlugin needs to be reimagined because it implicitly takes
// control of the entire window. This is unacceptable for non-fullscreen
// use-cases.
platformPlugin = host.providePlatformPlugin(host.getActivity(), flutterEngine);
host.configureFlutterEngine(flutterEngine);
host.getActivity().getWindow().setFormat(PixelFormat.TRANSLUCENT);
}
private void setupFlutterEngine() {
Log.d(TAG, "Setting up FlutterEngine.");
// Second, defer to subclasses for a custom FlutterEngine.
flutterEngine = host.provideFlutterEngine(host.getContext());
if (flutterEngine != null) {
isFlutterEngineFromHost = true;
return;
}
// Our host did not provide a custom FlutterEngine. Create a FlutterEngine to back our
// FlutterView.
Log.d(TAG, "No preferred FlutterEngine was provided. Creating a new FlutterEngine for"
+ " this NewFlutterFragment.");
isFlutterEngineFromHost = false;
}
5、onCreateView通过 FlutterViewContainerManager 新建ContainerRecord
mSyncer = FlutterBoost.instance().containerManager().generateSyncer(this);
FlutterActivityAndFragmentDelegate.java
@SuppressLint("ResourceType")
@NonNull
View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
Log.d(TAG, "Creating FlutterView.");
flutterEngine.getActivityControlSurface().attachToActivity(
host.getActivity(),
host.getLifecycle()
);
Log.d(TAG, "onCreateView getContainerUrl " + getContainerUrl());
mSyncer = FlutterBoost.instance().containerManager().generateSyncer(this);
ensureAlive();
flutterView = new XFlutterView(host.getActivity(), FlutterBoost.instance().platform().renderMode(), host.getTransparencyMode());
flutterSplashView = new FlutterSplashView(host.getContext());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
flutterSplashView.setId(View.generateViewId());
} else {
// TODO(mattcarroll): Find a better solution to this ID. This is a random, static ID.
// It might conflict with other Views, and it means that only a single FlutterSplashView
// can exist in a View hierarchy at one time.
flutterSplashView.setId(486947586);
}
flutterSplashView.displayFlutterViewWithSplash(flutterView, host.provideSplashScreen());
mSyncer.onCreate();
return flutterSplashView;
}
FlutterViewContainerManager.java
@Override
public IOperateSyncer generateSyncer(IFlutterViewContainer container) {
Utils.assertCallOnMainThread();
Log.d(TAG, "generateSyncer.");
ContainerRecord record = new ContainerRecord(this, container);
Log.d(TAG, "container.getContainerUrl() "+container.getContainerUrl());
if (mRecordMap.put(container, record) != null) {
Debuger.exception("container:" + container.getContainerUrl() + " already exists!");
}
mRefs.add(new ContainerRef(record.uniqueId(),container));
return record;
}
XFlutterView类型的flutterView ,通过TextureView或者SurfaceView显示flutter view
XFlutterView.java
private void init() {
Log.v(TAG, "Initializing FlutterView");
switch (renderMode) {
case surface:
Log.v(TAG, "Internally using a FlutterSurfaceView.");
FlutterSurfaceView flutterSurfaceView = new FlutterSurfaceView(getContext(), transparencyMode == FlutterView.TransparencyMode.transparent);
renderSurface = flutterSurfaceView;
addView(flutterSurfaceView);
break;
case texture:
Log.v(TAG, "Internally using a FlutterTextureView.");
XFlutterTextureView flutterTextureView = new XFlutterTextureView(getContext());
renderSurface = flutterTextureView;
addView(flutterTextureView);
break;
}
6、步骤3 最后的onCreateView 函数最后执行创建
mSyncer.onCreate();
通过MethodChannel 调用didInitPageContainer
ContainerRecord.java
@Override
public void onCreate() {
Utils.assertCallOnMainThread();
if (mState != STATE_UNKNOW) {
Debuger.exception("state error");
}
mState = STATE_CREATED;
// mContainer.getBoostFlutterView().onResume();
mProxy.create();
}
private void create() {
if (mState == STATE_UNKNOW) {
Log.d(TAG, "create. Container.getContainerUrl() "+mContainer.getContainerUrl());
invokeChannelUnsafe("didInitPageContainer",
mContainer.getContainerUrl(),
mContainer.getContainerUrlParams(),
mUniqueId
);
//Debuger.log("didInitPageContainer");
mState = STATE_CREATED;
}
}
7、boost_channel.dart 注册flutter 方法
class BoostChannel {
final MethodChannel _methodChannel = MethodChannel("flutter_boost");
final Map<String, List<EventListener>> _eventListeners = Map();
final Set<MethodHandler> _methodHandlers = Set();
BoostChannel() {
_methodChannel.setMethodCallHandler((MethodCall call){
if (call.method == "__event__") {
String name = call.arguments["name"];
Map arg = call.arguments["arguments"];
List<EventListener> list = _eventListeners[name];
if (list != null) {
for (EventListener l in list) {
l(name, arg);
}
}
}else{
for(MethodHandler handler in _methodHandlers) {
handler(call);
}
}
return Future.value();
});
}
8、container_coordinator 执行didInitPageContainer
container_coordinator.dart
ContainerCoordinator(BoostChannel channel) {
assert(_instance == null);
_instance = this;
channel.addEventListener("lifecycle",
(String name, Map arguments) => _onChannelEvent(arguments));
channel.addMethodHandler((MethodCall call) => _onMethodCall(call));
}
Future<dynamic> _onMethodCall(MethodCall call) {
Logger.log("onMetohdCall ${call.method}");
switch (call.method) {
case "didInitPageContainer":
{
String pageName = call.arguments["pageName"];
Map params = call.arguments["params"];
String uniqueId = call.arguments["uniqueId"];
_nativeContainerDidInit(pageName, params, uniqueId);
}
break;
case "willShowPageContainer":
{
String pageName = call.arguments["pageName"];
Map params = call.arguments["params"];
String uniqueId = call.arguments["uniqueId"];
_nativeContainerWillShow(pageName, params, uniqueId);
}
break;
case "didShowPageContainer":
{
String pageName = call.arguments["pageName"];
Map params = call.arguments["params"];
String uniqueId = call.arguments["uniqueId"];
nativeContainerDidShow(pageName, params, uniqueId);
}
break;
case "willDisappearPageContainer":
{
String pageName = call.arguments["pageName"];
Map params = call.arguments["params"];
String uniqueId = call.arguments["uniqueId"];
_nativeContainerWillDisappear(pageName, params, uniqueId);
}
break;
case "didDisappearPageContainer":
{
String pageName = call.arguments["pageName"];
Map params = call.arguments["params"];
String uniqueId = call.arguments["uniqueId"];
_nativeContainerDidDisappear(pageName, params, uniqueId);
}
break;
case "willDeallocPageContainer":
{
String pageName = call.arguments["pageName"];
Map params = call.arguments["params"];
String uniqueId = call.arguments["uniqueId"];
_nativeContainerWillDealloc(pageName, params, uniqueId);
}
break;
case "onNativePageResult":
{}
break;
}
bool _nativeContainerDidInit(String name, Map params, String pageId) {
performContainerLifeCycle(_createContainerSettings(name, params, pageId),
ContainerLifeCycle.Init);
return true;
}
9、_createContainerSettings 建立fluttter view
BoostContainerSettings _createContainerSettings(
String name, Map params, String pageId) {
Widget page;
final BoostContainerSettings routeSettings = BoostContainerSettings(
uniqueId: pageId,
name: name,
params: params,
builder: (BuildContext ctx) {
//Try to build a page using keyed builder.
if (_pageBuilders[name] != null) {
page = _pageBuilders[name](name, params, pageId);
}
//Build a page using default builder.
if (page == null && _defaultPageBuilder != null) {
page = _defaultPageBuilder(name, params, pageId);
}
assert(page != null);
Logger.log('build widget:$page for page:$name($pageId)');
return page;
});
return routeSettings;
}
10、_createContainerSettings 中的PageBuilder是main开始配置
main.dart
class _MyAppState extends State<MyApp> {
@override
void initState() {
super.initState();
FlutterBoost.singleton.registerPageBuilders({
'embeded': (pageName, params, _)=>EmbededFirstRouteWidget(),
'first': (pageName, params, _) => FirstRouteWidget(),
'second': (pageName, params, _) => SecondRouteWidget(),
'tab': (pageName, params, _) => TabRouteWidget(),
'platformView': (pageName, params, _) => PlatformRouteWidget(),
'flutterFragment': (pageName, params, _) => FragmentRouteWidget(params),
///可以在native层通过 getContainerParams 来传递参数
'flutterPage': (pageName, params, _) {
print("flutterPage params:$params");
return FlutterRouteWidget(params:params);
},
});
FlutterBoost.singleton.addBoostNavigatorObserver(TestBoostNavigatorObserver());
}
Flutter 页面的显示
1、BoostFlutterActivity onResume
mSyncer.onAppear();
BoostFlutterActivity.java
@Override
protected void onResume() {
super.onResume();
lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_RESUME);
delegate.onResume();
}
FlutterActivityAndFragmentDelegate.java
void
onResume() {
mSyncer.onAppear();
Log.v(TAG, "onResume()");
ensureAlive();
flutterEngine.getLifecycleChannel().appIsResumed();
BoostPluginRegistry registry = (BoostPluginRegistry) FlutterBoost.instance().getPluginRegistry();
ActivityPluginBinding binding = registry.getRegistrarAggregate().getActivityPluginBinding();
if (binding != null && (binding.getActivity() != this.host.getActivity())) {
flutterEngine.getActivityControlSurface().attachToActivity(
host.getActivity(),
host.getLifecycle()
);
}
}
2、ContainerRecord 调用didShowPageContainer
ContainerRecord.java
private void appear() {
Log.d(TAG, "appear. Container.getContainerUrl() "+mContainer.getContainerUrl());
invokeChannelUnsafe("didShowPageContainer",
mContainer.getContainerUrl(),
mContainer.getContainerUrlParams(),
mUniqueId
);
//Debuger.log("didShowPageContainer");
mState = STATE_APPEAR;
}
FlutterBoostPlugin.java
private class MethodChannelProxy {
private int mState = STATE_UNKNOW;
private void create() {
if (mState == STATE_UNKNOW) {
Log.d(TAG, "create. Container.getContainerUrl() "+mContainer.getContainerUrl());
invokeChannelUnsafe("didInitPageContainer",
mContainer.getContainerUrl(),
mContainer.getContainerUrlParams(),
mUniqueId
);
//Debuger.log("didInitPageContainer");
mState = STATE_CREATED;
}
}
private void appear() {
Log.d(TAG, "appear. Container.getContainerUrl() "+mContainer.getContainerUrl());
invokeChannelUnsafe("didShowPageContainer",
mContainer.getContainerUrl(),
mContainer.getContainerUrlParams(),
mUniqueId
);
//Debuger.log("didShowPageContainer");
mState = STATE_APPEAR;
}
private void disappear() {
if (mState < STATE_DISAPPEAR) {
invokeChannel("didDisappearPageContainer",
mContainer.getContainerUrl(),
mContainer.getContainerUrlParams(),
mUniqueId
);
//Debuger.log("didDisappearPageContainer");
mState = STATE_DISAPPEAR;
}
}
private void destroy() {
if (mState < STATE_DESTROYED) {
invokeChannel("willDeallocPageContainer",
mContainer.getContainerUrl(),
mContainer.getContainerUrlParams(),
mUniqueId
);
//Debuger.log("willDeallocPageContainer");
mState = STATE_DESTROYED;
}
}
public void invokeChannel(String method, String url, Map params, String uniqueId) {
HashMap<String, Object> args = new HashMap<>();
args.put("pageName", url);
args.put("params", params);
args.put("uniqueId", uniqueId);
FlutterBoost.instance().channel().invokeMethod(method, args);
}
public void invokeChannelUnsafe(String method, String url, Map params, String uniqueId) {
HashMap<String, Object> args = new HashMap<>();
args.put("pageName", url);
args.put("params", params);
args.put("uniqueId", uniqueId);
FlutterBoost.instance().channel().invokeMethodUnsafe(method, args);
}
}
3、ContainerRecord 调用didShowPageContainer
container_coordinator.dart
Future<dynamic> _onMethodCall(MethodCall call) {
Logger.log("onMetohdCall ${call.method}");
switch (call.method) {
case "didInitPageContainer":
{
String pageName = call.arguments["pageName"];
Map params = call.arguments["params"];
String uniqueId = call.arguments["uniqueId"];
_nativeContainerDidInit(pageName, params, uniqueId);
}
break;
case "willShowPageContainer":
{
String pageName = call.arguments["pageName"];
Map params = call.arguments["params"];
String uniqueId = call.arguments["uniqueId"];
_nativeContainerWillShow(pageName, params, uniqueId);
}
break;
case "didShowPageContainer":
{
String pageName = call.arguments["pageName"];
Map params = call.arguments["params"];
String uniqueId = call.arguments["uniqueId"];
nativeContainerDidShow(pageName, params, uniqueId);
}
bool _nativeContainerWillShow(String name, Map params, String pageId) {
if (FlutterBoost.containerManager?.containsContainer(pageId) != true) {
FlutterBoost.containerManager
?.pushContainer(_createContainerSettings(name, params, pageId));
}
//TODO, 需要验证android代码是否也可以移到这里
if (Platform.isIOS) {
try {
final SemanticsOwner owner =
WidgetsBinding.instance.pipelineOwner?.semanticsOwner;
final SemanticsNode root = owner?.rootSemanticsNode;
root?.detach();
root?.attach(owner);
} catch (e) {
assert(false, e.toString());
}
}
return true;
}
4、container_manager pushContainer
container_manager.dart
void pushContainer(BoostContainerSettings settings) {
assert(settings.uniqueId != _onstage.settings.uniqueId);
assert(_offstage.every((BoostContainer container) =>
container.settings.uniqueId != settings.uniqueId));
print("pushContainer ");
_offstage.add(_onstage);
_onstage = BoostContainer.obtain(widget.initNavigator, settings);
setState(() {});
for (BoostContainerObserver observer in FlutterBoost
.singleton.observersHolder
.observersOf<BoostContainerObserver>()) {
observer(ContainerOperation.Push, _onstage.settings);
}
Logger.log('ContainerObserver#2 didPush');
}
boost_container.dart
factory BoostContainer.obtain(
Navigator navigator, BoostContainerSettings settings) =>
BoostContainer(
key: GlobalKey<BoostContainerState>(),
settings: settings,
onGenerateRoute: (RouteSettings routeSettings) {
if (routeSettings.name == '/') {
return BoostPageRoute<dynamic>(
pageName: settings.name,
params: settings.params,
uniqueId: settings.uniqueId,
animated: false,
settings: routeSettings,
builder: settings.builder);
} else {
return navigator.onGenerateRoute(routeSettings);
}
},
observers: <NavigatorObserver>[
ContainerNavigatorObserver.bindContainerManager(),
HeroController(),
],
onUnknownRoute: navigator.onUnknownRoute);
boost_page_route.dart
typedef Widget PageBuilder(String pageName, Map params, String uniqueId);
class BoostPageRoute<T> extends MaterialPageRoute<T> {
final String pageName;
final String uniqueId;
final Map params;
final bool animated;
final WidgetBuilder builder;
final RouteSettings settings;
final Set<VoidCallback> backPressedListeners = Set<VoidCallback>();
BoostPageRoute(
{this.pageName,
this.params,
this.uniqueId,
this.animated,
this.builder,
this.settings})
: super(builder: builder, settings: settings);
static BoostPageRoute<T> of<T>(BuildContext context) {
final Route<T> route = ModalRoute.of(context);
if (route != null && route is BoostPageRoute<T>) {
return route;
} else {
throw Exception('not in a BoostPageRoute');
}
}
static BoostPageRoute<T> tryOf<T>(BuildContext context) {
final Route<T> route = ModalRoute.of(context);
if (route != null && route is BoostPageRoute<T>) {
return route;
} else {
return null;
}
}
}
simple_page_widgets.dart
FlutterBoost.singleton.open("second").then((Map value) {
print(
"call me when page is finished. did recieve second route result $value");
});
flutter_boost.dart
Future<Map<dynamic,dynamic>> open(String url,{Map<dynamic,dynamic> urlParams,Map<dynamic,dynamic> exts}){
Map<dynamic, dynamic> properties = new Map<dynamic, dynamic>();
properties["url"] = url;
properties["urlParams"] = urlParams;
properties["exts"] = exts;
print("invokeMethod open native page "+ url);
return channel.invokeMethod<Map<dynamic,dynamic>>(
'openPage', properties);
}
FlutterBoostPlugin.java
class BoostMethodHandler implements MethodChannel.MethodCallHandler {
@Override
public void onMethodCall(MethodCall methodCall, final MethodChannel.Result result) {
FlutterViewContainerManager mManager = (FlutterViewContainerManager) FlutterBoost.instance().containerManager();
switch (methodCall.method) {
case "pageOnStart": {
Map<String, Object> pageInfo = new HashMap<>();
try {
IContainerRecord record = mManager.getCurrentTopRecord();
if (record == null) {
record = mManager.getLastGenerateRecord();
}
if (record != null) {
Log.i("pageOnStart","record.getContainer().getContainerUrl() " + record.getContainer().getContainerUrl());
pageInfo.put("name", record.getContainer().getContainerUrl());
pageInfo.put("params", record.getContainer().getContainerUrlParams());
pageInfo.put("uniqueId", record.uniqueId());
}
result.success(pageInfo);
FlutterBoost.instance().setFlutterPostFrameCallTime(new Date().getTime());
} catch (Throwable t) {
result.error("no flutter page found!", t.getMessage(), t);
}
}
break;
case "openPage": {
try {
Map<String, Object> params = methodCall.argument("urlParams");
Map<String, Object> exts = methodCall.argument("exts");
String url = methodCall.argument("url");
Log.i("openPageByUrl","BoostMethodHandler openPage " + url);
mManager.openContainer(url, params, exts, new FlutterViewContainerManager.OnResult() {
@Override
public void onResult(Map<String, Object> rlt) {
if (result != null) {
result.success(rlt);
}
}
});
} catch (Throwable t) {
result.error("open page error", t.getMessage(), t);
}
}
MyApplication.java
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
INativeRouter router =new INativeRouter() {
@Override
public void openContainer(Context context, String url, Map<String, Object> urlParams, int requestCode, Map<String, Object> exts) {
Log.i("openPageByUrl","MyApplication openContainer url " + url);
String assembleUrl=Utils.assembleUrl(url,urlParams);
PageRouter.openPageByUrl(context,assembleUrl, urlParams);
}
};
FlutterBoost.BoostPluginsRegister pluginsRegister= new FlutterBoost.BoostPluginsRegister(){
@Override
public void registerPlugins(PluginRegistry mRegistry) {
GeneratedPluginRegistrant.registerWith(mRegistry);
TextPlatformViewPlugin.register(mRegistry.registrarFor("TextPlatformViewPlugin"));
}
};
Platform platform= new FlutterBoost
.ConfigBuilder(this,router)
.isDebug(true)
.whenEngineStart(FlutterBoost.ConfigBuilder.ANY_ACTIVITY_CREATED)
.renderMode(FlutterView.RenderMode.texture)
.pluginsRegister(pluginsRegister)
.build();
FlutterBoost.instance().init(platform);
}
}
context.startActivity(new Intent(context, NativePageActivity.class));
PageRouter.java
String path = url.split("\\?")[0];
Log.i("openPageByUrl","PageRouter openPageByUrl" + path);
try {
if (pageName.containsKey(path)) {
Intent intent = BoostFlutterActivity.withNewEngine().url(pageName.get(path)).params(params)
.backgroundMode(BoostFlutterActivity.BackgroundMode.opaque).build(context);
if(context instanceof Activity){
Activity activity=(Activity)context;
activity.startActivityForResult(intent,requestCode);
}else{
context.startActivity(intent);
}
return true;
} else if (url.startsWith(FLUTTER_FRAGMENT_PAGE_URL)) {
context.startActivity(new Intent(context, FlutterFragmentPageActivity.class));
return true;
} else if (url.startsWith(NATIVE_PAGE_URL)) {
context.startActivity(new Intent(context, NativePageActivity.class));
return true;
}
return false;
} catch (Throwable t) {
return false;
}
}
PageRouter.java
context.startActivity(new Intent(context, FlutterFragmentPageActivity.class));
底部有四个Tab,click事件打开FlutterFragment,
默认onResume 打开第一个tab
FlutterFragmentPageActivity.java
@Override
public void onClick(View v) {
mTab1.setBackgroundColor(Color.WHITE);
mTab2.setBackgroundColor(Color.WHITE);
mTab3.setBackgroundColor(Color.WHITE);
mTab4.setBackgroundColor(Color.WHITE);
if(mTab1 == v) {
mTab1.setBackgroundColor(Color.YELLOW);
mFragment= new FlutterFragment.NewEngineFragmentBuilder().url("flutterFragment").build();
}else if(mTab2 == v) {
mTab2.setBackgroundColor(Color.YELLOW);
mFragment= new FlutterFragment.NewEngineFragmentBuilder().url("flutterFragment").build();
}else if(mTab3 == v) {
mTab3.setBackgroundColor(Color.YELLOW);
mFragment= new FlutterFragment.NewEngineFragmentBuilder().url("flutterFragment").build();
}else{
mTab4.setBackgroundColor(Color.YELLOW);
mFragment= new FlutterFragment.NewEngineFragmentBuilder().url("flutterFragment").build();
}
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.fragment_stub,mFragment)
.commit();
}
@Override
protected void onResume() {
super.onResume();
mTab1.performClick();
}
NewEngineFragmentBuilder 配置url flutterFragment 生成FlutterFragment
public class FlutterFragment extends Fragment implements FlutterActivityAndFragmentDelegate.Host {
@NonNull
protected Bundle createArgs() {
Bundle args = new Bundle();
// TODO(mattcarroll): determine if we should have an explicit FlutterTestFragment instead of conflating.
if (null != shellArgs) {
args.putStringArray(ARG_FLUTTER_INITIALIZATION_ARGS, shellArgs.toArray());
}
BoostFlutterActivity.SerializableMap serializableMap = new BoostFlutterActivity.SerializableMap();
serializableMap.setMap(params);
args.putString(EXTRA_URL, url);
args.putSerializable(EXTRA_PARAMS, serializableMap);
args.putString(ARG_FLUTTERVIEW_RENDER_MODE, renderMode != null ? renderMode.name() : FlutterView.RenderMode.surface.name());
args.putString(ARG_FLUTTERVIEW_TRANSPARENCY_MODE, transparencyMode != null ? transparencyMode.name() : FlutterView.TransparencyMode.transparent.name());
args.putBoolean(ARG_DESTROY_ENGINE_WITH_FRAGMENT, true);
return args;
}
/**
* Constructs a new {@code NewFlutterFragment} (or a subclass) that is configured based on
* properties set on this {@code Builder}.
*/
@NonNull
public <T extends FlutterFragment> T build() {
try {
@SuppressWarnings("unchecked")
T frag = (T) fragmentClass.getDeclaredConstructor().newInstance();
if (frag == null) {
throw new RuntimeException("The NewFlutterFragment subclass sent in the constructor ("
+ fragmentClass.getCanonicalName() + ") does not match the expected return type.");
}
Bundle args = createArgs();
frag.setArguments(args);
return frag;
} catch (Exception e) {
throw new RuntimeException("Could not instantiate NewFlutterFragment subclass (" + fragmentClass.getName() + ")", e);
}
}
FlutterFragment.java
FlutterFragment生命周期函数onAttach onCreateView onStart onResume,和FlutterActivityAndFragmentDelegate关联,
流程同打开native 页面
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
Log.d(TAG, "FlutterFragment onAttach");
delegate = new FlutterActivityAndFragmentDelegate(this);
delegate.onAttach(context);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
Log.d(TAG, "FlutterFragment onCreateView");
return delegate.onCreateView(inflater, container, savedInstanceState);
}
@Override
public void onStart() {
super.onStart();
Log.d(TAG, "FlutterFragment onStart");
delegate.onStart();
}
@Override
public void onResume() {
super.onResume();
Log.d(TAG, "FlutterFragment onResume");
delegate.onResume();
}