Flutter笔记--Flutter页面嵌入Android Activity中

前言

  • 上一篇我们已经在Android工程中集成Flutter工程了,也就是说有一些界面是需要使用Flutter写的,那么如何从native界面跳转到flutter界面中呢

  • 因为上一次的项目是测试时创建的,这次我们重新创建全新项目专门用于这一系列的学习。

  • 首先创建一个空的Android项目AndroidWithFlutter

    Android项目目录.png

  • 进入workspace,或其它任意你喜欢的目录,执行flutter create -t module android_with_flutter

    创建flutter module.png

  • 执行成功后即在当前目录创建好了flutter module android_with_flutter

    创建好的flutter module 文件目录.png

  • 进入.android文件夹中找到include_flutter.groovy文件,copy 它的路径


    include_flutter.groovy文件路径.png
  • 在Android项目中的settings.gradle中添加如下配置代码


    引入flutter module.png
  • Sync now一下,可以看到成功引入flutter module


    flutter module目录结构.png
  • 像使用其它android library module一样,在app 的build.gradle中添加依赖

 implementation project(':flutter')
  • Android项目中嵌入flutter,Android项目和flutter项目是分开开发的,下面在Android Studio中打开创建好的flutter module


    Android Studio打开flutter module项目.png
  • 可以看到它自动创建了一个main.dart文件

    flutter module项目.png

  • 运行main.dart效果


    main.dart运行效果.png
  • 进入正题,就以启动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一模一样。运行点击
    启动MainActivity跳转.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) 
  • 点击跳转,可以看到报了java.lang.IllegalStateException: ensureInitializationComplete must be called after startInitialization这样的错误,这里先说解决方式,在MainFlutterActivitysuper.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下丝滑如跳转到原生)

    跳转到flutter界面.gif

  • 好像可以了,其实并没有,是因为刚好我们要跳转的是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);
    }
}
  • 回到MainFlutterActivityFlutter.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寻找路由的顺序
  1. 对于'/'路由,如果[home]属性配置不为空,首先使用
  2. 对于非'/'路由,优先使用[routes]配置的(如果它包含路由的条目的话)
  3. 否则,如果[onGenerateRoute]提供了一个有效的没有被[home]和[routes]处理的路由,调用
  4. 最后,如果前面的寻找都失败了,调用[onUnknownRoute]
  5. 如果所有的都失败了,将不会进行导航
  6. 从其它应用启动时指定了无效的[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中进行说明
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容

  • 每到年末都会反思自己一年的过往,哪些不足,哪些尚可,好像每年的不足都比尚可多很多,所以每年都会痛定思痛:明年,一定...
    青儿姑娘阅读 145评论 4 4
  • 在冰冷的街头回想 那昨日 擦身而过的 淘书,豪饮,做站以及无法再详细描述的运气 然后忘了该向哪里去 假如森林也是曾...
    读叔阅读 280评论 0 2
  • 搬运自本人 CSDN 博客:https://blog.csdn.net/ajianyingxiaoqinghan/...
    琦小虾阅读 1,194评论 0 0
  • 本文参加#感悟三下乡,青春筑梦行#活动,本人承诺,文章内容为原创,且未在其他平台发表过 2017年7月3日,河南科...
    瑞瑞ZZ阅读 260评论 0 0