Оптимизируйте свои подборки
Сохраняйте и классифицируйте контент в соответствии со своими настройками.
Рецепт модульной навигации (Hilt)
В этом примере показано, как структурировать многомодульное приложение, используя Navigation 3 и Dagger/Hilt для внедрения зависимостей. Цель — создать децентрализованную архитектуру, в которой навигация определяется и реализуется в отдельных функциональных модулях.
Как это работает
Приложение разделено на несколько модулей:
Модуль app : Это основной модуль приложения. Он инициализирует общий Navigator и внедряет набор EntryProviderInstaller из модулей функций. Затем он использует эти установщики для создания окончательного entryProvider для NavDisplay .
common модуль : Этот модуль содержит основную логику навигации, включая:
- Класс
Navigator , управляющий стеком возврата. - Тип
EntryProviderInstaller , представляющий собой функцию, которую модули функций используют для добавления своих элементов навигации в entryProvider приложения.
Функциональные модули (например, conversation , profile ) : Каждая функция разделена на два подмодуля:
- Модуль
api : определяет публичный API для данной функции, включая маршруты навигации. Это позволяет другим модулям переходить к этой функции, не зная деталей её реализации. - Модуль
impl : Предоставляет реализацию функции, включая её компонуемые компоненты и EntryProviderInstaller , который сопоставляет маршруты функции с её компонуемыми компонентами. Затем этот установщик передаётся модулю app с помощью Dagger/Hilt.
Такой модульный подход обеспечивает четкое разделение задач, что делает кодовую базу более масштабируемой и удобной для сопровождения. Каждая функция отвечает за свою собственную логику навигации, а модуль app лишь объединяет эти части.

Исследовать
Полный рецепт можно посмотреть на GitHub.
arrow_forward package com.example.nav3recipes.modular.hilt
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.components.ActivityRetainedComponent
import dagger.multibindings.IntoSet
// API
object Profile
// IMPLEMENTATION
@Module
@InstallIn(ActivityRetainedComponent::class)
object ProfileModule {
@IntoSet
@Provides
fun provideEntryProviderInstaller() : EntryProviderInstaller = {
entry<Profile>{
ProfileScreen()
}
}
}
@Composable
private fun ProfileScreen() {
val profileColor = MaterialTheme.colorScheme.surfaceVariant
Column(
modifier = Modifier
.fillMaxSize()
.background(profileColor)
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text(
text = "Profile Screen",
style = MaterialTheme.typography.headlineMedium,
color = MaterialTheme.colorScheme.onSurface
)
}
}
package com.example.nav3recipes.modular.hilt
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
import androidx.compose.ui.Modifier
import androidx.navigation3.runtime.entryProvider
import androidx.navigation3.ui.NavDisplay
import com.example.nav3recipes.ui.setEdgeToEdgeConfig
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
@AndroidEntryPoint
class HiltModularActivity : ComponentActivity() {
@Inject
lateinit var navigator: Navigator
@Inject
lateinit var entryProviderScopes: Set<@JvmSuppressWildcards EntryProviderInstaller>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setEdgeToEdgeConfig()
setContent {
Scaffold { paddingValues ->
NavDisplay(
backStack = navigator.backStack,
modifier = Modifier.padding(paddingValues),
onBack = { navigator.goBack() },
entryProvider = entryProvider {
entryProviderScopes.forEach { builder -> this.builder() }
}
)
}
}
}
}
package com.example.nav3recipes.modular.hilt
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material3.Button
import androidx.compose.material3.ListItem
import androidx.compose.material3.ListItemDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.dropUnlessResumed
import com.example.nav3recipes.ui.theme.colors
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.components.ActivityRetainedComponent
import dagger.multibindings.IntoSet
// API
object ConversationList
data class ConversationDetail(val id: Int) {
val color: Color
get() = colors[id % colors.size]
}
// IMPL
@Module
@InstallIn(ActivityRetainedComponent::class)
object ConversationModule {
@IntoSet
@Provides
fun provideEntryProviderInstaller(navigator: Navigator): EntryProviderInstaller =
{
entry<ConversationList> {
ConversationListScreen(
onConversationClicked = { conversationDetail ->
navigator.goTo(conversationDetail)
}
)
}
entry<ConversationDetail> { key ->
ConversationDetailScreen(key) { navigator.goTo(Profile) }
}
}
}
@Composable
private fun ConversationListScreen(
onConversationClicked: (ConversationDetail) -> Unit
) {
LazyColumn(
modifier = Modifier.fillMaxSize(),
) {
items(10) { index ->
val conversationId = index + 1
val conversationDetail = ConversationDetail(conversationId)
val backgroundColor = conversationDetail.color
ListItem(
modifier = Modifier
.fillMaxWidth()
.clickable(onClick = dropUnlessResumed {
onConversationClicked(conversationDetail)
}),
headlineContent = {
Text(
text = "Conversation $conversationId",
style = MaterialTheme.typography.headlineSmall,
color = MaterialTheme.colorScheme.onSurface
)
},
colors = ListItemDefaults.colors(
containerColor = backgroundColor // Set container color directly
)
)
}
}
}
@Composable
private fun ConversationDetailScreen(
conversationDetail: ConversationDetail,
onProfileClicked: () -> Unit
) {
Column(
modifier = Modifier
.fillMaxSize()
.background(conversationDetail.color)
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text(
text = "Conversation Detail Screen: ${conversationDetail.id}",
style = MaterialTheme.typography.headlineMedium,
color = MaterialTheme.colorScheme.onSurface
)
Spacer(modifier = Modifier.height(16.dp))
Button(onClick = dropUnlessResumed(block = onProfileClicked)) {
Text("View Profile")
}
}
}
package com.example.nav3recipes.modular.hilt
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.snapshots.SnapshotStateList
import androidx.navigation3.runtime.EntryProviderScope
import dagger.hilt.android.scopes.ActivityRetainedScoped
typealias EntryProviderInstaller = EntryProviderScope<Any>.() -> Unit
@ActivityRetainedScoped
class Navigator(startDestination: Any) {
val backStack : SnapshotStateList<Any> = mutableStateListOf(startDestination)
fun goTo(destination: Any){
backStack.add(destination)
}
fun goBack(){
backStack.removeLastOrNull()
}
}package com.example.nav3recipes.modular.hilt
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.components.ActivityRetainedComponent
import dagger.hilt.android.scopes.ActivityRetainedScoped
@Module
@InstallIn(ActivityRetainedComponent::class)
object AppModule {
@Provides
@ActivityRetainedScoped
fun provideNavigator() : Navigator = Navigator(startDestination = ConversationList)
}
Контент и образцы кода на этой странице предоставлены по лицензиям. Java и OpenJDK – это зарегистрированные товарные знаки корпорации Oracle и ее аффилированных лиц.
Последнее обновление: 2026-05-09 UTC.
[[["Прост для понимания","easyToUnderstand","thumb-up"],["Помог мне решить мою проблему","solvedMyProblem","thumb-up"],["Другое","otherUp","thumb-up"]],[["Отсутствует нужная мне информация","missingTheInformationINeed","thumb-down"],["Слишком сложен/слишком много шагов","tooComplicatedTooManySteps","thumb-down"],["Устарел","outOfDate","thumb-down"],["Проблема с переводом текста","translationIssue","thumb-down"],["Проблемы образцов/кода","samplesCodeIssue","thumb-down"],["Другое","otherDown","thumb-down"]],["Последнее обновление: 2026-05-09 UTC."],[],[]]