美文网首页flutter
Flutter系列笔记-9.Android原生Flutter混合

Flutter系列笔记-9.Android原生Flutter混合

作者: 缘焕 | 来源:发表于2020-01-05 15:55 被阅读0次

    体验后,发现官方的flutter混合编程不支持androidx,下文为安卓工程支持androidx为例的。
    flutter技术不断更新,以flutter官方更新为准

    本文笔记代码 github

    体验Flutter混合开发

    下面以Android项目为例,体验Flutter混合开发,Android Flutter混合开发,指的是在普通的安卓工程里,把Flutter以普通第三方类库的形式集成进来,官方有两种集成方式,一种是Flutter以aar形式集成到安卓工程,一种是以源码的形式依赖进安卓工程,源码的形式开发时比较方便,下面以flutter工程以安卓源码模块的方法集成进安卓工程为例

    官方原文可以查看这里

    IOS混合开发,请点击这里

    1.创建Android工程

    Android Studio -> New Project -> Empty Activity

    创建工程后,gradle 需要添加一些配置

    因为
    Flutter currently only supports building ahead-of-time (AOT) compiled libraries for armeabi-v7a and arm64-v8a.

    所以

    要在 app目录下的build.gradle下添加

    ndk {
          // Filter for architectures supported by Flutter.
          abiFilters 'armeabi-v7a', 'arm64-v8a'
        }
    

    因为The Flutter Android engine uses Java 8 features.

    所以还要添加

     compileOptions {
        sourceCompatibility 1.8
        targetCompatibility 1.8
      }
    

    添加后如下所示

    android {
        compileSdkVersion 29
        buildToolsVersion "29.0.2"
        defaultConfig {
            applicationId "com.example.androidapplication"
            minSdkVersion 16
            targetSdkVersion 29
            versionCode 1
            versionName "1.0"
            testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
            ndk {
                // Filter for architectures supported by Flutter.
                abiFilters 'armeabi-v7a', 'arm64-v8a'
            }
        }
    
        buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
            }
        }
    
        compileOptions {
            sourceCompatibility 1.8
            targetCompatibility 1.8
        }
    }
    

    创建Flutter Module

    File -> New Flutter Project -> Flutter Module

    创建后,工程如下

    AndroidFlutter混合开发.png

    创建后的flutter_module可以用AndroidStudio用一个新窗口重新打开,并且打开后flutter_module 其实是可以独立编译安装成一个app的。

    配置依赖Flutter Module

    创建完Flutter Module后,在安卓工程项目的settings.gradle里添加

    setBinding(new Binding([gradle: this]))  
    evaluate(new File(                                                   
        settingsDir.parentFile,                                            
        '你的安卓项目名称/flutter_module/.android/include_flutter.groovy'       
    ))
    

    app/build.gradle里添加如下依赖

    implementation project(':flutter')
    

    向 Android 应用中添加 Flutter 页面

    1.在安卓工程的app/src/main/AndroidManifest.xml里添加

        <activity
            android:name="io.flutter.embedding.android.FlutterActivity"
            android:theme="@style/AppTheme"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
            android:hardwareAccelerated="true"
            android:windowSoftInputMode="adjustResize"
            />
    

    2.在安卓代码里调起FlutterActivity

    启动主页

       //kotlin代码
       startFlutterActivity.setOnClickListener {
            startActivity(FlutterActivity.createDefaultIntent(this));
        }
    

    使用路由,打开Flutter指定页面

     startFlutterInitialRoute.setOnClickListener {
            startActivity(FlutterActivity
                .withNewEngine()
                .initialRoute("secondPage")
                .build(this));
        }
    

    使用缓存的 FlutterEngine

    class MyApplication : Application() {
     lateinit var flutterEngine : FlutterEngine
    
     override fun onCreate() {
      super.onCreate()
    
      // Instantiate a FlutterEngine.
      flutterEngine = FlutterEngine(this)
    
      // Start executing Dart code to pre-warm the FlutterEngine.
      flutterEngine.dartExecutor.executeDartEntrypoint(
        DartExecutor.DartEntrypoint.createDefault()
      )
    
      // Cache the FlutterEngine to be used by FlutterActivity.
      FlutterEngineCache
        .getInstance()
        .put("my_engine_id", flutterEngine)
     }
    }
    

    根据id获取对应的缓存FlutterEngine,,缓存的FlutterEngine打开Flutter页面时,明显比withNewEngine的引擎打开Flutter页面快,但是 CachedEngineIntentBuilder没有NewEngineIntentBuilder的initialRoute("")方法

     startFlutterWithCachedEngine.setOnClickListener {
            startActivity(
                FlutterActivity
                    .withCachedEngine("my_engine_id")
                    .build(this)
            );
        }
    

    Start FlutterActivity with transparency 打开有透明度的FlutterActivity#

    官方例子

          startActivity(
                FlutterActivity
                    .withCachedEngine("my_engine_id")
                    .backgroundMode(FlutterActivity.BackgroundMode.transparent)
                    .build(this)
            );
    

    看backgroundMode定义

    @NonNull
    public CachedEngineIntentBuilder backgroundMode(@NonNull BackgroundMode backgroundMode) {
      this.backgroundMode = backgroundMode.name();
      return this;
    }
    

    BackgroundMode所以的 FlutterActivityLaunchConfigs 类竟然不是public的,什么情况?跑不通,先放下

    package io.flutter.embedding.android;
    
    class FlutterActivityLaunchConfigs {
      //省略无关代码
      public enum BackgroundMode {
        /** Indicates a FlutterActivity with an opaque background. This is the default. */
        opaque,
        /** Indicates a FlutterActivity with a transparent background. */
        transparent
      }
    
      private FlutterActivityLaunchConfigs() {}
    }
    

    向 Android 应用中添加 Flutter Fragment

    FlutterFragment api的和FlutterActivity api 的差不多,都有createDefaultIntent withNewEngine withCachedEngine initialRoute之类的方法

    创建一个默认启动Flutter主页的

    FlutterFragment.createDefault();
    

    打开指定Flutter路由

    FlutterFragment.withNewEngine().initialRoute("secondPage").build();
    

    使用缓存的引擎

    FlutterFragment.withCachedEngine("my_engine_id")
                .build()
    

    使用transparencyMode renderMode等

    FlutterFragment
        .withNewEngine()
        .transparencyMode(FlutterView.TransparencyMode.transparent)
        .renderMode(FlutterView.RenderMode.texture)
        .initialRoute("thirdPage")
        .build();
    

    FlutterFragment使用特殊的入口

    一般是使用 main()入口,可以指定其他方法作为入口
    使用有一个dart方法如下

    void mySpecialEntrypoint() {
        runApp(TestEntryPoint());
    }
    

    指定mySpecialEntrypoint方法作为入口

    FlutterFragment flutterFragment = FlutterFragment.withNewEngine()
        .dartEntrypoint("mySpecialEntrypoint")
        .build();
    

    FlutterFragment的渲染模式

    有两种渲染模式 SurfaceView TextureView

    机器翻译文

    FlutterFragment既可以使用SurfaceView来呈现它的Flutter内容,也可以使用TextureView。默认的是SurfaceView,它的性能明显优于TextureView。然而,SurfaceView不能交错在Android视图层次结构的中间。SurfaceView必须是层次结构中最下面的视图,或者是层次结构中最上面的视图。此外,在Android N之前的Android版本中,SurfaceViews不能被动画化,因为它们的布局和渲染与视图层次的其余部分不同步。如果这两个用例是你的应用程序的需求,那么你需要使用TextureView而不是SurfaceView。通过使用纹理渲染模式构建一个FlutterFragment来选择一个TextureView

    // With a new FlutterEngine.
    FlutterFragment flutterFragment = FlutterFragment.withNewEngine()
        .renderMode(FlutterView.RenderMode.texture)
        .build();
    
    // With a cached FlutterEngine.
    FlutterFragment flutterFragment = FlutterFragment.withCachedEngine("my_engine_id")
        .renderMode(FlutterView.RenderMode.texture)
        .build();
    

    显示一个有透明度的FlutterFragment

    默认情况下,FlutterFragment使用SurfaceView呈现不透明的背景。(参见Control FlutterFragment s渲染模式。)对于任何不是由Flutter绘制的像素,该背景都是黑色的。出于性能考虑,使用不透明背景进行呈现是首选的呈现模式。Android上带有透明度的Flutter渲染会对性能产生负面影响。然而,有许多设计需要在Flutter体验中通过底层Android UI显示透明像素。由于这个原因,Flutter在一个FlutterFragment中支持半透明。

    备忘

    SurfaceView和TextureView支持透明度。然而,当一个SurfaceView被指示使用透明渲染时,它会将自己定位在一个比所有其他Android视图更高的z索引上,这意味着它会出现在所有其他视图之上。这是SurfaceView的一个限制。如果可以在所有其他内容之上呈现Flutter体验,那么FlutterFragment的默认surface渲染模式就是您应该使用的渲染模式。然而,如果你需要在你的Flutter体验上面和下面显示Android视图,那么你必须指定一个纹理渲染模式。有关控制渲染模式的信息,请参阅 FlutterFragment的渲染模式

    开启透明度

    // Using a new FlutterEngine.
    FlutterFragment flutterFragment = FlutterFragment.withNewEngine()
        .transparencyMode(FlutterView.TransparencyMode.transparent)
        .build();
    
    // Using a cached FlutterEngine.
    FlutterFragment flutterFragment = FlutterFragment.withCachedEngine("my_engine_id")
        .transparencyMode(FlutterView.TransparencyMode.transparent)
        .build();
    

    shouldAttachEngineToActivity

    将false传递给shouldAttachEngineToActivity()构造器方法可以防止Flutter与周围的Activity进行交互。默认值为true,允许Flutter和Flutter插件与周围的Activity交互。

    Some plugins may expect or require an Activity reference. Ensure that none of your plugins require an Activity before you disable access.

    // Using a new FlutterEngine.
    FlutterFragment flutterFragment = FlutterFragment.withNewEngine()
        .shouldAttachEngineToActivity(false)
        .build();
    
    // Using a cached FlutterEngine.
    FlutterFragment flutterFragment = FlutterFragment.withCachedEngine("my_engine_id")
        .shouldAttachEngineToActivity(false)
        .build();
    

    解决FlutterActivity页面打开时白屏或黑屏的问题

    启动页

    如果启动页继承的是FlutterActivity,先定义一个style

    <style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
        <item name="android:windowBackground">@mipmap/normal_background</item>
    </style>
    

    然后添加到AndroidManifest.xml的启动页Activity下

    <activity android:name=".FlutterLauncherActivity">
        <meta-data
                android:name="io.flutter.embedding.android.NormalTheme"
                android:resource="@style/NormalTheme" />
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
    
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    

    startActivity启动的FlutterActivity

    在AndroidManifest.xml的FlutterActivity下添加

    <meta-data
        android:name="io.flutter.embedding.android.SplashScreenDrawable"
        android:resource="@mipmap/normal_background" />
    

    完整如下

        <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/MyTheme"
            android:windowSoftInputMode="adjustResize">
            <meta-data
                android:name="io.flutter.embedding.android.SplashScreenDrawable"
                android:resource="@mipmap/normal_background" />
        </activity>
    

    启动时就会先显示过渡的图片,而不会白屏或者黑屏

    FlutterFragment添加过渡图片

    官方指导文档就重写provideSplashScreen方法即可,SplashScreen可以自定义,显示Android View

    public class MyFlutterFragment extends FlutterFragment {
        @Override
        protected SplashScreen provideSplashScreen() {
            // Load the splash Drawable.
            Drawable splash = getResources().getDrawable(R.drawable.my_splash);
    
            // Construct a DrawableSplashScreen with the loaded splash Drawable and
            // return it.
            return new DrawableSplashScreen(splash);
        }
    }
    

    但是FlutterFragment如果要重写,还有getActivity getContext getLifecycle三个方法要重写,这里又遇到了androidx的问题,重写失败,先放下。

     override fun getActivity(): Activity? {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }
    
    override fun getContext(): Context {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }
    
    override fun getLifecycle(): android.arch.lifecycle.Lifecycle {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }
    

    相关文章

      网友评论

        本文标题:Flutter系列笔记-9.Android原生Flutter混合

        本文链接:https://www.haomeiwen.com/subject/xgxcactx.html