← Назад к вопросам

Для чего нужен Voyager?

2.0 Middle🔥 101 комментариев
#Архитектура и паттерны#Жизненный цикл и навигация

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Назначение и роль Voyager в Android-разработке

Voyager — это современная, декларативная библиотека для навигации в приложениях на Kotlin Multiplatform (KMP), построенная на основе Jetpack Compose (для Android) и Compose Multiplatform (для других платформ). Её основная цель — предоставить единую, идиоматичную и композ-ориентированную модель навигации, которая полностью заменяет традиционные подходы с использованием NavHostController, NavGraphBuilder и сложных обработок жизненного цикла.

Ключевые проблемы, которые решает Voyager

  1. Упрощение навигации в Compose: Стандартный androidx.navigation-compose, хотя и мощный, остаётся императивным в своей основе (вызовы navigate(...)) и требует ручного управления стеками, зависимостями (внедрение NavController в ViewModel) и сохранением состояния. Voyager предлагает полностью декларативную модель, где экран — это простой композибл.

  2. Управление жизненным циклом и состоянием: Одна из самых сложных задач в навигации — корректное сохранение и восстановление состояния экрана (данные в ViewModel, UiState) при конфигурационных изменениях (поворот экрана) и при навигации. Voyager интегрируется с популярными библиотеками управления состоянием, такими как MVIKotlin, Mobius, ViewModel (де-факто стандарт), и автоматически управляет их жизненным циклом, привязывая его к жизненному циклу экрана в стеке.

  3. Поддержка Kotlin Multiplatform: Voyager создан "из коробки" для KMP. Вы можете использовать единую логику навигации для Android, iOS (через Compose Multiplatform) и Desktop. Это кардинально сокращает усилия по созданию кроссплатформенных приложений.

  4. Типобезопасность и избегание "сырых" строк: Навигация в Voyager основана на классах экранов (Screen), а не на строковых идентификаторах маршрутов (route), что исключает ошибки в runtime из-за опечаток и обеспечивает безопасность на уровне типов.

Основные концепции и использование

Экран (Screen)

Базовый строительный блок. Каждый экран — это класс, реализующий интерфейс Screen. Контент определяется в функции Content.

// 1. Объявляем класс экрана. Он может принимать аргументы.
class ArticleScreen(private val articleId: String) : Screen {
    // 2. Используем аннотацию @Composable для определения UI.
    @Composable
    override fun Content() {
        // 3. Для управления состоянием и жизненным циклом используем
        // модификаторы, например, voyagerViewModel.
        val viewModel: ArticleScreenViewModel = voyagerViewModel()
        val state by viewModel.state.collectAsState()

        Column {
            Text(text = "Статья ID: $articleId")
            Text(text = state.title)
            Button(onClick = { navigator.pop() }) {
                Text("Назад")
            }
        }
    }
}

Навигатор (Navigator)

Объект, доступный внутри Screen.Content(), который предоставляет методы для управления стеком навигации: push, pop, replace, pushAll и другие.

@Composable
override fun Content() {
    // Navigator автоматически предоставляется в Scope экрана.
    val navigator = LocalNavigator.currentOrThrow

    Button(onClick = {
        // Навигация осуществляется вызовом методов с передачей
        // инстанса класса Screen. Типобезопасно!
        navigator.push(ProfileScreen(userId = "123"))
    }) {
        Text("Перейти в профиль")
    }
}

Модификаторы состояния (ScreenModel Modifiers)

Это "магия" Voyager, которая связывает жизненный цикл экрана с жизненным циклом модели состояния (например, ViewModel).

// Стандартный ViewModel
class DetailsViewModel(...) : ViewModel() { ... }

@Composable
override fun Content() {
    // voyagerViewModel создаёт или восстанавливает ViewModel,
    // который будет уничтожен, только когда экран будет окончательно
    // удалён из стека навигации.
    val viewModel: DetailsViewModel = voyagerViewModel()
    // ... использование viewModel.state
}

// Для использования с MVIKotlin
class SearchScreen(...) : Screen {
    @Composable
    override fun Content() {
        // Модификатор `mvikotlinViewModel` обеспечивает правильный
        // жизненный цикл для MVIKotlin Store.
        val store = mvikotlinViewModel(...)
        // ... использование store
    }
}

Преимущества перед стандартными решениями

  • Декларативность и простота: Код навигации становится более читаемым и предсказуемым.
  • Автоматическое управление жизненным циклом: Разработчик меньше думает о onCreate, onDestroy и SavedStateHandle.
  • Идеальная совместимость с MVI/MVVM: Библиотека создана с учётом современных архитектурных паттернов.
  • Мультиплатформенность: Единое решение для всех поддерживаемых платформ.
  • Сообщество и экосистема: Активно развивается и имеет плагины для популярных инструментов, таких как Hilt.

Когда стоит выбирать Voyager?

  • При разработке нового приложения на Jetpack Compose.
  • При создании кроссплатформенного (KMP) приложения с Compose Multiplatform.
  • Когда в проекте используется сложное управление состоянием (MVIKotlin, Mobius) и требуется его надёжная интеграция с навигацией.
  • Когда вы хотите уменьшить количество шаблонного кода, связанного с навигацией и жизненным циклом.

Итог: Voyager — это не просто "ещё одна библиотека навигации", а целостный фреймворк, который переосмысливает навигацию в композ-мире, делая её типобезопасной, декларативной и тесно интегрированной с управлением состоянием, что существенно повышает производительность разработки и надёжность приложения.

Для чего нужен Voyager? | PrepBro