Ответ
Недавно работал над внедрением Jetpack Compose Navigation с поддержкой глубоких ссылок и анимации переходов. Реализовал nested navigation graphs для модульности и использовал AnimatedContent для плавных анимаций между экранами.
Пример навигации:
NavHost(navController, startDestination = "main") {
composable("main") { MainScreen(onDetailClick = { navController.navigate("detail/$it") }) }
composable(
"detail/{id}",
deepLinks = listOf(navDeepLink { uriPattern = "app://detail/{id}" })
) { backStackEntry ->
DetailScreen(backStackEntry.arguments?.getString("id"))
}
}
Особое внимание уделил обработке бэкстека и восстановлению состояния при deep linking.
Ответ 18+ 🔞
А, ну вот, вспомнил! Сидел я на этой неделе, ебать мои старые костыли, с этой навигацией в Jetpack Compose. Хотел сделать всё по-взрослому: глубокие ссылки, анимации, чтобы не просто тупо экраны переключались, а с красивым переходом. Ёпта, думал, за час сделаю, а в итоге полдня убил, пока во все эти NavHost и backStackEntry въехал.
Вот смотри, в чём прикол. Берёшь ты NavHost, даёшь ему контроллер и стартовый экран. Всё просто, да? А потом начинается магия, или, как я её называю, ёперный театр. Для модульности нужно nested graphs делать — это когда у тебя внутри одного большого графа свои маленькие миры с навигацией. Чтобы не превратить код в одну большую сраку, где всё перемешано.
Основной код навигации выглядит примерно так, тут всё чётко:
NavHost(navController, startDestination = "main") {
composable("main") { MainScreen(onDetailClick = { navController.navigate("detail/$it") }) }
composable(
"detail/{id}",
deepLinks = listOf(navDeepLink { uriPattern = "app://detail/{id}" })
) { backStackEntry ->
DetailScreen(backStackEntry.arguments?.getString("id"))
}
}
Видишь, на основном экране (MainScreen) вешаем клик, который швыряет нас на детали с каким-то id. А в самом определении composable для "detail/{id}" мы ещё и глубокую ссылку прикрутили. Это чтобы, если кто-то по ссылке app://detail/123 тыкнул, приложение сразу на нужный экран открылось. Удобно же, правда?
Но вот самый интересный геморрой начался, когда я стал анимации впиливать. Хотел не стандартный слайд, а что-то покруче. Использовал AnimatedContent — штука мощная, но, блядь, с ней надо аккуратнее, чем с ядрёной вошью. Настроил переходы, а оно в какой-то момент начало глючить при возврате назад. Оказалось, с бэкстеком накосячил — не полностью очищал его при глубокой ссылке, и накопился овердохуища мусорных экранов в истории.
И восстановление состояния! Вот это отдельная песня. Открываешь приложение по глубокой ссылке, а оно должно не просто экран показать, но и все данные подтянуть, как будто пользователь сам через интерфейс прошёл. Пришлось повозиться, чтобы backStackEntry.arguments корректно вытаскивались и передавались в DetailScreen. А то сначала получалось — экран открывается, а id равен null. И сидит пользователь, смотрит на пустоту и думает: «Какого хуя?».
В общем, задача-то выглядит простой — переход между экранами. Но как начинаешь в детали закапываться: deep linking, обработка бэкстека, кастомные анимации, сохранение состояния… Волнение ебать, терпения ноль ебать. Зато когда всё заработало плавно и с любого места приложение в нужную точку открывается — чувствуешь себя прям красавчиком. Почти как Гамлет, который всех послал, но свою архитектуру таки выстроил.