Navigation + BottomNavigationでタブの遷移先を条件に合わせて変更する

NavigationとBottomNavigationを組み合わせた場合、各タブの遷移先は以下のようなmenu xmlに記述したものに固定される。

<?xml version="1.0" encoding="utf-8"?>
<menu
  xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:app="http://schemas.android.com/apk/res-auto"
  >
  <item
    android:id="@+id/fragment_foo"
    android:title="Foo"
    />
  <item
    android:id="@+id/fragment_bar"
    android:title="Bar"
    />
</menu>

タブ Bar の遷移先を条件に合わせて変更する方法について考える。
タブ Bar をタップすると、まずBarSetupFragmentに遷移し、そこでなにかしらの設定を完了させて以降、タブ Bar をタップするとBarFragmentに遷移するとする。

GatewayFragmentを作成するパターン

上述したように、各タブの遷移先はmenu xmlに記述したものに固定されるので、フラグを読み取りBarSetupFragmentかBarFragmentにnavigateしてくれるBarGatewayFragmentを実装し、menu xmlにはBarGatewayFragmentを記述する方法をとってみる。

<?xml version="1.0" encoding="utf-8"?>
<menu
  xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:app="http://schemas.android.com/apk/res-auto"
  >
  <item
    android:id="@+id/fragment_foo"
    android:title="Foo"
    />
  <item
    android:id="@+id/fragment_bar_gateway"
    android:title="Bar"
    />
</menu>
class BarGatewayFragment : Fragment() {

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    val direction = if (isSetupDone)
      BarGatewayFragmentDirections.toBar()
    else
      BarGatewayFragmentDirections.toBarSetup()

    findNavController().navigate(direction)
  }
}

このままだと、BarGatewayFragmentからBarFragment/BarSetupFragmentに遷移した際にBackStackにBarGatewayFragmentが残ってしまうので、navigation xmlにおけるBarGatewayFragmentの各actionは以下のようにし、BarGatewayFragmentがBackStackから消えるようにする。

  <fragment
    android:id="@+id/bar_gateway"
    android:name="io.moyuru.myapplication.GatewayFragment"
    >
    <action
      android:id="@+id/to_bar"
      app:destination="@id/bar"
      app:popUpTo="@id/bar_gateway"
      app:popUpToInclusive="true"
      />
    <action
      android:id="@+id/to_bar_setup"
      app:destination="@id/bar_setup"
      app:popUpTo="@id/bar_gateway"
      app:popUpToInclusive="true"
      />
  </fragment>

ちなみに、サンプルコードではonCreate内でNavController#navigateをcallしているが、onAttach ~ onStartまでのどこのタイミングが一番最適なのかはわかっていない。

とりあえずこの方法で進めてみようと思っている。

ボツにしたパターン

BottomNavigationのmenuを随時clearしてinflateし直すパターン。 地雷臭がぷんぷんしたので試してすらいない。

bottomNavigation.menu.clear()
bottomNavigation.inflateMenu(...)