ViewPager2を使って2ページ目以降にFlutterFragmentを設定すると、完全にページ遷移するまではFlutterFragmentが描画されず真っ白になります。一度表示すると真っ白になりません。
このように遷移途中でもFlutterFragmentが描画されて欲しいです。
なぜそうなるか
作り方
ViewPager2のAdapterはこのように作りました。
class MyFragmentStateAdapter(activity: FragmentActivity) : FragmentStateAdapter(activity) {
override fun getItemCount() = 2
override fun createFragment(position: Int): Fragment {
return if (position == 0)
// AndroidネイティブなFragment
MainFragment()
else
// FlutterFragment
FlutterFragment.withNewEngine()
.shouldAttachEngineToActivity(false)
.build()
}
}
ViewPager2はこのように設定しました。
viewPager.adapter = MyFragmentStateAdapter(this)
// こちらを設定しないとスワイプ開始時に一瞬ひっかかる
viewPager.offscreenPageLimit = 1
Fragmentのライフサイクルを確認する
2ページ目をFlutterFragmentから空のフラグメントにします。
class MyFragmentStateAdapter(activity: FragmentActivity) : FragmentStateAdapter(activity) {
override fun getItemCount() = 2
override fun createFragment(position: Int): Fragment {
return if (position == 0)
MainFragment()
else
EmptyFragment()
}
}
空のフラグメントのライフサイクルを確認します。
class EmptyFragment : Fragment() {
override fun onStart() {
super.onStart()
Log.d("EmptyFragment", "onStart")
}
override fun onResume() {
super.onResume()
Log.d("EmptyFragment", "onResume")
}
override fun onPause() {
super.onPause()
Log.d("EmptyFragment", "onPause")
}
override fun onStop() {
super.onStop()
Log.d("EmptyFragment", "onStop")
}
}
logcatを確認した所、onStartは画面を開いたときに呼ばれますが、onResumeは2ページ目に完全遷移するまで呼ばれませんでした。
# 画面を開いた
D EmptyFragment: onStart
# 2ページ目に完全に遷移
D EmptyFragment: onResume
どうやらFlutterFragmentは初回のonResumeで描画されるようです。この挙動を変更する方法は見つけられませんでした。
ViewPager2ではなくViewPagerを使った場合
コンストラクタのbehavior
フィールドにBEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT
定数を設定した場合はViewPager2と同じくonResumeは2ページ目に完全遷移するまで呼ばれずFlutterFragmentの描画も完全遷移まで開始されません。
class MyFragmentStateAdapter(fm: FragmentManager) :
FragmentStatePagerAdapter(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
override fun getCount() = 2
override fun getItem(position: Int): Fragment {
return if (position == 0)
MainFragment()
else
FlutterFragment.withNewEngine()
.shouldAttachEngineToActivity(false)
.build()
}
}
behavior
フィールドをBEHAVIOR_SET_USER_VISIBLE_HINT
定数にすれば、画面が開かれると同時にonResumeが呼ばれるので、FlutterFragmentの描画もスワイプ中に行われます。しかしその定数は非推奨扱いなので、後ほど技術的負債になる危険性を感じました。
onResumeが完全遷移まで呼ばれない理由
非推奨になったBEHAVIOR_SET_USER_VISIBLE_HINT定数について調べてみたところ、setMaxLifecycleメソッドが紹介されていて、それについて調べてみたところ、こちらの記事に行き着きました。
setUserVisibleHintのdeprecatedとsetMaxLifecycle
この記事の情報を持ってViewPager, ViewPager2のアダプターのソースコードを見てみたところsetMaxLifecycleメソッドの呼び出しで、完全遷移するまではonStartまでしかライフサイクルを遷移させない設定にしていたようです。どうしてもViewPagerを使いたい場合は、ライブラリをフォークしてsetMaxLifecycle呼び出し部分を書き換える必要がありそうです。
最終的にはUIデザインを変更しました
結局、横スワイプによるページ切り替えは諦めて、ボタンによる画面呼び出しにしました。
今回に関してはそちらの方がUIとしては分かりやすいと思いました。