如何在Flutter应用中以原生视图承载Android Fragment(MutableContextWrapper场景)
Flutter嵌入Android Fragment时MutableContextWrapper强转失败的解决方案
问题背景
在Flutter应用中通过PlatformView嵌入Android Fragment时,尝试通过(context as FragmentActivity).supportFragmentManager获取FragmentManager,但传入的上下文是MutableContextWrapper,无法直接强转为FragmentActivity,触发ClassCastException。
解决方案
方法一:递归获取真实的FragmentActivity上下文
编写工具函数递归剥离MutableContextWrapper,获取底层的FragmentActivity实例:
fun Context.getFragmentActivity(): FragmentActivity? { var currentContext = this while (currentContext is ContextWrapper) { if (currentContext is FragmentActivity) { return currentContext } currentContext = currentContext.baseContext } return null }
修改EmbeddedNavigationView中的addFragment和removeFragment方法:
private fun addFragment(fragment: Fragment) { val fragmentActivity = context.getFragmentActivity() ?: return val fragmentManager = fragmentActivity.supportFragmentManager val transaction = fragmentManager.beginTransaction() transaction.add(R.id.fragment_map, fragment) transaction.commit() } private fun removeFragment(fragment: Fragment) { val fragmentActivity = context.getFragmentActivity() ?: return val fragmentManager = fragmentActivity.supportFragmentManager val transaction = fragmentManager.beginTransaction() transaction.remove(fragment) transaction.commit() } override fun onDetachedFromWindow() { fragmentMap?.let { val fragmentActivity = context.getFragmentActivity() ?: return@let fragmentActivity.supportFragmentManager.beginTransaction().remove(it).commit() } super.onDetachedFromWindow() }
方法二:使用FragmentContainerView作为容器并延迟初始化
将EmbeddedNavigationView的父类改为FrameLayout(或FragmentContainerView),并在视图附加到窗口后再添加Fragment:
class EmbeddedNavigationView(context: Context, val viewParams: Map<String, Any>) : FrameLayout(context), PlatformView { private lateinit var fragmentMap: Fragment init { // 先加载布局,确保有fragment_map容器 LayoutInflater.from(context).inflate(R.layout.fragment_container, this, true) initObjects() } private fun initObjects() { fragmentMap = MapFragment() } override fun onAttachedToWindow() { super.onAttachedToWindow() // 视图附加后再添加Fragment addFragment(fragmentMap) } private fun addFragment(fragment: Fragment) { val fragmentActivity = context.getFragmentActivity() ?: return val fragmentManager = fragmentActivity.supportFragmentManager if (fragmentManager.findFragmentById(R.id.fragment_map) == null) { val transaction = fragmentManager.beginTransaction() transaction.add(R.id.fragment_map, fragment) transaction.commit() } } // 实现PlatformView的其他方法... override fun getView(): View = this override fun dispose() { fragmentMap?.let { val fragmentActivity = context.getFragmentActivity() ?: return fragmentActivity.supportFragmentManager.beginTransaction().remove(it).commit() } } }
对应的fragment_container.xml布局:
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <FrameLayout android:id="@+id/fragment_map" android:layout_width="match_parent" android:layout_height="match_parent"/> </FrameLayout>
方法三:直接传递Activity实例到Factory
在MainActivity注册ViewFactory时,直接传入自身实例:
class MainActivity: FlutterActivity() { override fun configureFlutterEngine(flutterEngine: FlutterEngine) { flutterEngine .platformViewsController .registry .registerViewFactory("EmbeddedNavigationView", EmbeddedNavigationFactory(this, mapOf("text" to "hello!"))) } }
修改EmbeddedNavigationFactory和EmbeddedNavigationView,直接使用传入的Activity:
class EmbeddedNavigationFactory( private val activity: FragmentActivity, private val createParams: Map<String, Any>? ) : PlatformViewFactory(StandardMessageCodec.INSTANCE) { override fun create(context: Context, viewId: Int, args: Any?): PlatformView { val viewParams = createParams ?: args as? Map<String, Any> ?: mapOf() return EmbeddedNavigationView(activity, viewParams) } } class EmbeddedNavigationView( private val fragmentActivity: FragmentActivity, val viewParams: Map<String, Any> ) : FrameLayout(fragmentActivity), PlatformView { private lateinit var fragmentMap: Fragment init { LayoutInflater.from(fragmentActivity).inflate(R.layout.fragment_container, this, true) initObjects() initSubView() } private fun initObjects() { fragmentMap = MapFragment() } private fun initSubView() { addFragment(fragmentMap) } private fun addFragment(fragment: Fragment) { val fragmentManager = fragmentActivity.supportFragmentManager val transaction = fragmentManager.beginTransaction() transaction.add(R.id.fragment_map, fragment) transaction.commit() } // 实现PlatformView的其他方法... override fun getView(): View = this override fun dispose() { fragmentMap?.let { fragmentActivity.supportFragmentManager.beginTransaction().remove(it).commit() } } }
关键注意事项
- 避免在
init块中直接操作Fragment,此时视图尚未附加到窗口,上下文可能未完全初始化 - 确保在Fragment添加前检查是否已存在,防止重复添加
- 视图销毁时要正确移除Fragment,避免内存泄漏
内容的提问来源于stack exchange,提问作者Mostafa Mahmoud
相关产品推荐
相关产品推荐

