Как я могу создать динамическую / условную навигацию с помощью Jetpack Navigation?

Я столкнулся с интересной проблемой при попытке выполнить динамическую или условную навигацию с помощью библиотеки Jetpack Navigation.

Я имею в виду цель - иметь возможность продолжать использовать nav_graph.xml для управления общим навигационным графом, но одновременно позволять условную навигацию, основанную на некоторых факторах.

Я добавил код ниже, который показывает, куда движется мое решение. Проблема в том, что он по своей сути требует значительного обслуживания для работы условной логики в будущем.

Я действительно хочу, чтобы функция navigateToDashboard в примере могла выполняться либо без параметров, либо без параметров, которые меняются редко. Например, вместо передачи NavDirections может быть передан некоторый идентификатор, который позволит функции navigateToDashboard узнать, какой NavDirections вернуть.

Код для класса, управляющего условной логикой.

class DynamicNavImpl(private val featureFlagService: FeatureFlagService) : DynamicNav {

    override fun navigateToDashboard(navDirectionsMap: Map<Int, NavDirections>): NavDirections {
        val destinationIdRes = if (featureFlagService.isDashboardV2Enabled()) {
            R.id.dashboardV2Fragment
        } else {
            R.id.dashboardFragment
        }

        return navDirectionsMap[destinationIdRes] ?: handleNavDirectionsException(destinationIdRes)
    }

    private fun handleNavDirectionsException(destinationIdRes: Int): Nothing {
        throw IllegalStateException("Destination $destinationIdRes does not have an accompanying set of NavDirections. Are you sure you added NavDirections for it?")
    }
}

Примеры сайтов звонков

navigate(
        dynamicNav.navigateToDashboard(
                mapOf(
                        Pair(R.id.dashboardFragment, PhoneVerificationFragmentDirections.phoneVerificationToDashboard()),
                        Pair(R.id.dashboardV2Fragment, PhoneVerificationFragmentDirections.phoneVerificationToDashboardV2())
                )
        )
)

navigate(
        dynamicNav.navigateToDashboard(
                mapOf(
                        Pair(R.id.dashboardFragment, EmailLoginFragmentDirections.emailLoginToDashboard()),
                        Pair(R.id.dashboardV2Fragment, EmailLoginFragmentDirections.emailLoginToDashboardV2())
                )
        )
)

Посмотрев на сайт звонка, вы могли понять, насколько это может быть проблематично. Если я когда-нибудь захочу добавить новое потенциальное место назначения, скажем dashboardV3Fragment, мне придется переходить на каждый сайт звонков и добавлять еще Pair.

Это почти противоречит цели наличия класса DynamicNavImpl. Вот где я застрял. Я хочу иметь возможность инкапсулировать различные переменные, участвующие в принятии решения, к какому пункту назначения идти, но, похоже, с тем, как реализованы NavDirections, я не могу.




Ответы (1)


Я выбрал несколько разных подходов и остановился на том, что все еще не кажется идеальным, но работает для моего варианта использования.

Я полностью отказался от идеи использования центрального диспетчера динамической навигации. Вместо этого я решил использовать «перенаправление» или «контейнер» Fragment, который решает, что Fragment показывать.

Итак, вот новый код внутри DashboardRedirectFragment

childFragmentManager.beginTransaction().replace(
                R.id.dashboard_placeholder,
                if (featureFlagService.isDashboardV2Enabled()) {
                    DashboardV2Fragment.newInstance()
                } else {
                    DashboardFragment.newInstance()
                }
        ).commit()

Я использую это путем регистрации нового пункта назначения в моем навигационном графе с именем dashboardRedirectFragment, а все, что на графике требует доступа к панели управления, использует dashboardRedirectFragment пункт назначения.

Это полностью инкапсулирует логику динамической навигации в перенаправлении Fragment и позволяет мне продолжать использовать мой навигационный граф, как ожидалось.

person Ryan Simon    schedule 24.01.2020