我们知道,原生Android集成Flutter主要有两种方式,一种是创建flutter module,然后以原生module那样依赖;另一种方式是将flutter module打包成aar,然后在原生工程中依赖aar包,官方推荐aar的方式接入。
如何在原生Android工程中以aar的方式接入Flutter,大家可以参考我之前文章的介绍:原生Android工程接入Flutter aar。今天想给大家分享的是FlutterFragment的使用。
一、Android原生工程
在Android原生开发中,实现底部Tab导航通常有3种方式,分别是:
- RadioGroup + ViewPager + Fragment:能够预加载相邻的Fragment
- FragmentTabHost + Fragment:加载选中的Fragment
- BottomNavigationView:有选中动画效果
代码中引入了一个bottom_nav_menu.xml布局,代码如下:
其中,BottomNavigationView常用的属性如下:
- app:iteamBackground:指的是底部导航栏的背景颜色,默认是主题的颜色
- app:menu:指的是底部菜单(文字和图片都写在这个里面,推荐图片使用矢量图)
- app:itemTextColor:指的是导航栏文字的颜色
- app:itemIconTint:指的是导航栏中图片的颜色
class MainActivity : AppCompatActivity() {private var fragments = mutableListOf()
private var lastfragment = 0override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)initFragment()
initNavigationSelectedListener()
}private fun initFragment() {
val homeFragment = HomeFragment()
val carFragment = CarFragment()
val mineFragment = MineFragment()
fragments.add(homeFragment)
fragments.add(carFragment)
fragments.add(mineFragment)supportFragmentManager.beginTransaction()
.replace(R.id.fl_container, homeFragment)
.show(homeFragment)
.commit()
}private fun switchFragment(index: Int) {
if (lastfragment != index) {
val transaction = supportFragmentManager.beginTransaction()
//隐藏上个Fragment
transaction.hide(fragments[lastfragment])
if (!fragments[index].isAdded) {
transaction.add(R.id.fl_container, fragments[index])
}
transaction.show(fragments[index]).commitAllowingStateLoss()
lastfragment = index
}
}private fun initNavigationSelectedListener() {
findViewById(R.id.bottom_navigation).setOnNavigationItemSelectedListener { item ->
when (item.itemId) {
R.id.nav_home -> {
switchFragment(0)
return@setOnNavigationItemSelectedListener true
}
R.id.nav_car -> {
switchFragment(1)
return@setOnNavigationItemSelectedListener true
}
R.id.nav_me -> {
switchFragment(2)
return@setOnNavigationItemSelectedListener true
}
}
false
}
}
}
二、引入Flutter Module 首先,创建一个Flutter Module工程。创建Flutter Module有两种方式,一种是使用Android Studio进行生成,另一种是直接使用命令行。使用命令行创建flutter module的如下:
flutter create -t module flutter_module
然后,进入到flutter_module,执行flutter build aar命令生成aar包,如果没有任何出错,会在
/flutter_module/.android/Flutter/build/outputs
目录下生成对应的aar包,如下图。文章图片
接下来,我们把生成的aar包拷贝到Android工程的libs中,然后打开app/build.grade添加本地依赖。
repositories {
flatDir {
dirs 'libs'
}
}dependencies {
...
//添加本地依赖
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation(name: 'flutter_relaese-1.0', ext: 'aar')
implementation 'io.flutter:flutter_embedding_debug:1.0.0-f0826da7ef2d301eb8f4ead91aaf026aa2b52881'
implementation 'io.flutter:armeabi_v7a_debug:1.0.0-f0826da7ef2d301eb8f4ead91aaf026aa2b52881'
implementation 'io.flutter:arm64_v8a_debug:1.0.0-f0826da7ef2d301eb8f4ead91aaf026aa2b52881'
implementation 'io.flutter:x86_64_debug:1.0.0-f0826da7ef2d301eb8f4ead91aaf026aa2b52881'
}
然后在外层的build.gradle中申明为本地依赖,代码如下:
buildscript {
repositories {
...
maven {
url "http://download.flutter.io"//flutter依赖
}
}dependencies {
classpath 'com.android.tools.build:gradle:4.0.0'
}
}
三、使用Flutter Module 默认情况下,Android提供了FlutterActivity、Fragment和FlutterView视图,本例子我们讲的是Fragment的使用。
首先,我们创建一个 FlutterEngineGroup 对象,FlutterEngineGroup 可以用来管理多个 FlutterEngine 对象,而多个 FlutterEngine 是可以共享资源的,目的是减少 FlutterEngine 的资源占用,MyApplication的代码如下:
class MyApplication : Application() {lateinit var engineGroup: FlutterEngineGroupoverride fun onCreate() {
super.onCreate()
// 创建FlutterEngineGroup对象
engineGroup = FlutterEngineGroup(this)
}
}
接着,创建一个 FlutterEngineManager 缓存管理类,在 FlutterEngineManager 中创建一个静态方法 flutterEngine,用来缓存FlutterEngine。
object FlutterEngineManager {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 MyApplication
val dartEntrypoint = DartExecutor.DartEntrypoint(
FlutterInjector.instance().flutterLoader().findAppBundlePath(), entryPoint
)
engine = app.engineGroup.createAndRunEngine(context, dartEntrypoint)
// 2. 存入缓存
FlutterEngineCache.getInstance().put(engineId, engine)
}
return engine!!
}}
在上面的代码中,我们会先从中获取缓存的 FlutterEngine ,如果没有则新建一个 FlutterEngine ,然后再缓存起来。
接下来,我们将 FlutterEngine 和 FlutterFragment 进行绑定,如果默认没有提供路由,那么打开的是flutter module的路由首页。如果要指定flutter module的首页,可以使用setInitialRoute()方法。
class HomeFragment : Fragment() {// 1. FlutterEngine对象
private lateinit var engine: FlutterEngine
private var engineId="home_fra"override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 2. 通过FBFlutterEngineManager获取FlutterEngine对象
engine = FlutterEngineManager.flutterEngine(requireActivity(), engineId, "main")
// 3. 用FlutterEngine对象构建出一个FlutterFragment
val flutterFragment = FlutterFragment.withCachedEngine(engineId).build()
// 4. 显示FlutterFragment
parentFragmentManager.beginTransaction().replace(R.id.home_fl, flutterFragment).commit()
}override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_home, container, false)
}
}
我们这里使用缓存的 FlutterEngine 更能节省资源,因为 Bottom Navigation Activity 的 Fragment 来回切换的时候, Fragment 是会重新新建和销毁,比较消耗资源。
如果我们在进入将二级页面时候,返回的时候,还需要将 activity_main.xml 中的 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
}
}}
如果要和Flutter进行数据交互,那么我们可以使用MethodChannel,然后使用setMethodCallHandler即可将Android数据回调给Fluter,代码如下。
class HomeFragment : Fragment() {// 1. FlutterEngine对象
private lateinit var engine: FlutterEngine
private var engineId="home_fra"
private lateinit var channel: MethodChanneloverride fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)initEngine()
initChannel()
}private fun initEngine() {
// 2. 通过FBFlutterEngineManager获取FlutterEngine对象
engine = FlutterEngineManager.flutterEngine(requireActivity(), engineId, "main")
// 3. 用FlutterEngine对象构建出一个FlutterFragment
val flutterFragment = FlutterFragment.withCachedEngine(engineId).build()
// 4. 显示FlutterFragment
parentFragmentManager.beginTransaction().replace(R.id.home_fl, flutterFragment).commit()
}private fun initChannel() {
channel = MethodChannel(engine.dartExecutor.binaryMessenger, "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()
}
}
}
}override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_home, container, false)
}}
接着在Flutter里面是有invokeMethod方法注入即可。
class PluginManager {
static const MethodChannel _channel = MethodChannel('tab_switch');
static Future showTab(Map params) async {
String resultStr = await _channel.invokeMethod('showTab', params);
return resultStr;
}}
【Flutter混合开发之FlutterFragment使用】目前原生移动APP可以在应用集成多个 Flutter Module ,这样就方便我们进行多业务的模块化开发了。除了FlutterActivity、Fragment,在Android中可以使用FlutterView 会稍微复杂点,应使用个 FlutterView 需要绑定生命周期,需要开发者自己去管理FlutterView生命周期。
推荐阅读
- 回顾 Flutter 2021 重要时刻,奉上虎年红包封面喜迎新年!
- Flutter实现左侧边栏导航
- 如何在 Flutter 创建一个后台任务
- Flutter Convex Bottom 底部导航
- Flutter动态化框架Thresh
- 基于 Riverpod 的 Flutter 状态管理
- Flutter 2022 产品路线图发布
- Flutter 插件库
- Windows Running “flutter pub get“ in XXX卡死
- Flutter之下拉刷新,上拉加载更多