T O P

[资源分享]     【Flutter 混合开发】添加 Flutter 到 Android Fragment

  • By - 楼主

  • 2020-10-29 22:20:03
  • Flutter 混合开发系列 包含如下:

    • 嵌入原生View-Android
    • 嵌入原生View-iOS
    • 与原生通信-MethodChannel
    • 与原生通信-BasicMessageChannel
    • 与原生通信-EventChannel
    • 添加 Flutter 到 Android Activity
    • 添加 Flutter 到 Android Fragment
    • 添加 Flutter 到 iOS

    每个工作日分享一篇,欢迎关注、点赞及转发。

    使用新引擎创建 FlutterFragment

    添加 Flutter 到 Fragment 与添加 Activity 基本一样,如果添加到 Activity 满足需求,建议使用 Activity,因为 Activity 更加灵活和易于使用。

    添加到 Fragment 代码:

    class MainActivity : AppCompatActivity() {
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
    
            val fragment = FlutterFragment.createDefault()
            supportFragmentManager
                .beginTransaction()
                .add(R.id.fragment_container, fragment)
                .commit()
        }
    }
    

    activity_main 布局文件修改如下:

    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
        <FrameLayout
            android:id="@+id/fragment_container"
            android:layout_width="0dp"
            android:layout_height="0dp"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintBottom_toTopOf="@+id/button"/>
        <Button
            android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="跳转"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"/>
    
    
    </androidx.constraintlayout.widget.ConstraintLayout>
    

    红色区域就是 FlutterFragment 部分,这里大部分是 Android 原生的知识。

    上面已经加载了 UI,但并不能一些交互和行为,通常情况下,需要将 Activity 的生命周期透传给 FlutterFragment:

    class MainActivity : AppCompatActivity() {
      override fun onPostResume() {
        super.onPostResume()
        flutterFragment!!.onPostResume()
      }
    
      override fun onNewIntent(@NonNull intent: Intent) {
        flutterFragment!!.onNewIntent(intent)
      }
    
      override fun onBackPressed() {
        flutterFragment!!.onBackPressed()
      }
    
      override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<String?>,
        grantResults: IntArray
      ) {
        flutterFragment!!.onRequestPermissionsResult(
          requestCode,
          permissions,
          grantResults
        )
      }
    
      override fun onUserLeaveHint() {
        flutterFragment!!.onUserLeaveHint()
      }
    
      override fun onTrimMemory(level: Int) {
        super.onTrimMemory(level)
        flutterFragment!!.onTrimMemory(level)
      }
    }
    
    

    初始化新引擎路由

    指定引擎路由:

    val fragment = FlutterFragment
        .withNewEngine()
        .initialRoute("one_page")
        .build<FlutterFragment>()
    
    supportFragmentManager
        .beginTransaction()
        .add(R.id.fragment_container, fragment)
        .commit()
    

    使用缓存引擎创建 FlutterFragment

    上面的方式每一个 FlutterFragment 都会创建一个 FlutterEngine(Flutter 引擎),当然 FlutterFragment 也支持 缓存引擎,用法与 Activity 一样,在 MyApplication 启动引擎:

    class MyApplication : Application() {
        lateinit var flutterEngine: FlutterEngine
    
        override fun onCreate() {
            super.onCreate()
            flutterEngine = FlutterEngine(this)
            flutterEngine.dartExecutor.executeDartEntrypoint(
                DartExecutor.DartEntrypoint.createDefault()
            )
            FlutterEngineCache
                .getInstance()
                .put("engine_id", flutterEngine)
    
    
        }
    }
    

    使用:

    val fragment = FlutterFragment
        .withCachedEngine("engine_id")
        .build<FlutterFragment>()
    
    supportFragmentManager
        .beginTransaction()
        .add(R.id.fragment_container, fragment)
        .commit()
    

    初始化缓存引擎路由

    初始化缓存引擎的路由:

    flutterEngine = FlutterEngine(this)
    
    flutterEngine.navigationChannel.setInitialRoute("one_page")
    
    flutterEngine.dartExecutor.executeDartEntrypoint(
        DartExecutor.DartEntrypoint.createDefault()
    )
    FlutterEngineCache
        .getInstance()
        .put("engine_id", flutterEngine)
    

    更改入门点

    默认情况下,FlutterFragment 的 entrypoint(入口点)是 main() 函数,我们可以修改其 entrypoint,

    val fragment = FlutterFragment
        .withNewEngine()
        .dartEntrypoint("newMain")
        .build<FlutterFragment>()
    

    main.dart 文件中添加 entrypoint(入口点):

    void main() => runApp(MyApp());
    
    void newMain()=> runApp(NewApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
    
            primarySwatch: Colors.blue,
          ),
          routes: {
            'one_page':(context){
              return OnePage();
            },
            'two_page':(context){
              return TwoPage();
            }
          },
          home: MyHomePage(title: 'Flutter Demo Home Page'),
        );
      }
    }
    
    
    class NewApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
    
            primarySwatch: Colors.blue,
          ),
          home: TwoPage()
        );
      }
    }
    

    newMain 即新的 entrypoint。

    更改 FlutterFragment 的渲染模式

    FlutterFragment 的渲染模式有两种:SurfaceView 和 TextureView,默认是 SurfaceView,SurfaceView 的性能比 TextureView 好,但其层次结构必须在最顶层或最底层,而且在 Android N之前的Android版本上,无法对 SurfaceView 进行动画处理,因为它们的布局和渲染与其他 View 层次结构不同步,因此要合理选择渲染模式,渲染模式设置方法如下:

    val fragment = FlutterFragment
        .withNewEngine()
        .renderMode(RenderMode.texture)
        .build<FlutterFragment>()
    

    设置 FlutterFragment 透明

    默认情况下,FlutterFragment 使用 SurfaceView 渲染不透明背景。对于Flutter未绘制的任何像素,背景均为黑色。由于性能原因,首选使用不透明背景进行渲染。 Android上具有透明的 Flutter 渲染会对性能产生负面影响。但是,有的时候需要其透明,显示其底下的 UI,因此,Flutter在 FlutterFragment 中支持设置为透明。

    val fragment = FlutterFragment
        .withNewEngine()
        .transparencyMode(TransparencyMode.transparent)
        .build<FlutterFragment>()
    

    将按下放置在 FlutterFragment 的底下,

    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
        <Button
            android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="跳转"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"/>
    
    
        <FrameLayout
            android:id="@+id/fragment_container"
            android:layout_width="0dp"
            android:layout_height="0dp"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"/>
    
    
    </androidx.constraintlayout.widget.ConstraintLayout>
    

    此时 FlutterFragment 的背景已经透明了,但运行时发现并没有透明,按钮也没有显示,这是因为 Flutter 本身没有设置透明,设置Flutter 透明:

    @override
    Widget build(BuildContext context) {
      return Scaffold(
        appBar: AppBar(
          title: Text(widget.title),
        ),
        backgroundColor: Colors.transparent,
        ...
      );
    }
    

    交流

    老孟Flutter博客(330个控件用法+实战入门系列文章):http://laomengit.com

    欢迎加入Flutter交流群(微信:laomengit)、关注公众号【老孟Flutter】:

    本帖子中包含资源

    您需要 登录 才可以下载,没有帐号?立即注册