Компонент архитектуры навигации - экран входа
Я планирую реализовать навигацию следующим образом:
Проблема, с которой я сталкиваюсь, заключается в том, когда пользователь находится в LoginFragmennt
и нажимает кнопку назад, он снова загружается LognFragment
то есть. застрял в петле.
Я перехожу к LoginnFragment
используя условную навигацию согласно этому ответу.
Как правильно это реализовать?
1 ответ
Одним из решений, которое я могу предложить, является переопределение внутри вашего действия на методе onBackPressed и завершение действия, если ваше текущее назначение (до того, как обработано заднее нажатие) является фрагментом входа.
override fun onBackPressed() {
val currentDestination=NavHostFragment.findNavController(nav_host_fragment).currentDestination
when(currentDestination.id) {
R.id.loginFragment -> {
finish()
}
}
super.onBackPressed()
}
ИМХО, как я делаю это в моем приложении, немного чище. Просто добавьте эти настройки в график навигации:
<fragment
android:id="@+id/profile_dest"
android:name="com.example.ProfileFragment">
<action
android:id="@+id/action_profile_dest_to_login_dest"
app:destination="@id/login_dest"
app:popUpTo="@+id/profile_dest"
app:popUpToInclusive="true" />
</fragment>
а затем перейдите к входу через
findNavController().navigate(R.id.action_profile_dest_to_login_dest)
,
popUpTo и popUpToInclusive закрыть ProfileFragment
когда мы перейдем к LoginFragment
так что если пользователь переходит обратно, он выходит из приложения.
Вот официальное решение, предложенное Яном Лейком в видео о навигации по навигации от 23 июля 2020 г. на канале YouTube для разработчиков Android. Решение основано на версии навигации 2.3, в которой появилась возможность возвращать результат в предыдущий пункт назначения.
В нашем случае фрагмент логина возвращает
LOGIN_SUCCESSFUL
в предыдущий пункт назначения, это может быть фрагмент профиля или любой другой фрагмент, требующий входа в систему.
class LoginFragment : Fragment(R.layout.login) {
...
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val navController = findNavController()
val savedStateHandle = navController.previousBackStackEntry?.savedStateHandle
?: throw IllegalStateException("the login fragment must not be a start destination")
savedStateHandle.set(LOGIN_SUCCESSFUL, false)
// Hook up your UI, ask for login
userRepository.addLoginSuccessListener {
savedStateHandle.set(LOGIN_SUCCESSFUL, true)
navController.popBackStack()
}
}
}
Фрагмент профиля подписывается на
LOGIN_SUCCESSFUL
состояние и обрабатывает его. Обратите внимание, что лямбда-выражение наблюдателя не будет вызываться до тех пор, пока фрагмент входа в систему не поместит результат и не вернется обратно к фрагменту профиля.
class ProfileFragment : Fragment(R.layout.profile) {
...
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val navController = findNavController()
viewLifecycleOwner.lifecycleScope.launchWhenStarted {
userRepository.userFlow.collect { user ->
if (user == null) {
navController.navigate(R.id.login)
}
}
}
val savedStateHandle = navController.currentBackStackEntry?.savedStateHandle
?: throw IllegalStateException()
savedStateHandle.getLiveData<Boolean>(LOGIN_SUCCESSFUL)
.observe(viewLifecycleOwner) { success ->
if (!success) {
// do whathever we want, just for an example go to
// the start destination which doesn't require login
val startDestination = navController.graph.startDestination
navController.navigate(startDestination, navOptions {
popUpTo(startDestination {
inclusive = true
})
})
}
}
}
}