前言
上一篇我们已经在Android工程中集成Flutter工程了,也就是说有一些界面是需要使用Flutter写的,那么如何从native界面跳转到flutter界面中呢
因为上一次的项目是测试时创建的,这次我们重新创建全新项目专门用于这一系列的学习。
-
首先创建一个空的Android项目AndroidWithFlutter
-
进入workspace,或其它任意你喜欢的目录,执行
flutter create -t module android_with_flutter
-
执行成功后即在当前目录创建好了flutter module android_with_flutter
-
进入.android文件夹中找到include_flutter.groovy文件,copy 它的路径
-
在Android项目中的settings.gradle中添加如下配置代码
-
Sync now一下,可以看到成功引入flutter module
像使用其它android library module一样,在app 的build.gradle中添加依赖
implementation project(':flutter')
-
Android项目中嵌入flutter,Android项目和flutter项目是分开开发的,下面在Android Studio中打开创建好的flutter module
-
可以看到它自动创建了一个main.dart文件
-
运行main.dart效果
进入正题,就以启动main.dart为例,首先在Android项目中创建一个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)中启动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
中的代码很简单,就是一个Button,点击启动MainFlutterActivity
,启动方式和启动native Activity一模一样。运行点击
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)
- 点击跳转,可以看到报了
java.lang.IllegalStateException: ensureInitializationComplete must be called after startInitialization
这样的错误,这里先说解决方式,在MainFlutterActivity
的super.onCreate()
之前添加FlutterMain.startInitialization(getApplicationContext())
,当然更加推荐在Application#onCreate()
中调用,也可以让你的Application继承FlutterApplication ,可以看到它自己在onCreate()
中调用了
//
// 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;
}
}
- 现在先直接加入看看效果
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)
}
}
-
运行再来点击(debug模式跳转到flutter界面会有一个黑屏的过程(看到的白屏是本身
R.layout.activity_main_flutter
这个布局的效果),在release下丝滑如跳转到原生)
好像可以了,其实并没有,是因为刚好我们要跳转的是main.dart中的MyHomePage,而当flutter找不到我们要跳转的界面时,默认跳转到main.dart中的MyHomePage(main.dart中home声明了),再来看
MainFlutterActivity
中创建FlutterView的地方val mFlutterView: View = Flutter.createView(this, lifecycle, "main_flutter")
,这样子写,flutter怎么知道我们要嵌入Activity的是哪个flutter界面呢,关键的地方在main_flutter这个路由字符串,这个可以是任意字符串,只要保证一一对应即可,那么这个main_flutter对应哪个flutter页面,肯定是有一个和它对应的配置。还需要到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()
},
);
}
}
- 假如想要跳转到其它flutter,同样的写一个page,然后在routes的map中加参数就可以了(类似Android中的AndroidManifest.xml中配置组件,只不过flutter中都是Widget)
routes: <String, WidgetBuilder>{
"main_flutter": (context) => MyHomePage(),
"route_key": (context) => ANewPage()
}
到了这里,如何从原生跳转到flutter界面已经实现了,下面我们简单的看一下为什么这样可以跳转到flutter界面,并且解释
java.lang.IllegalStateException: ensureInitializationComplete must be called after startInitialization
错误的出现及解决首先注意到
MainFlutterActivity
继承了FlutterFragmentActivity
,并且在onCreate()
中创建了FlutterView并通过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
其实就是FragmentActivity
,区别就是实现了三个接口Provider, PluginRegistry, ViewFactory
,并且FlutterFragmentActivity
的生命周期交由FlutterActivityDelegate代理管理,顺便看一下这三个接口分别是什么
//只有一个方法,提供获取FlutterView
public interface Provider {
FlutterView getFlutterView();
}
// 用于插件注册,这里的插件注册在之后的flutter和android native通讯中会用到,当然flutter与ios通讯方式也差不多
public interface PluginRegistry {
// 注册一个插件
PluginRegistry.Registrar registrarFor(String var1);
// 是否有这个插件
boolean hasPlugin(String var1);
// 插件发布的值
<T> T valuePublishedByPlugin(String var1);
//插件生命周期回调
public interface PluginRegistrantCallback {
void registerWith(PluginRegistry var1);
}
//视图销毁的回调
public interface ViewDestroyListener {
boolean onViewDestroy(FlutterNativeView var1);
}
//下面的都是在Activity中熟悉的回调
//用户主动离开activity时会调用,比如切任务或者按home键,必须是用户主动的
public interface UserLeaveHintListener {
void onUserLeaveHint();
}
// 当activity 重新启动的时候调用
public interface NewIntentListener {
boolean onNewIntent(Intent var1);
}
//获取activity的返回值的回调
public interface ActivityResultListener {
boolean onActivityResult(int var1, int var2, Intent var3);
}
//请求权限的回调
public interface RequestPermissionsResultListener {
boolean onRequestPermissionsResult(int var1, String[] var2, int[] var3);
}
/// 插件的注册者
public interface Registrar {
//插件的宿主activity
Activity activity();
//插件的上下文 Application Context.
Context context();
//当前活动的context
Context activeContext();
//返回 BinaryMessenger 主要用来注册Platform channels
BinaryMessenger messenger();
//返回 TextureRegistry,从里面可以拿到SurfaceTexture
TextureRegistry textures();
/// 获取视图
FlutterView view();
//返回Asset资源对应的路径
String lookupKeyForAsset(String var1);
//返回Asset资源对应的路径
String lookupKeyForAsset(String var1, String var2);
//插件发布一个值,与上面的valuePublishedByPlugin对应
PluginRegistry.Registrar publish(Object var1);
//下面的都是添加上面的回调监听
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) {
//调用两个参数的方法
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) {
//执行完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) {
//重点看这里,当sSettings 为空的时候报错
throw new IllegalStateException("ensureInitializationComplete must be called after startInitialization");
} else if(!sInitialized) {
//other code
}
}
- 找到了报该错误的原因,当sSettings为空时就会导致该错误的产生,但是执行完
FlutterMain.startInitialization(activity.getApplicationContext());
后,new FlutterMain.Settings()
传了进去,不应该会导致这个错误,那么肯定在Flutter.createView(this, lifecycle, "main_flutter")
之前有地方调用了FlutterMain.ensureInitializationComplete
方法,往前看还调用了super.oncreate()
,还记得前面说过FlutterFragmentActivity
的生命周期交由FlutterActivityDelegate
代理管理,再看FlutterActivityDelegate.onCreate()
public void onCreate(Bundle savedInstanceState) {
//other code
String[] args = getArgsFromIntent(this.activity.getIntent());
//最终找到元凶 ,在这里也调用了该方法,所以需要在super.onCreate()之前调用FlutterMain.startInitialization(getApplicationContext())
FlutterMain.ensureInitializationComplete(this.activity.getApplicationContext(), args);
//创建flutterView ,注意viewFactory.createFlutterView默认返回的是null
this.flutterView = this.viewFactory.createFlutterView(this.activity);
if(this.flutterView == null) {
//默认走该流程
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);
}
}
}
}
到了现在,我们已经知道了该错误导致的原因及解决方式(为什么要这样解决)。
下面我们再看看创建的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 就是一个SurfaceView ,那么addContentView毫无疑问,但是Flutter是如何知道要加载的是哪个页面,在
Flutter#createView()
中有这样一段
if (initialRoute != null) {
flutterView.setInitialRoute(initialRoute);
}
- 在刚刚FlutterView源码中可以看到,实际上它是通过MethodChannel与Flutter进行通讯,交由Flutter进行处理
public void setInitialRoute(String route) {
this.mFlutterNavigationChannel.invokeMethod("setInitialRoute", route);
}
- 关于MethodChannel是什么,这里简单的提一下,Flutter需要与Native之间进行通讯(即native和Dart之间的互相调用),这个通讯是通过Platform Channels,同属于Platform Channels还有另外两种(EventChannel,BasicMessageChannel)了解更多
- 也就是说在Flutter方也有一个
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中,找到了实现处理的,但是并没有看到
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();
}
- 那这个initialRoute的作用是到底是什么呢?,在main.dart中通过MaterialApp中的routes配置启动的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寻找路由的顺序
- 对于'/'路由,如果[home]属性配置不为空,首先使用
- 对于非'/'路由,优先使用[routes]配置的(如果它包含路由的条目的话)
- 否则,如果[onGenerateRoute]提供了一个有效的没有被[home]和[routes]处理的路由,调用
- 最后,如果前面的寻找都失败了,调用[onUnknownRoute]
- 如果所有的都失败了,将不会进行导航
- 从其它应用启动时指定了无效的[initialRoute],默认使用 ' / '路由
- 查看
flutter.widgets.widgetsApp.initialRoute
的说明
/// {@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是第一个展示的路由的name,默认是Window.defaultRouteName
再看window.dart中对于
defaultRouteName
的说明
/// 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
可以设置该值,也就是说,当在native端创建FlutterView之后通过setInitialRoute
方法设置Window.defaultRouteName,从而决定启动的是那一个Flutter界面
待解决问题
-
没有搞清楚flutter端MethodChanel是如何处理setInitialRoute方法
但是可以看到Window.defaultRouteName是通过native方法在c++端设置的,推测是在MethodChanel传递消息的时候在c++端进行了处理。
扩展
-
在阅读源码的时候还发现了用另外一种方式可以将Flutter页面嵌入Activity中----Intent附带路由
由于篇幅的关系,将在下一篇Flutter页面嵌入Android Fragment中进行说明