با مجموعهها، منظم بمانید
ذخیره و طبقهبندی محتوا براساس اولویتهای شما.
دستور العمل ناوبری ماژولار (Hilt)
این دستورالعمل نحوه ساختاردهی یک برنامه چند ماژولی با استفاده از Navigation 3 و Dagger/Hilt برای تزریق وابستگی را نشان میدهد. هدف، ایجاد یک معماری جدا شده است که در آن navigation در ماژولهای ویژگی جداگانه تعریف و پیادهسازی میشود.
چگونه کار میکند؟
این برنامه به چندین ماژول تقسیم میشود:
ماژول app : این ماژول اصلی برنامه است. این ماژول یک Navigator مشترک را مقداردهی اولیه میکند و مجموعهای از EntryProviderInstaller ها را از ماژولهای feature تزریق میکند. سپس از این نصبکنندهها برای ساخت entryProvider نهایی برای NavDisplay استفاده میکند.
ماژول common : این ماژول شامل منطق ناوبری اصلی است، از جمله:
- یک کلاس
Navigator که back stack را مدیریت میکند. - یک نوع
EntryProviderInstaller ، که تابعی است که ماژولهای ویژگی برای ارائه ورودیهای ناوبری خود به entryProvider برنامه از آن استفاده میکنند.
ماژولهای ویژگی (مثلاً conversation ، profile ) : هر ویژگی به دو زیرماژول تقسیم میشود:
- ماژول
api : API عمومی برای این ویژگی، شامل مسیرهای ناوبری آن را تعریف میکند. این به ماژولهای دیگر اجازه میدهد بدون نیاز به دانستن جزئیات پیادهسازی آن، به این ویژگی دسترسی پیدا کنند. - ماژول
impl : پیادهسازی این ویژگی، شامل کامپوننتهای آن و یک EntryProviderInstaller که مسیرهای ویژگی را به کامپوننتهای آن نگاشت میکند، فراهم میکند. سپس این نصبکننده با استفاده از Dagger/Hilt در اختیار ماژول app قرار میگیرد.
این رویکرد ماژولار امکان جداسازی دقیق دغدغهها را فراهم میکند و کدبیس را مقیاسپذیرتر و قابل نگهداریتر میسازد. هر ویژگی مسئول منطق ناوبری خود است و ماژول app فقط این قطعات را با هم ترکیب میکند.

کاوش
دستور پخت کامل را در گیتهاب ببینید.
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)
}
محتوا و نمونه کدها در این صفحه مشمول پروانههای توصیفشده در پروانه محتوا هستند. جاوا و OpenJDK علامتهای تجاری یا علامتهای تجاری ثبتشده Oracle و/یا وابستههای آن هستند.
تاریخ آخرین بهروزرسانی 2026-05-09 بهوقت ساعت هماهنگ جهانی.
[[["درک آسان","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 بهوقت ساعت هماهنگ جهانی."],[],[]]