NA embedded fluent page

Directory introduction

  • 01.Android hosted shuttle container
  • 02. Outdated NA jump shuttle scheme
  • 03. Upgrade version NA to jump to Flutter processing
  • 04. How to handle NA jump parameter transfer
  • 05. Analysis of several problems encountered in thinking
  • 06. Crash when the shutter page is closed
  • 07.Android introduces the essence of fluent
  • 08.Flutter start loading process and optimization

00. Recommendation

  • Flight utils tool class library: https://github.com/yangchong211/YCFlutterUtils
  • flutter mixed project code case: https://github.com/yangchong211/YCHybridFlutter

01.Android hosted shuttle container

  • How does Android host the shuttle page
    • The first case: get a container from Android, open a new page, and load a new fluent page.
    • The second case: get a container from Android and load a fluent page in the NA page. [one page, one part is NA and one part is fluent]
  • How to embed a page written by fluent into an Activity
    • The official provides two ways: through fluterview and fluterfragment.

02. Outdated NA jump shuttle scheme

2.1 using FlutterView

  • NA add FlutterView
    • Create an Activity in NA, create a FlutterView in onCreate, and then add it to the layout.
    • Flutter. The createview () method returns a fluterview, which inherits from the View. We can treat it as an ordinary View.
    • Flutter. The third parameter of the createview () method passes in the "yc_route" string, indicating the route name, which determines the Widget to be displayed in the fluent.
    private void addFlutterView() {
        // Introduce the page written by Flutter through FlutterView
        // Flutter. The createview () method returns a fluterview, which inherits from the View. We can treat it as an ordinary View
        // Flutter. The third parameter of the createview () method passes in the "yc_route" string, indicating the route name, which determines the Widget to be displayed in the fluent
        flutterView = Flutter.createView(this, getLifecycle(), INIT_ROUTE);
        FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(
                FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT);
        //Add to layout
        frameLayout.addView(flutterView, layoutParams);
        //addContentView(flutterView, layout);
    }
    
  • Flutter add page
    • In the runApp() method, through window Defaultroutename can be obtained in the flight The route name passed in the createview() method, that is, "yc_route",
    • Then I wrote a_ The widgetForRoute() method displays the corresponding Widget according to the passed in route string.
    import 'dart:ui';
    import 'package:flutter/material.dart';
    
    void main() => runApp(_widgetForRoute(window.defaultRouteName));
    
    Widget _widgetForRoute(String route) {
      switch (route) {
        case 'yc_route':
          return  MyHomePage(title: 'Yes, this is flutter page');
      }
    }
    
  • Jump to the black screen of the activity where the flitter is located
    • The situation of debug package is obvious, but the release loads quickly. You can provide a loading when entering the fluent page

2.2 using FlutterFragment

  • NA add FlutterView
    • Create an Activity in NA, create a FlutterFragment in onCreate, and then add it to the layout.
    • Flutter. The parameters passed in by the createfragment () method also represent the route name, which is used to determine the Widget to be displayed by the fluent, and return a FlutterFragment. This class inherits from the Fragment and adds the Fragment to the Activity.
    private void addFlutterFragment(){
        // Introduce pages written by Flutter through FlutterFragment
        FragmentTransaction tx = getSupportFragmentManager().beginTransaction();
        // Flutter. The parameter passed in by the createfragment () method also represents the route name, which is used to determine the Widget to be displayed by the fluent
        // Return a FlutterFragment. This class inherits from the Fragment. Just add the Fragment to the Activity.
        FlutterFragment flutterFragment = Flutter.createFragment(INIT_ROUTE);
        tx.replace(R.id.rl_flutter, flutterFragment);
        tx.commit();
    }
    
  • Flutter adds a page, which is the same as above

2.3 problems needing attention

  • Compatibility issue with Flutter version upgrade
    • Due to the update of the fluent version, some APIs in the above description have been abandoned. After a brief check, we found that the error is that the IO. 0 is discarded in version 1.12 of fluent Flutter. Caused by facade package, fluent Createview and fluent The two APIs createfragment cannot be found. They are no longer used
  • How to add parameters to NA jump shuttle
    • NA, this transfer parameter only needs to splice parameters behind the route.
    • Fluent, this received parameter only needs to be parsed.
    • It will be mentioned in the use case of the upgraded version of fluterview below. You can continue to look down

03. Upgrade version NA to jump to Flutter processing

3.1 use the new version of fluterview

  • Brief description of new version
    • We introduced the fluent page through the fluent view. Previously, we used to use io Flutter. The createView() method of the Flutter class in the facade package creates a FlutterView and adds it to the layout of the Activity. However, due to io Flutter. The facade package is discarded, and this method can no longer be used.
    • According to the official documents, there is no convenient API for introducing Flutter at the View level. Therefore, if possible, we should avoid using FlutterView, but it is also feasible to introduce Flutter pages through FlutterView.
    • Note that the FlutterView here is located in io flutter. embedding. The Android package is different from the FlutterView we created earlier (in the io.flutter.view package).
  • NA add FlutterView
    • Create an Activity in NA, create a FlutterView in onCreate, and then add it to the layout.
    • Call the attachToFlutterEngine() method of FlutterView. The function of this method is to display the UI page written by FlutterView. Notice that a flutterengine parameter is passed in here. What is it? The type of flutterengine is flutterengine, which literally means Flutter engine. It is responsible for executing Dart code on the Android side and displaying the UI written by Flutter into the container of FlutterView.
    private void addFlutterView() {
        flutterEngine = new FlutterEngine(this);
        binaryMessenger = flutterEngine.getDartExecutor().getBinaryMessenger();
        flutterEngine.getNavigationChannel().setInitialRoute("yc");
        flutterEngine.getDartExecutor().executeDartEntrypoint(
                DartExecutor.DartEntrypoint.createDefault()
        );
        // Introduce the page written by Flutter through FlutterView
        // The FlutterView here is located in io flutter. embedding. Android package
        // It is different from the FlutterView we created earlier (in the io.flutter.view package).
        // By looking at the source code of FlutterView, you can find that it inherits from FrameLayout, so you can add it like an ordinary View.
        flutterView = new FlutterView(this);
        FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT);
        rlFlutter.addView(flutterView, lp);
    
        //flutterEngine.getNavigationChannel().setInitialRoute("yc");
    
        // Key code to display the shutter page in the shutter view
        // The function of this method is to display the UI page written by Flutter in FlutterView
        // The type of flutterengine is flutterengine, which literally means flutterengine
        // It is responsible for executing Dart code on the Android side and displaying the UI written by Flutter in FlutterView/FlutterActivity/FlutterFragment.
        flutterView.attachToFlutterEngine(flutterEngine);
    
        // The route name loaded by the FlutterEngine is "/". We can specify the initial route name through the following code
        // There is no change in parameter transmission. You can splice parameters directly after the route name
        // todo doesn't work here. Think about why
        // flutterEngine.getNavigationChannel().setInitialRoute("yc");
    }
    
  • Flutter add page
    • In the runApp() method, through window Defaultroutename can be obtained in the flight The route name passed in the createview() method, that is, "yc_route",
    • Then I wrote a_ The widgetForRoute() method displays the corresponding Widget according to the passed in route string.
    import 'dart:ui';
    import 'package:flutter/material.dart';
    
    void main() => runApp(_widgetForRoute(window.defaultRouteName));
    
    Widget _widgetForRoute(String route) {
      switch (route) {
        case 'yc_route':
          return  MyHomePage(title: 'Yes, this is flutter page');
      }
    }
    

3.2 use the new version of FlutterFragment

  • NA can be added in several ways
    • FlutterFragment.createDefault()
      • Through FlutterFragment Createdefault() creates a FlutterFragment. The created Fragment displays a route name of "/". If we need to specify other route names, we can't use this method.
    • FlutterFragment.withNewEngine()
      • Through FlutterFragment Withnewengine() obtains the NewEngineFragmentBuilder object and constructs the FlutterFragment object using the builder mode. You can specify the initial route name through the initialRoute() method.
      • The withNewEngine() method used can also be seen from the name. Each time, a new FlutterEngine object is created to display the Flutter UI, However, it can be seen from the official documents that each FlutterEngine object needs a warm up before displaying the Flutter UI (simply understood as warm-up) period, which will lead to a short blank screen. The solution is to create and start the FlutterEngine in advance, complete the warm-up process, cache the FlutterEngine, and then use the FlutterEngine to display the Flutter UI.
    • FlutterFragment.withCachedEngine
      • Executed flutterenginecache getInstance(). Put ("my_engine_id", flutterEngine) caches the FlutterEngine. The "my_engine_id" passed in here is equivalent to the cache name.
      • After that, call FlutterFragment.. withCachedEngine(“my_engine_id”). build(); Gets the cached flutterfragment object
  • NA add FlutterFragment
    • Create an Activity in NA, create a FlutterFragment in onCreate, and then add it to the layout.
    • Flutter. The parameters passed in by the createfragment () method also represent the route name, which is used to determine the Widget to be displayed by the fluent, and return a FlutterFragment. This class inherits from the Fragment and adds the Fragment to the Activity.
    private void addFlutterView() {
        // Introduce pages written by Flutter through FlutterFragment
        // Through FlutterFragment Createdefault() creates a FlutterFragment
        // Note that the FlutterFragment here is located in io flutter. embedding. Android package
        //FlutterFragment flutterFragment = FlutterFragment.createDefault();
    
        // Through flutterfragment Withnewengine() gets the NewEngineFragmentBuilder object
        FlutterFragment.NewEngineFragmentBuilder fragmentBuilder = FlutterFragment.withNewEngine();
        // The builder pattern is used to construct the FlutterFragment object, and the initial route name can be specified through the initialRoute() method.
        // Pass parameters only need to be spliced after the route name.
        FlutterFragment.NewEngineFragmentBuilder initialRoute = fragmentBuilder.initialRoute("yc");
        FlutterFragment flutterFragment = initialRoute.build();
    
        getSupportFragmentManager()
                .beginTransaction()
                .add(R.id.rl_flutter, flutterFragment)
                .commit();
    
    
        // Existing problems
        // The withNewEngine() method used can also be seen from the name. Each time, a new FlutterEngine object is created to display the Flutter UI,
        // However, from the official documents, we can see that each FlutterEngine object before displaying the Flutter UI
        // A warm-up period is required, which will lead to a short blank screen,
        // The solution is to create and start the FlutterEngine in advance, complete the warm up process, and then cache the FlutterEngine,
        // Then use the FlutterEngine to display the Flutter UI.
        // Solution: FlutterFragmentCachedActivity
    
    
        // How to get the FlutterEngine object? A getFlutterEngine() method is defined in the FlutterFragment,
        // Judging from the method name, it is probably to obtain the FlutterEngine object.
        // When attempting to create a MethodChannel, a flutterfragment. Is passed in getFlutterEngine(). getDartExecutor(),
        // After running, a null pointer exception will be thrown directly. The location of the exception is in the getFlutterEngine() method of FlutterFragment
        // The reason for the error is that the delegate here is null. Search globally and find that the delegate will be assigned in the onAttach() method of the FlutterFragment, which means that the onAttach() method is not executed at this time.
        // I guess this is due to the above-mentioned warm-up mechanism of FlutterEngine, which is a time-consuming process,
        // Therefore, the FlutterFragment does not immediately execute the onAttach() method, which causes us to throw an exception by directly using the getFlutterEngine() method of the FlutterFragment in the onCreate() method of the Activity.
        // When todo calls the following sentence, the null pointer will crash
        // FlutterEngine flutterEngine = flutterFragment.getFlutterEngine();
    }
    
  • Flutter add page
    • This is the same as above

3.3 using the new version of fluteractivity

  • Native introduction of fluent page mode
    • Use the FlutterActivity, which is also located in io flutter. embedding. Under the Android package.
  • First, add code to the manifest file
    <activity
        android:name="io.flutter.embedding.android.FlutterActivity"
        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
        android:hardwareAccelerated="true"
        android:theme="@style/AppTheme"
        android:windowSoftInputMode="adjustResize" />
    
  • Start the Activity directly. The code is as follows
    /**
     * It corresponds to the three methods of creating FlutterFragment introduced
     *
     * FlutterActivity The displayed fluent route is specified when the Intent object is created,
     * The advantage is that it is simpler to use, but the disadvantage is that it is not flexible enough,
     * It cannot be displayed as a part of the native page like fluterview / fluterfragment,
     * Therefore, this method is more suitable for scenarios where the whole page is written by Flutter.
     */
    private void test(){
        // Method 1: the route name displayed by the fluteractivity is "/", which cannot be set
        /*startActivity(
                FlutterActivity.createDefaultIntent(this)
        );*/
    
        // Mode 2: the route name displayed by the FlutterActivity can be set, and a new FlutterEngine object is created each time
        startActivity(
                FlutterActivity
                        .withNewEngine()
                        .initialRoute("yc")
                        .build(this)
        );
    
        // Method 3: the route name displayed by the FlutterActivity can be set, and the cached FlutterEngine object is used
        /*startActivity(
                FlutterActivity
                        .withCachedEngine("my_engine_id")
                        .build(this)
        );*/
    }
    
  • Characteristics of using this method
    • In this way, we do not need to create an Activity ourselves. The shuttle route displayed by the fluteractivity is specified when creating the Intent object. The advantage is that it is easier to use, but the disadvantage is that it is not flexible enough to be displayed as part of the native page like the fluterview / fluterfragment, Therefore, this method is more suitable for scenarios where the whole page is written by Flutter.

3.4 supplementary questions

  • After updating the version of Flutter to 1.17, it is found that the FlutterView cannot be displayed after the above code is run. Why?
    • And the official sample shuttle_ After comparing the view, it is found that the following code is missing:
    @Override
    protected void onResume() {
        super.onResume();
        // flutterEngine.getLifecycleChannel() obtains a LifecycleChannel object, which is similar to MethodChannel,
        // The function is probably to connect the life cycle of the Flutter and the native end.
        flutterEngine.getLifecycleChannel().appIsResumed();
    }
    
    @Override
    protected void onPause() {
        super.onPause();
        flutterEngine.getLifecycleChannel().appIsInactive();
    }
    
    @Override
    protected void onStop() {
        super.onStop();
        flutterEngine.getLifecycleChannel().appIsPaused();
    }
    
  • It may have something to do with the life cycle
    • flutterEngine.getLifecycleChannel() obtains a LifecycleChannel object, which is similar to MethodChannel. Its function is to connect the life cycle of the Flutter and the native end.
    • The appIsResumed(), appIsInactive() and appIsPaused() methods of LifecycleChannel are invoked in the onResume(), onPause() and onStop() methods respectively. The purpose is to synchronize the life cycle between the Flutter end and the native side. After adding the above code, the FlutterView can be displayed normally.
  • Why add in a later version
    • Maybe there are some changes in the rendering mechanism of fluterview. It will be displayed only after receiving the notification sent in the corresponding life cycle method of the native end. The specific principle is to compare the current and previous source codes.

04. How to handle NA jump parameter transfer

4.1 how does Na transfer parameters to Flutter?

  • If you need to pass parameters during page Jump, how can you get the parameters in the native code in the fluent code? In fact, it is very simple. You only need to splice parameters behind the route.
  • Take the method of creating a FlutterView as an example.
    NavigationChannel navigationChannel = flutterEngine.getNavigationChannel();
    String route = "yc?{\"name\":\"Yang Chong\"}";
    navigationChannel.setInitialRoute(route);
    
  • Take the method of creating a FlutterFragment as an example
    FlutterFragment.NewEngineFragmentBuilder fragmentBuilder = FlutterFragment.withNewEngine();
    // The builder pattern is used to construct the FlutterFragment object, and the initial route name can be specified through the initialRoute() method.
    // Pass parameters only need to be spliced after the route name.
    String route = "yc?{\"author\":\"Yang Chong\"}";
    FlutterFragment.NewEngineFragmentBuilder initialRoute = fragmentBuilder.initialRoute(route);
    FlutterFragment flutterFragment = initialRoute.build();
    

4.2 precautions for transmission parameters

  • Use "? Between route name and parameters Like the url in the browser, the parameters are passed in Json format. The reason is that it is convenient for the fluent side to parse, and for some complex data, such as custom objects, it is also easy to use Json serialization.

4.3 Flutter receiving and transmitting parameters

  • At this time, the fluent end passes through the window What defaultroutename gets is the route name + parameter. We need to separate the route name from the parameter, which is just a simple string processing.
      Widget _widgetForRoute() {
        //var route = window.defaultRouteName;
        Map<String, dynamic> router = parseRouter();
        var route = router["route"];
        switch (route) {
          case 'yc':
            return AboutMePage(title: 'Yes, this is flutter page',params : router);
        }
      }
    
      Map<String, dynamic> parseRouter(){
        String url = window.defaultRouteName;
        // Route name, route path name
        String route = url.indexOf('?') == -1 ? url : url.substring(0, url.indexOf('?'));
        // Parameter Json string
        String paramsJson = url.indexOf('?') == -1 ? '{}' : url.substring(url.indexOf('?') + 1);
        // Analytical parameters
        Map<String, dynamic> params = json.decode(paramsJson);
        params["route"] = route;
        return params;
      }
    
  • Via "?" Separate the route name from the parameter, parse the Json string corresponding to the parameter into a Map object, and import the dart:convert package.

05. Analysis of several problems encountered in thinking

5.1 setInitialRoute validation

  • flutterEngine.getNavigationChannel().setInitialRoute("yc") validation problem
    //The first is effective
    private void addFlutterView() {
        flutterEngine = new FlutterEngine(this);
        binaryMessenger = flutterEngine.getDartExecutor().getBinaryMessenger();
        flutterEngine.getNavigationChannel().setInitialRoute("yc");
        flutterEngine.getDartExecutor().executeDartEntrypoint(
                DartExecutor.DartEntrypoint.createDefault()
        );
        flutterView = new FlutterView(this);
        FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT);
        rlFlutter.addView(flutterView, lp);
        flutterView.attachToFlutterEngine(flutterEngine);
    }
    
    //The second is not effective
    private void addFlutterView() {
        flutterEngine = new FlutterEngine(this);
        binaryMessenger = flutterEngine.getDartExecutor().getBinaryMessenger();
        flutterEngine.getDartExecutor().executeDartEntrypoint(
                DartExecutor.DartEntrypoint.createDefault()
        );
        flutterView = new FlutterView(this);
        FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT);
        rlFlutter.addView(flutterView, lp);
    
        // todo doesn't work here. Think about why
        flutterEngine.getNavigationChannel().setInitialRoute("yc");
        flutterView.attachToFlutterEngine(flutterEngine);
    
        // todo doesn't work here. Think about why
        // flutterEngine.getNavigationChannel().setInitialRoute("yc");
    }
    

5.2 flutterFragment.getFlutterEngine() null pointer

  • Usage scenario analysis
    private void createChannel() {
        // When todo calls the following sentence, the null pointer will crash
        FlutterEngine flutterEngine = flutterFragment.getFlutterEngine();
        BinaryMessenger binaryMessenger = flutterEngine.getDartExecutor().getBinaryMessenger();
        nativeChannel = new MethodChannel(binaryMessenger, METHOD_CHANNEL, StandardMethodCodec.INSTANCE);
    }
    
    //Source code
    @Nullable
    public FlutterEngine getFlutterEngine() {
        return delegate.getFlutterEngine();
    }
    
  • The reason for the error is that the delegate here is null
    • After looking at the source code, I found that delegate will be assigned in the onAttach() method of FlutterFragment, which means that the onAttach() method is not executed at this time.
  • problem analysis
    • The warm-up mechanism of FlutterEngine is a time-consuming process. Therefore, FlutterFragment does not immediately execute onAttach() method. As a result, we directly use getFlutterEngine() method of FlutterFragment in onCreate() method of Activity, which will throw an exception.
  • How to solve the problem
    • To solve the problem, wait until the FlutterFragment executes the onAttach() method and calls getFlutterEngine. So how to monitor the execution of this method?

06. Crash when the shutter page is closed

  • The error log is as follows:
         Caused by: java.lang.RuntimeException: Cannot execute operation because FlutterJNI is not attached to native.
            at io.flutter.embedding.engine.FlutterJNI.ensureAttachedToNative(FlutterJNI.java:259)
            at io.flutter.embedding.engine.FlutterJNI.onSurfaceDestroyed(FlutterJNI.java:369)
            at io.flutter.embedding.engine.renderer.FlutterRenderer.stopRenderingToSurface(FlutterRenderer.java:219)
            at io.flutter.embedding.android.FlutterTextureView.disconnectSurfaceFromRenderer(FlutterTextureView.java:223)
            at io.flutter.embedding.android.FlutterTextureView.access$400(FlutterTextureView.java:33)
            at io.flutter.embedding.android.FlutterTextureView$1.onSurfaceTextureDestroyed(FlutterTextureView.java:84)
            at android.view.TextureView.releaseSurfaceTexture(TextureView.java:261)
            at android.view.TextureView.onDetachedFromWindowInternal(TextureView.java:232)
            at android.view.View.dispatchDetachedFromWindow(View.java:22072)
            at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:4747)
            at android.view.ViewGroup.removeAllViewsInLayout(ViewGroup.java:6606)
            at android.view.ViewGroup.removeAllViews(ViewGroup.java:6552)
            at com.yc.fluttercontainer.FlutterEngineActivity.onDestroy(FlutterEngineActivity.java:292)
    
  • The error codes are as follows:
    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (flutterEngine != null) {
            flutterEngine.destroy();
        }
        mFlutterContainer.removeAllViews();
        mFlutterView.removeAllViews();
        if (mRenderSurface != null) {
            // Interrupt memory leak
            ((FixFlutterTextureView) mRenderSurface).setSurfaceTextureListener(null);
        }
    }
    
  • https://blog.csdn.net/cxz200367/article/details/105998930

07.Android introduces the essence of fluent

  • How to understand Android's introduction of the flitter page
    • The introduction of Flutter into Android project is essentially to embed the Widget written by Flutter into the Activity, which is similar to WebView. Container Activity is equivalent to WebView and route is equivalent to url. There are two ways: FlutterView and FlutterFragment. Jump and parameter transfer between pages can be realized with the help of MethodChannel.

08.Flutter start loading optimization

8.1 analyze the startup page flow of the shuttle

  • Through the fluent engine, the initialization of the whole fluent engine starts in the onCreate method
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        this.switchLaunchThemeForNormalTheme();
        super.onCreate(savedInstanceState);
        this.lifecycle.handleLifecycleEvent(Event.ON_CREATE);
        this.delegate = new FlutterActivityAndFragmentDelegate(this);
        //Create binding engine, etc
        delegate.onAttach(this);
        //Used to restore the status of plug-ins and frameworks
        delegate.onActivityCreated(savedInstanceState);
        //Set the window background transparent and hide the status bar
        configureWindowForTransparency();
        //From here, this is our entrance
        setContentView(createFlutterView());
        this.configureStatusBarForFullscreenFlutterExperience();
    }
    
  • Then, looking down, you will call the onCreateView method of the fluteractivityandfragmentdelegate class
    • The FlutterActivityAndFragmentDelegate class delegates the initialization, startup and other operations of the flutter.
    • It is generally understood that a FlutterSurfaceView is created, which inherits from the surfaceview (our flutter page is also rendered on this surface). Then we use it to initialize a FlutterView,
    @NonNull
    View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        Log.v("FlutterActivityAndFragmentDelegate", "Creating FlutterView.");
        this.ensureAlive();
        if (this.host.getRenderMode() == RenderMode.surface) {
            //The fluent application is displayed on the surface, so it will enter here
            FlutterSurfaceView flutterSurfaceView = new FlutterSurfaceView(this.host.getActivity(), this.host.getTransparencyMode() == TransparencyMode.transparent);
            this.host.onFlutterSurfaceViewCreated(flutterSurfaceView);
            //A FlutterView is initialized with flutterSurfaceView
            this.flutterView = new FlutterView(this.host.getActivity(), flutterSurfaceView);
        } else {
            //Otherwise, the application is displayed on the TextureView
            FlutterTextureView flutterTextureView = new FlutterTextureView(this.host.getActivity());
            this.host.onFlutterTextureViewCreated(flutterTextureView);
            //A FlutterView is initialized with flutterTextureView
            this.flutterView = new FlutterView(this.host.getActivity(), flutterTextureView);
        }
    
        this.flutterView.addOnFirstFrameRenderedListener(this.flutterUiDisplayListener);
        //Create a splash view - flutersplashview
        this.flutterSplashView = new FlutterSplashView(this.host.getContext());
        if (VERSION.SDK_INT >= 17) {
            this.flutterSplashView.setId(View.generateViewId());
        } else {
            this.flutterSplashView.setId(486947586);
        }
        //Display the splash screen page
        this.flutterSplashView.displayFlutterViewWithSplash(this.flutterView, this.host.provideSplashScreen());
        Log.v("FlutterActivityAndFragmentDelegate", "Attaching FlutterEngine to FlutterView.");
        //The created surface is bound to the engine
        this.flutterView.attachToFlutterEngine(this.flutterEngine);
        return this.flutterSplashView;
    }
    
  • Then we create a FlutterSplashView (inheriting FrameLayout). It's important to call the displayFlutterViewWithSplash() method.
    • It can be seen here that through splashScreen (an interface), you can see the interface implementation class, create a splashScreenView, and finally add it to the layout of fluent
    public void displayFlutterViewWithSplash(@NonNull FlutterView flutterView, @Nullable SplashScreen splashScreen) {
        if (this.splashScreenView != null) {
            this.removeView(this.splashScreenView);
        }
        //Omit a lot of code
        this.flutterView = flutterView;
        this.addView(flutterView);
        this.splashScreen = splashScreen;
        if (splashScreen != null) {
          if (this.isSplashScreenNeededNow()) {
              Log.v(TAG, "Showing splash screen UI.");
              this.splashScreenView = splashScreen.createSplashView(this.getContext(), this.splashScreenState);
              //Add splashScreenView 
              this.addView(this.splashScreenView);
              flutterView.addOnFirstFrameRenderedListener(this.flutterUiDisplayListener);
          } 
        }
    }
    
    • So when do you remove this startup Splash layout? When creating the FlutterSplashView, a listener for completion events is added. It is removed only after the flutter is successfully loaded.
    public FlutterSplashView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.onTransitionComplete = new Runnable() {
            public void run() {
                FlutterSplashView.this.removeView(FlutterSplashView.this.splashScreenView);
                FlutterSplashView.this.previousCompletedSplashIsolate = FlutterSplashView.this.transitioningIsolateId;
            }
        };
        this.setSaveEnabled(true);
    }
    
  • come to conclusion
    • It can be found that there will be a long process from the display of the flash page to the start of the engine and the display of the shutter page, and the flash page will not be removed until the shutter page is displayed.

8.2 how to optimize the shutter startup screen

  • The first scheme
    • Since the creation and initialization of the Flutter engine takes some time, it also provides a transition scheme (the default is white screen). As shown below, you can set the background
    AndroidManifest.xml Lower
    <meta-data
              android:name="io.flutter.embedding.android.SplashScreenDrawable"
              android:resource="@drawable/launch_background"/>
    
  • The second scheme
    @Nullable
    @Override
    public SplashScreen provideSplashScreen() {
        //Create a custom shutter startup screen view
        return new FlutterSplashView();
    }
    
    public class FlutterSplashView implements SplashScreen {
    
        @Nullable
        @Override
        public View createSplashView(@NonNull Context context, @Nullable Bundle savedInstanceState) {
            View v = new View(context);
            v.setBackgroundColor(Color.WHITE);
            return v;
        }
    
        @Override
        public void transitionToFlutter(@NonNull Runnable onTransitionComplete) {
            onTransitionComplete.run();
        }
    }
    

Flight utils tool class library: https://github.com/yangchong211/YCFlutterUtils

flutter mixed project code case: https://github.com/yangchong211/YCHybridFlutter

Keywords: Flutter

Added by TripleDES on Wed, 22 Dec 2021 10:07:48 +0200