Flutter混合开发—Android篇

作者: chonglingliu | 来源:发表于2021-05-25 13:31 被阅读0次

    上篇文章我们介绍了如何将Flutter模块混入iOS项目中,本篇文章我们来介绍下Android项目混入Flutter模块的方法。

    建议先阅读一下Flutter混合开发—iOS篇,本文中提到的一些与iOS篇中相同的内容就不会再介绍了。

    现在就进入Flutter和Android进行混合开发的实现过程。

    搭建Android项目

    首先我们搭建一个首页为Bottom Navigation Activity的安卓项目,然后修改代码将三个Fragment重命名为HomeFragmentChannelFragmentMineFragment

    项目
    fragment_home.xml
    <?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"
        android:background="#2196F3"
        tools:context=".ui.home.HomeFragment">
        
        // 这个FrameLayout会被用来显示Flutter Module内容
        <FrameLayout
            android:id="@+id/main_fl"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:paddingBottom="56dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    
    </androidx.constraintlayout.widget.ConstraintLayout>
    

    搭建完成后,App的效果如下图所示:

    效果图

    我们接下来的工作是把FlutterMainApp(),ChannelApp()MineApp()分别加入到上面提到的三个Fragment中。

    Android项目引入Flutter Module

    • 首先确保Android Studio安装了Flutter plugin(安装Flutter插件的过程略过)

    • 使用 File > New > Import Module ... -> 选择flutter module ,然后指定一个module name

    新建Module
    • 填写相应的信息
    信息

    图中有一个警告,是因为我第一操作的时候没有截图,第二次重复操作的警告。

    • 点击确定,等待Gradle sync完成

    Android项目集成Flutter Module

    创建一个FlutterEngineGroup对象

    FlutterEngineGroup可以用来管理多个FlutterEngine对象,多个FlutterEngine之间是可以共享资源的,这样建立多个FlutterEngine占用的资源相对会少一下。

    FBApplication.kt
    class FBApplication: Application() {
    
        lateinit var engineGroup: FlutterEngineGroup
    
        override fun onCreate() {
            super.onCreate()
            // 创建FlutterEngineGroup对象
            engineGroup = FlutterEngineGroup(this)
        }
    
    }
    
    AndroidManifest.xml
    <application
        android:name=".FBApplication"
        // 省略...
        >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
    

    创建一个FBFlutterEngineManager缓存管理类

    我们在FBFlutterEngineManager中创建一个静态方法flutterEngine:

    object FBFlutterEngineManager {
    
        fun flutterEngine(context: Context, engineId: String, entryPoint: String): FlutterEngine {
            // 1. 从缓存中获取FlutterEngine
            var engine = FlutterEngineCache.getInstance().get(engineId)
            if (engine == null) { 
                // 如果缓存中没有FlutterEngine
                // 1. 新建FlutterEngine,执行的入口函数是entryPoint
                val app = context.applicationContext as FBApplication
                val dartEntrypoint = DartExecutor.DartEntrypoint(FlutterInjector.instance().flutterLoader().findAppBundlePath(), entryPoint)
                engine = app.engineGroup.createAndRunEngine(context, dartEntrypoint)
                // 2. 存入缓存
                FlutterEngineCache.getInstance().put(engineId, engine)
            }
            return engine!!
        }
    
    }
    

    这里我们使用了FlutterEngineCache缓存类,先从中获取缓存的FlutterEngine,如果没有取到,则新建一个FlutterEngine,然后缓存起来。

    Fragment中嵌入Flutter Module

    我们这一步是将FlutterEngineFlutterFragment进行绑定,然后显示。

    class HomeFragment : Fragment() {
        
        // 1. FlutterEngine对象
        private lateinit var engine: FlutterEngine
    
        override fun onCreateView(
                inflater: LayoutInflater,
                container: ViewGroup?,
                savedInstanceState: Bundle?
        ): View? {
            return inflater.inflate(R.layout.fragment_home, container, false)
        }
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            
            // 2. 通过FBFlutterEngineManager获取FlutterEngine对象
            engine = FBFlutterEngineManager.flutterEngine(requireActivity(), R.id.main_fl.toString(), "main")
            // 3. 用FlutterEngine对象构建出一个FlutterFragment
            val flutterFragment = FlutterFragment.withCachedEngine(R.id.main_fl.toString()).build<FlutterFragment>()
            // 4. 显示FlutterFragment
            parentFragmentManager.beginTransaction().replace(R.id.main_fl, flutterFragment).commit()
        }
    
    }
    

    我们这里使用缓存的FlutterEngine更能节省资源,因为Bottom Navigation ActivityFragment来回切换的时候,Fragment是会重新新建和销毁的。

    下面就是实现的效果图:

    效果图

    编写插件代码

    和iOS类似,我们需要在进入将二级页面时候,将activity_main.xml中的BottomNavigationView隐藏,回到一级页面的时候将BottomNavigationView显示出来。

    • 添加显示和隐藏的方法
    class MainActivity : AppCompatActivity() {
    
        fun switchBottomView(show: Boolean) {
            val navView: BottomNavigationView = findViewById(R.id.nav_view)
            if (show) {
                navView.visibility = View.VISIBLE
            } else {
                navView.visibility = View.GONE
            }
        }
    
    }
    
    • MethodChannel 注册回调
    var channel: MethodChannel = MethodChannel(engine.dartExecutor.binaryMessenger, "fbmovie.com/tab_switch")
    channel.setMethodCallHandler { call, result ->
        when (call.method) {
            "showTab" -> {
                val activity = requireActivity() as MainActivity
                activity.switchBottomView(true)
                result.success(null)
        }
        "hideTab" -> {
            val activity = requireActivity() as MainActivity
            activity.switchBottomView(false)
            result.success(null)
        }
        else -> {
            result.notImplemented()
        }
    }
    }
    

    代码

    FBApplication.kt
    
    class FBApplication: Application() {
    
        lateinit var engineGroup: FlutterEngineGroup
    
        override fun onCreate() {
            super.onCreate()
            engineGroup = FlutterEngineGroup(this)
        }
    
    }
    
    FBFlutterEngineManager.kt
    object FBFlutterEngineManager {
    
        fun flutterEngine(context: Context, engineId: String, entryPoint: String): FlutterEngine {
            var engine = FlutterEngineCache.getInstance().get(engineId)
            if (engine == null) { //如果是空的就新建,然后存起来
                val app = context.applicationContext as FBApplication
                val dartEntrypoint = DartExecutor.DartEntrypoint(FlutterInjector.instance().flutterLoader().findAppBundlePath(), entryPoint)
                engine = app.engineGroup.createAndRunEngine(context, dartEntrypoint)
                FlutterEngineCache.getInstance().put(engineId, engine)
            }
            return engine!!
        }
    
    }
    
    MainActivity.kt
    class MainActivity : AppCompatActivity() {
    
        override fun onCreate(savedInstanceState: Bundle?) {
            // 修改状态栏颜色
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
            }
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
            val navView: BottomNavigationView = findViewById(R.id.nav_view)
    
            val navController = findNavController(R.id.nav_host_fragment)
            navView.setupWithNavController(navController)
            
        }
    
        fun switchBottomView(show: Boolean) {
            val navView: BottomNavigationView = findViewById(R.id.nav_view)
            if (show) {
                navView.visibility = View.VISIBLE
            } else {
                navView.visibility = View.GONE
            }
        }
    
    }
    
    HomeFragment.kt
    class HomeFragment : Fragment() {
    
        private lateinit var engine: FlutterEngine
        private lateinit var channel: MethodChannel
    
        override fun onCreateView(
                inflater: LayoutInflater,
                container: ViewGroup?,
                savedInstanceState: Bundle?
        ): View? {
            return inflater.inflate(R.layout.fragment_home, container, false)
        }
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
    
            engine = FBFlutterEngineManager.flutterEngine(requireActivity(), R.id.main_fl.toString(), "main")
            val flutterFragment = FlutterFragment.withCachedEngine(R.id.main_fl.toString()).build<FlutterFragment>()
            parentFragmentManager.beginTransaction().replace(R.id.main_fl, flutterFragment).commit()
    
            channel = MethodChannel(engine.dartExecutor.binaryMessenger, "fbmovie.com/tab_switch")
            channel.setMethodCallHandler { call, result ->
                when (call.method) {
                    "showTab" -> {
                        val activity = requireActivity() as MainActivity
                        activity.switchBottomView(true)
                        result.success(null)
                    }
                    "hideTab" -> {
                        val activity = requireActivity() as MainActivity
                        activity.switchBottomView(false)
                        result.success(null)
                    }
                    else -> {
                        result.notImplemented()
                    }
                }
            }
        }
    }
    

    总结

    目前原生移动应用能集成多个Flutter Module,这样使用起来就方便多了。

    Android中也可以使用FlutterView, 在一个Activity或者Fragment中可以展示多个FlutterView,但是使用个FlutterView需要绑定生命周期,使用起来稍显复杂。

    相关文章

      网友评论

        本文标题:Flutter混合开发—Android篇

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