我正在开发一个应用程序,其中有 3 个屏幕:登录屏幕、报价列表屏幕和报价详细信息屏幕。 我想基于用户
isLoggedIn
布尔状态并具有深层链接支持,在登录和报价列表屏幕之间实现简单的条件导航。
我读了一篇好文章并尝试遵循官方导航原则(单一NavHost,固定起始目的地)和深层链接支持。 不幸的是,官方文档并没有完全涵盖它。
这是项目中导航逻辑的基本代码:
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
private val viewModel: MainViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
installSplashScreen()
super.onCreate(savedInstanceState)
setContent {
val isLoggedIn by viewModel.isLoggedIn.collectAsStateWithLifecycle()
val navController = rememberNavController()
// without it the deeplink works
LaunchedEffect(key1 = isLoggedIn) {
if (isLoggedIn) {
navController.navigate(QuotesNavigationGraph) {
popUpTo(0)
}
} else {
navController.navigate(LoginNavigationGraph) {
popUpTo(0)
}
}
}
TemplateTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
NavHost(
navController = navController,
startDestination = QuotesNavigationGraph
) {
navigation(
route = LoginNavigationGraph,
startDestination = LoginNavigationRoute,
) {
loginScreen()
}
navigation(
route = QuotesNavigationGraph,
startDestination = QuotesNavigationRoute
) {
quotesScreen(
onQuoteClick = { quoteId ->
navController.navigateToQuoteDetail(quoteId)
}
)
quoteDetailScreen(
onBackClick = {
navController.navigateUp()
}
)
}
}
}
}
}
}
}
@HiltViewModel
class MainViewModel @Inject constructor(
userDataRepository: UserDataRepository
) : ViewModel() {
val isLoggedIn = userDataRepository.isLoggedIn
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = true
)
}
const val LoginNavigationGraph = "login_graph"
const val LoginNavigationRoute = "login_route"
fun NavGraphBuilder.loginScreen() {
composable(route = LoginNavigationRoute) {
LoginRoute()
}
}
const val QuotesNavigationGraph = "quotes_graph"
const val QuotesNavigationRoute = "quotes_route"
fun NavGraphBuilder.quotesScreen(
onQuoteClick: (Int) -> Unit,
) {
composable(route = QuotesNavigationRoute) {
QuotesRoute(
onQuoteClick = onQuoteClick
)
}
}
const val QuoteDetailNavigationRoute = "quote_detail_route"
const val QuoteIdArg = "quoteId"
fun NavController.navigateToQuoteDetail(
quoteId: Int = -1,
navOptions: NavOptions? = null
) {
this.navigate(
QuoteDetailNavigationRoute.plus("/$quoteId"),
navOptions
)
}
fun NavGraphBuilder.quoteDetailScreen(
onBackClick: () -> Unit
) {
composable(
route = QuoteDetailNavigationRoute.plus("/{$QuoteIdArg}"),
arguments = listOf(
navArgument(QuoteIdArg) { type = NavType.IntType },
),
deepLinks = listOf(
navDeepLink {
uriPattern = "https://template.com/{$QuoteIdArg}"
}
),
) {
QuoteDetailRoute(
onBackClick = onBackClick
)
}
}
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
....
<activity
android:name=".feature.MainActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.Template">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
<category android:name="android.intent.category.DEFAULT" />
<data
android:host="template.com"
android:scheme="https" />
</intent-filter>
</activity>
</application>
</manifest>
一般来说,几乎所有事情都适用于这种方法:
isLoggedIn == true
应用程序打开主屏幕 - 行情列表isLoggedIn == false
应用程序打开登录屏幕,但显示报价列表一秒钟(这不好)这种方法的问题:
isLoggedIn == true
它会打开报价列表而不是报价详细信息屏幕所以我的问题是:
我想要实现的目标:
isLoggedIn == true
应用程序打开主屏幕 - 行情列表isLoggedIn == false
应用程序将打开登录屏幕isLoggedIn == true
深层链接应打开报价详细信息屏幕isLoggedIn == false
应用程序应打开登录屏幕,并且在用户登录后,它应将用户导航至报价详细信息屏幕我在我的项目中所做的与你的代码是一样的:
NavHost(
navController = navController,
startDestination = if (isLoggedIn) QuotesNavigationGraph else LoginNavigationGraph
) { ... }
我知道这不是一个好的做法,因为会使 startDestination 可变,但在您有更好的解决方案之前,这是一个很好的解决方案。