前言
上一篇我們已經(jīng)在Android工程中集成Flutter工程了畦粮,也就是說(shuō)有一些界面是需要使用Flutter寫(xiě)的墨礁,那么如何從native界面跳轉(zhuǎn)到flutter界面中呢
因?yàn)樯弦淮蔚捻?xiàng)目是測(cè)試時(shí)創(chuàng)建的湾笛,這次我們重新創(chuàng)建全新項(xiàng)目專門(mén)用于這一系列的學(xué)習(xí)甥绿。
-
首先創(chuàng)建一個(gè)空的Android項(xiàng)目AndroidWithFlutter
Android項(xiàng)目目錄.png -
進(jìn)入workspace硝清,或其它任意你喜歡的目錄耐亏,執(zhí)行
flutter create -t module android_with_flutter
創(chuàng)建flutter module.png -
執(zhí)行成功后即在當(dāng)前目錄創(chuàng)建好了flutter module android_with_flutter
創(chuàng)建好的flutter module 文件目錄.png -
進(jìn)入.android文件夾中找到include_flutter.groovy文件,copy 它的路徑
include_flutter.groovy文件路徑.png -
在Android項(xiàng)目中的settings.gradle中添加如下配置代碼
引入flutter module.png -
Sync now一下郭怪,可以看到成功引入flutter module
flutter module目錄結(jié)構(gòu).png 像使用其它android library module一樣支示,在app 的build.gradle中添加依賴
implementation project(':flutter')
-
Android項(xiàng)目中嵌入flutter,Android項(xiàng)目和flutter項(xiàng)目是分開(kāi)開(kāi)發(fā)的鄙才,下面在Android Studio中打開(kāi)創(chuàng)建好的flutter module
Android Studio打開(kāi)flutter module項(xiàng)目.png -
可以看到它自動(dòng)創(chuàng)建了一個(gè)main.dart文件
flutter module項(xiàng)目.png -
運(yùn)行main.dart效果
main.dart運(yùn)行效果.png 進(jìn)入正題颂鸿,就以啟動(dòng)main.dart為例,首先在Android項(xiàng)目中創(chuàng)建一個(gè)empty activity
MainFlutterActivity
package com.ywy.androidwithflutter
import android.os.Bundle
import android.view.View
import android.widget.FrameLayout
import io.flutter.app.FlutterFragmentActivity
import io.flutter.facade.Flutter
class MainFlutterActivity : FlutterFragmentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main_flutter)
val mFlutterView: View = Flutter.createView(this, lifecycle, "main_flutter")
val mParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT)
addContentView(mFlutterView, mParams)
}
}
- 然后在
MainActivity
(native)中啟動(dòng)MainFlutterActivity
(flutter)
package com.ywy.androidwithflutter
import android.content.Intent
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val dumpIntent = Intent(this, MainFlutterActivity::class.java)
btnJumpToFlutter.setOnClickListener { startActivity(dumpIntent) }
}
}
-
MainActivity
中的代碼很簡(jiǎn)單,就是一個(gè)Button,點(diǎn)擊啟動(dòng)MainFlutterActivity
,啟動(dòng)方式和啟動(dòng)native Activity一模一樣攒庵。運(yùn)行點(diǎn)擊
啟動(dòng)MainActivity跳轉(zhuǎn).png
01-08 17:08:07.813 7220-7220/com.ywy.androidwithflutter E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.ywy.androidwithflutter, PID: 7220
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.ywy.androidwithflutter/com.ywy.androidwithflutter.MainFlutterActivity}: java.lang.IllegalStateException: ensureInitializationComplete must be called after startInitialization
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2817)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2892)
at android.app.ActivityThread.-wrap11(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1593)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6541)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
Caused by: java.lang.IllegalStateException: ensureInitializationComplete must be called after startInitialization
at io.flutter.view.FlutterMain.ensureInitializationComplete(FlutterMain.java:178)
at io.flutter.app.FlutterActivityDelegate.onCreate(FlutterActivityDelegate.java:152)
at io.flutter.app.FlutterFragmentActivity.onCreate(FlutterFragmentActivity.java:89)
at com.ywy.androidwithflutter.MainFlutterActivity.onCreate(MainFlutterActivity.kt:12)
at android.app.Activity.performCreate(Activity.java:6975)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1213)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2770)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2892)
at android.app.ActivityThread.-wrap11(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1593)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6541)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
- 點(diǎn)擊跳轉(zhuǎn)嘴纺,可以看到報(bào)了
java.lang.IllegalStateException: ensureInitializationComplete must be called after startInitialization
這樣的錯(cuò)誤,這里先說(shuō)解決方式浓冒,在MainFlutterActivity
的super.onCreate()
之前添加FlutterMain.startInitialization(getApplicationContext())
,當(dāng)然更加推薦在Application#onCreate()
中調(diào)用栽渴,也可以讓你的Application繼承FlutterApplication ,可以看到它自己在onCreate()
中調(diào)用了
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package io.flutter.app;
import android.app.Activity;
import android.app.Application;
import android.support.annotation.CallSuper;
import io.flutter.view.FlutterMain;
public class FlutterApplication extends Application {
private Activity mCurrentActivity = null;
public FlutterApplication() {
}
@CallSuper
public void onCreate() {
super.onCreate();
FlutterMain.startInitialization(this);
}
public Activity getCurrentActivity() {
return this.mCurrentActivity;
}
public void setCurrentActivity(Activity mCurrentActivity) {
this.mCurrentActivity = mCurrentActivity;
}
}
- 現(xiàn)在先直接加入看看效果
class MainFlutterActivity : FlutterFragmentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
FlutterMain.startInitialization(applicationContext)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main_flutter)
val mFlutterView: View = Flutter.createView(this, lifecycle, "main_flutter")
val mParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT)
addContentView(mFlutterView, mParams)
}
}
-
運(yùn)行再來(lái)點(diǎn)擊(debug模式跳轉(zhuǎn)到flutter界面會(huì)有一個(gè)黑屏的過(guò)程(看到的白屏是本身
R.layout.activity_main_flutter
這個(gè)布局的效果),在release下絲滑如跳轉(zhuǎn)到原生)
跳轉(zhuǎn)到flutter界面.gif 好像可以了稳懒,其實(shí)并沒(méi)有闲擦,是因?yàn)閯偤梦覀円D(zhuǎn)的是main.dart中的MyHomePage,而當(dāng)flutter找不到我們要跳轉(zhuǎn)的界面時(shí),默認(rèn)跳轉(zhuǎn)到main.dart中的MyHomePage(main.dart中home聲明了),再來(lái)看
MainFlutterActivity
中創(chuàng)建FlutterView的地方val mFlutterView: View = Flutter.createView(this, lifecycle, "main_flutter")
,這樣子寫(xiě)场梆,flutter怎么知道我們要嵌入Activity的是哪個(gè)flutter界面呢墅冷,關(guān)鍵的地方在main_flutter這個(gè)路由字符串,這個(gè)可以是任意字符串或油,只要保證一一對(duì)應(yīng)即可寞忿,那么這個(gè)main_flutter對(duì)應(yīng)哪個(gè)flutter頁(yè)面,肯定是有一個(gè)和它對(duì)應(yīng)的配置装哆。還需要到flutter module中的main.dart中配置路由
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or press Run > Flutter Hot Reload in a Flutter IDE). Notice that the
// counter didn't reset back to zero; the application is not restarted.
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
routes: <String, WidgetBuilder>{
"main_flutter": (context) => MyHomePage()
},
);
}
}
- 假如想要跳轉(zhuǎn)到其它flutter罐脊,同樣的寫(xiě)一個(gè)page,然后在routes的map中加參數(shù)就可以了(類似Android中的AndroidManifest.xml中配置組件定嗓,只不過(guò)flutter中都是Widget)
routes: <String, WidgetBuilder>{
"main_flutter": (context) => MyHomePage(),
"route_key": (context) => ANewPage()
}
到了這里,如何從原生跳轉(zhuǎn)到flutter界面已經(jīng)實(shí)現(xiàn)了萍桌,下面我們簡(jiǎn)單的看一下為什么這樣可以跳轉(zhuǎn)到flutter界面宵溅,并且解釋
java.lang.IllegalStateException: ensureInitializationComplete must be called after startInitialization
錯(cuò)誤的出現(xiàn)及解決首先注意到
MainFlutterActivity
繼承了FlutterFragmentActivity
,并且在onCreate()
中創(chuàng)建了FlutterView并通過(guò)addContentView
嵌入上炎,下面看看FlutterFragmentActivity
是什么
public class FlutterFragmentActivity extends FragmentActivity implements Provider, PluginRegistry, ViewFactory {
private final FlutterActivityDelegate delegate = new FlutterActivityDelegate(this, this);
private final FlutterActivityEvents eventDelegate;
private final Provider viewProvider;
private final PluginRegistry pluginRegistry;
public FlutterFragmentActivity() {
this.eventDelegate = this.delegate;
this.viewProvider = this.delegate;
this.pluginRegistry = this.delegate;
}
public FlutterView getFlutterView() {
return this.viewProvider.getFlutterView();
}
public FlutterView createFlutterView(Context context) {
return null;
}
public FlutterNativeView createFlutterNativeView() {
return null;
}
public boolean retainFlutterNativeView() {
return false;
}
public final boolean hasPlugin(String key) {
return this.pluginRegistry.hasPlugin(key);
}
public final <T> T valuePublishedByPlugin(String pluginKey) {
return this.pluginRegistry.valuePublishedByPlugin(pluginKey);
}
public final Registrar registrarFor(String pluginKey) {
return this.pluginRegistry.registrarFor(pluginKey);
}
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.eventDelegate.onCreate(savedInstanceState);
}
protected void onDestroy() {
this.eventDelegate.onDestroy();
super.onDestroy();
}
public void onBackPressed() {
if(!this.eventDelegate.onBackPressed()) {
super.onBackPressed();
}
}
protected void onStop() {
this.eventDelegate.onStop();
super.onStop();
}
protected void onPause() {
super.onPause();
this.eventDelegate.onPause();
}
protected void onPostResume() {
super.onPostResume();
this.eventDelegate.onPostResume();
}
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
this.eventDelegate.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if(!this.eventDelegate.onActivityResult(requestCode, resultCode, data)) {
super.onActivityResult(requestCode, resultCode, data);
}
}
protected void onNewIntent(Intent intent) {
this.eventDelegate.onNewIntent(intent);
}
public void onUserLeaveHint() {
this.eventDelegate.onUserLeaveHint();
}
public void onTrimMemory(int level) {
this.eventDelegate.onTrimMemory(level);
}
public void onLowMemory() {
this.eventDelegate.onLowMemory();
}
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
this.eventDelegate.onConfigurationChanged(newConfig);
}
}
- 可以看到
FlutterFragmentActivity
其實(shí)就是FragmentActivity
恃逻,區(qū)別就是實(shí)現(xiàn)了三個(gè)接口Provider, PluginRegistry, ViewFactory
,并且FlutterFragmentActivity
的生命周期交由FlutterActivityDelegate代理管理藕施,順便看一下這三個(gè)接口分別是什么
//只有一個(gè)方法寇损,提供獲取FlutterView
public interface Provider {
FlutterView getFlutterView();
}
// 用于插件注冊(cè),這里的插件注冊(cè)在之后的flutter和android native通訊中會(huì)用到裳食,當(dāng)然flutter與ios通訊方式也差不多
public interface PluginRegistry {
// 注冊(cè)一個(gè)插件
PluginRegistry.Registrar registrarFor(String var1);
// 是否有這個(gè)插件
boolean hasPlugin(String var1);
// 插件發(fā)布的值
<T> T valuePublishedByPlugin(String var1);
//插件生命周期回調(diào)
public interface PluginRegistrantCallback {
void registerWith(PluginRegistry var1);
}
//視圖銷毀的回調(diào)
public interface ViewDestroyListener {
boolean onViewDestroy(FlutterNativeView var1);
}
//下面的都是在Activity中熟悉的回調(diào)
//用戶主動(dòng)離開(kāi)activity時(shí)會(huì)調(diào)用矛市,比如切任務(wù)或者按home鍵,必須是用戶主動(dòng)的
public interface UserLeaveHintListener {
void onUserLeaveHint();
}
// 當(dāng)activity 重新啟動(dòng)的時(shí)候調(diào)用
public interface NewIntentListener {
boolean onNewIntent(Intent var1);
}
//獲取activity的返回值的回調(diào)
public interface ActivityResultListener {
boolean onActivityResult(int var1, int var2, Intent var3);
}
//請(qǐng)求權(quán)限的回調(diào)
public interface RequestPermissionsResultListener {
boolean onRequestPermissionsResult(int var1, String[] var2, int[] var3);
}
/// 插件的注冊(cè)者
public interface Registrar {
//插件的宿主activity
Activity activity();
//插件的上下文 Application Context.
Context context();
//當(dāng)前活動(dòng)的context
Context activeContext();
//返回 BinaryMessenger 主要用來(lái)注冊(cè)Platform channels
BinaryMessenger messenger();
//返回 TextureRegistry诲祸,從里面可以拿到SurfaceTexture
TextureRegistry textures();
/// 獲取視圖
FlutterView view();
//返回Asset資源對(duì)應(yīng)的路徑
String lookupKeyForAsset(String var1);
//返回Asset資源對(duì)應(yīng)的路徑
String lookupKeyForAsset(String var1, String var2);
//插件發(fā)布一個(gè)值浊吏,與上面的valuePublishedByPlugin對(duì)應(yīng)
PluginRegistry.Registrar publish(Object var1);
//下面的都是添加上面的回調(diào)監(jiān)聽(tīng)
PluginRegistry.Registrar addRequestPermissionsResultListener(PluginRegistry.RequestPermissionsResultListener var1);
PluginRegistry.Registrar addActivityResultListener(PluginRegistry.ActivityResultListener var1);
PluginRegistry.Registrar addNewIntentListener(PluginRegistry.NewIntentListener var1);
PluginRegistry.Registrar addUserLeaveHintListener(PluginRegistry.UserLeaveHintListener var1);
PluginRegistry.Registrar addViewDestroyListener(PluginRegistry.ViewDestroyListener var1);
}
}
- 回到
MainFlutterActivity
中Flutter.createView(this, lifecycle, "main_flutter")
這句
public static FlutterView createView(@NonNull final Activity activity, @NonNull final Lifecycle lifecycle, final String initialRoute) {
FlutterMain.startInitialization(activity.getApplicationContext());
FlutterMain.ensureInitializationComplete(activity.getApplicationContext(), null);
final FlutterNativeView nativeView = new FlutterNativeView(activity);
final FlutterView flutterView = new FlutterView(activity, null, nativeView) {
//other code
};
if (initialRoute != null) {
flutterView.setInitialRoute(initialRoute);
}
flutterView.setAlpha(0.0f);
return flutterView;
}
- 主要看
FlutterMain.startInitialization(activity.getApplicationContext());
和FlutterMain.ensureInitializationComplete(activity.getApplicationContext(), null);
public static void startInitialization(Context applicationContext) {
//調(diào)用兩個(gè)參數(shù)的方法
startInitialization(applicationContext, new FlutterMain.Settings());
}
public static void startInitialization(Context applicationContext, FlutterMain.Settings settings) {
if(Looper.myLooper() != Looper.getMainLooper()) {
throw new IllegalStateException("startInitialization must be called on the main thread");
} else if(sSettings == null) {
//執(zhí)行完FlutterMain.startInitialization(activity.getApplicationContext());之后,sSettings 不為null
sSettings = settings;
long initStartTimestampMillis = SystemClock.uptimeMillis();
initConfig(applicationContext);
initAot(applicationContext);
initResources(applicationContext);
System.loadLibrary("flutter");
long initTimeMillis = SystemClock.uptimeMillis() - initStartTimestampMillis;
nativeRecordStartTimestamp(initTimeMillis);
}
}
public static void ensureInitializationComplete(Context applicationContext, String[] args) {
if(Looper.myLooper() != Looper.getMainLooper()) {
throw new IllegalStateException("ensureInitializationComplete must be called on the main thread");
} else if(sSettings == null) {
//重點(diǎn)看這里救氯,當(dāng)sSettings 為空的時(shí)候報(bào)錯(cuò)
throw new IllegalStateException("ensureInitializationComplete must be called after startInitialization");
} else if(!sInitialized) {
//other code
}
}
- 找到了報(bào)該錯(cuò)誤的原因找田,當(dāng)sSettings為空時(shí)就會(huì)導(dǎo)致該錯(cuò)誤的產(chǎn)生,但是執(zhí)行完
FlutterMain.startInitialization(activity.getApplicationContext());
后着憨,new FlutterMain.Settings()
傳了進(jìn)去墩衙,不應(yīng)該會(huì)導(dǎo)致這個(gè)錯(cuò)誤,那么肯定在Flutter.createView(this, lifecycle, "main_flutter")
之前有地方調(diào)用了FlutterMain.ensureInitializationComplete
方法甲抖,往前看還調(diào)用了super.oncreate()
,還記得前面說(shuō)過(guò)FlutterFragmentActivity
的生命周期交由FlutterActivityDelegate
代理管理,再看FlutterActivityDelegate.onCreate()
public void onCreate(Bundle savedInstanceState) {
//other code
String[] args = getArgsFromIntent(this.activity.getIntent());
//最終找到元兇 漆改,在這里也調(diào)用了該方法,所以需要在super.onCreate()之前調(diào)用FlutterMain.startInitialization(getApplicationContext())
FlutterMain.ensureInitializationComplete(this.activity.getApplicationContext(), args);
//創(chuàng)建flutterView 准谚,注意viewFactory.createFlutterView默認(rèn)返回的是null
this.flutterView = this.viewFactory.createFlutterView(this.activity);
if(this.flutterView == null) {
//默認(rèn)走該流程
FlutterNativeView nativeView = this.viewFactory.createFlutterNativeView();
this.flutterView = new FlutterView(this.activity, (AttributeSet)null, nativeView);
this.flutterView.setLayoutParams(matchParent);
this.activity.setContentView(this.flutterView);
this.launchView = this.createLaunchView();
if(this.launchView != null) {
this.addLaunchView();
}
}
if(!this.loadIntent(this.activity.getIntent())) {
if(!this.flutterView.getFlutterNativeView().isApplicationRunning()) {
String appBundlePath = FlutterMain.findAppBundlePath(this.activity.getApplicationContext());
if(appBundlePath != null) {
FlutterRunArguments arguments = new FlutterRunArguments();
arguments.bundlePath = appBundlePath;
arguments.entrypoint = "main";
this.flutterView.runFromBundle(arguments);
}
}
}
}
到了現(xiàn)在籽懦,我們已經(jīng)知道了該錯(cuò)誤導(dǎo)致的原因及解決方式(為什么要這樣解決)。
下面我們?cè)倏纯磩?chuàng)建的FlutterView是什么
public class FlutterView extends SurfaceView implements BinaryMessenger, TextureRegistry, AccessibilityStateChangeListener {
//other code
private final MethodChannel mFlutterLocalizationChannel;
private final MethodChannel mFlutterNavigationChannel;
private final BasicMessageChannel<Object> mFlutterKeyEventChannel;
private final BasicMessageChannel<String> mFlutterLifecycleChannel;
private final BasicMessageChannel<Object> mFlutterSystemChannel;
private final BasicMessageChannel<Object> mFlutterSettingsChannel;
public FlutterView(Context context, AttributeSet attrs, FlutterNativeView nativeView) {
//other code
this.mFlutterNavigationChannel = new MethodChannel(this, "flutter/navigation", JSONMethodCodec.INSTANCE);
}
public void setInitialRoute(String route) {
this.mFlutterNavigationChannel.invokeMethod("setInitialRoute", route);
}
public void pushRoute(String route) {
this.mFlutterNavigationChannel.invokeMethod("pushRoute", route);
}
public void popRoute() {
this.mFlutterNavigationChannel.invokeMethod("popRoute", (Object)null);
}
//other code
}
- 可以看到FlutterView 就是一個(gè)SurfaceView 氛魁,那么addContentView毫無(wú)疑問(wèn),但是Flutter是如何知道要加載的是哪個(gè)頁(yè)面,在
Flutter#createView()
中有這樣一段
if (initialRoute != null) {
flutterView.setInitialRoute(initialRoute);
}
- 在剛剛FlutterView源碼中可以看到厅篓,實(shí)際上它是通過(guò)MethodChannel與Flutter進(jìn)行通訊秀存,交由Flutter進(jìn)行處理
public void setInitialRoute(String route) {
this.mFlutterNavigationChannel.invokeMethod("setInitialRoute", route);
}
- 關(guān)于MethodChannel是什么,這里簡(jiǎn)單的提一下羽氮,F(xiàn)lutter需要與Native之間進(jìn)行通訊(即native和Dart之間的互相調(diào)用)或链,這個(gè)通訊是通過(guò)Platform Channels,同屬于Platform Channels還有另外兩種(EventChannel档押,BasicMessageChannel)了解更多
- 也就是說(shuō)在Flutter方也有一個(gè)
MethodChannel(this, "flutter/navigation", JSONMethodCodec.INSTANCE)
,在system_channels.dart中
/// A JSON [MethodChannel] for navigation.
///
/// The following incoming methods are defined for this channel (registered
/// using [MethodChannel.setMethodCallHandler]):
///
/// * `popRoute`, which is called when the system wants the current route to
/// be removed (e.g. if the user hits a system-level back button).
///
/// * `pushRoute`, which is called with a single string argument when the
/// operating system instructs the application to open a particular page.
///
/// See also:
///
/// * [WidgetsBindingObserver.didPopRoute] and
/// [WidgetsBindingObserver.didPushRoute], which expose this channel's
/// methods.
static const MethodChannel navigation = MethodChannel(
'flutter/navigation',
JSONMethodCodec(),
);
- 在binding.dart中,找到了實(shí)現(xiàn)處理的澳盐,但是并沒(méi)有看到
setInitialRoute
的處理
SystemChannels.navigation.setMethodCallHandler(_handleNavigationInvocation);
Future<dynamic> _handleNavigationInvocation(MethodCall methodCall) {
switch (methodCall.method) {
case 'popRoute':
return handlePopRoute();
case 'pushRoute':
return handlePushRoute(methodCall.arguments);
}
return Future<dynamic>.value();
}
- 那這個(gè)initialRoute的作用是到底是什么呢祈纯?,在main.dart中通過(guò)MaterialApp中的routes配置啟動(dòng)的flutter界面
/// The [MaterialApp] configures the top-level [Navigator] to search for routes
/// in the following order:
///
/// 1. For the `/` route, the [home] property, if non-null, is used.
///
/// 2. Otherwise, the [routes] table is used, if it has an entry for the route.
///
/// 3. Otherwise, [onGenerateRoute] is called, if provided. It should return a
/// non-null value for any _valid_ route not handled by [home] and [routes].
///
/// 4. Finally if all else fails [onUnknownRoute] is called.
///
/// If a [Navigator] is created, at least one of these options must handle the
/// `/` route, since it is used when an invalid [initialRoute] is specified on
/// startup (e.g. by another application launching this one with an intent on
/// Android; see [Window.defaultRouteName]).
///
/// This widget also configures the observer of the top-level [Navigator] (if
/// any) to perform [Hero] animations.
///
/// If [home], [routes], [onGenerateRoute], and [onUnknownRoute] are all null,
/// and [builder] is not null, then no [Navigator] is created.
class MaterialApp extends StatefulWidget {
//other code
/// {@macro flutter.widgets.widgetsApp.home}
final Widget home;
/// The application's top-level routing table.
///
/// When a named route is pushed with [Navigator.pushNamed], the route name is
/// looked up in this map. If the name is present, the associated
/// [WidgetBuilder] is used to construct a [MaterialPageRoute] that performs
/// an appropriate transition, including [Hero] animations, to the new route.
///
/// {@macro flutter.widgets.widgetsApp.routes}
final Map<String, WidgetBuilder> routes;
/// {@macro flutter.widgets.widgetsApp.initialRoute}
final String initialRoute;
/// {@macro flutter.widgets.widgetsApp.onGenerateRoute}
final RouteFactory onGenerateRoute;
/// {@macro flutter.widgets.widgetsApp.onUnknownRoute}
final RouteFactory onUnknownRoute;
}
- 可以看到Flutter中頂層的Navigator尋找路由的順序
- 對(duì)于'/'路由叼耙,如果[home]屬性配置不為空腕窥,首先使用
- 對(duì)于非'/'路由,優(yōu)先使用[routes]配置的(如果它包含路由的條目的話)
- 否則筛婉,如果[onGenerateRoute]提供了一個(gè)有效的沒(méi)有被[home]和[routes]處理的路由簇爆,調(diào)用
- 最后,如果前面的尋找都失敗了爽撒,調(diào)用[onUnknownRoute]
- 如果所有的都失敗了入蛆,將不會(huì)進(jìn)行導(dǎo)航
- 從其它應(yīng)用啟動(dòng)時(shí)指定了無(wú)效的[initialRoute],默認(rèn)使用 ' / '路由
- 查看
flutter.widgets.widgetsApp.initialRoute
的說(shuō)明
/// {@template flutter.widgets.widgetsApp.initialRoute}
/// The name of the first route to show, if a [Navigator] is built.
///
/// Defaults to [Window.defaultRouteName], which may be overridden by the code
/// that launched the application.
///
/// If the route contains slashes, then it is treated as a "deep link", and
/// before this route is pushed, the routes leading to this one are pushed
/// also. For example, if the route was `/a/b/c`, then the app would start
/// with the three routes `/a`, `/a/b`, and `/a/b/c` loaded, in that order.
///
/// If any part of this process fails to generate routes, then the
/// [initialRoute] is ignored and [Navigator.defaultRouteName] is used instead
/// This can happen if the app is started with an intent that specifies
/// a non-existent route.
/// The [Navigator] is only built if routes are provided (either via [home],
/// [routes], [onGenerateRoute], or [onUnknownRoute]); if they are not,
/// [initialRoute] must be null and [builder] must not be null.
/// {@endtemplate}
final String initialRoute;
initialRoute是第一個(gè)展示的路由的name,默認(rèn)是Window.defaultRouteName
再看window.dart中對(duì)于
defaultRouteName
的說(shuō)明
/// The route or path that the embedder requested when the application was
/// launched.
///
/// This will be the string "`/`" if no particular route was requested.
///
/// ## Android
///
/// On Android, calling
/// [`FlutterView.setInitialRoute`](/javadoc/io/flutter/view/FlutterView.html#setInitialRoute-java.lang.String-)
/// will set this value. The value must be set sufficiently early, i.e. before
/// the [runApp] call is executed in Dart, for this to have any effect on the
/// framework. The `createFlutterView` method in your `FlutterActivity`
/// subclass is a suitable time to set the value. The application's
/// `AndroidManifest.xml` file must also be updated to have a suitable
/// [`<intent-filter>`](https://developer.android.com/guide/topics/manifest/intent-filter-element.html).
///
/// See also:
///
/// * [Navigator], a widget that handles routing.
/// * [SystemChannels.navigation], which handles subsequent navigation
/// requests from the embedder.
String get defaultRouteName => _defaultRouteName();
String _defaultRouteName() native 'Window_defaultRouteName';
- 可以看到
FlutterView#setInitialRoute
可以設(shè)置該值硕勿,也就是說(shuō)哨毁,當(dāng)在native端創(chuàng)建FlutterView之后通過(guò)setInitialRoute
方法設(shè)置Window.defaultRouteName,從而決定啟動(dòng)的是那一個(gè)Flutter界面
待解決問(wèn)題
-
沒(méi)有搞清楚flutter端MethodChanel是如何處理setInitialRoute方法
但是可以看到Window.defaultRouteName是通過(guò)native方法在c++端設(shè)置的源武,推測(cè)是在MethodChanel傳遞消息的時(shí)候在c++端進(jìn)行了處理扼褪。
擴(kuò)展
-
在閱讀源碼的時(shí)候還發(fā)現(xiàn)了用另外一種方式可以將Flutter頁(yè)面嵌入Activity中----Intent附帶路由
由于篇幅的關(guān)系,將在下一篇Flutter頁(yè)面嵌入Android Fragment中進(jìn)行說(shuō)明