Compose应用跨可组合项导航失效,多屏适配遇问题
解决方案实现步骤
1. 核心状态管理:用ViewModel统一控制全局状态
创建ViewModel持有选中的分类、推荐项及筛选逻辑,确保不同屏幕模式下状态同步:
class MyCityViewModel : ViewModel() { // 假设你已有Category和Recommendation数据类 private val allCategories = getCategories() // 你的分类数据源 private val allRecommendations = getRecommendations() // 你的推荐数据源 // 默认选中第一个分类 private val _selectedCategory = mutableStateOf(allCategories.first()) val selectedCategory: State<Category> = _selectedCategory // 根据选中分类自动筛选推荐列表 val filteredRecommendations: State<List<Recommendation>> = derivedStateOf { allRecommendations.filter { it.categoryId == selectedCategory.value.id } } // 默认选中筛选列表的第一个推荐项 private val _selectedRecommendation = mutableStateOf(filteredRecommendations.value.firstOrNull()) val selectedRecommendation: State<Recommendation?> = _selectedRecommendation // 切换分类时自动选中首个推荐 fun onCategorySelected(category: Category) { _selectedCategory.value = category _selectedRecommendation.value = filteredRecommendations.value.firstOrNull() } // 切换选中的推荐项 fun onRecommendationSelected(recommendation: Recommendation) { _selectedRecommendation.value = recommendation } }
2. 适配Compact模式:栈式导航实现页面跳转
集成Navigation Compose规范页面跳转,不会破坏多屏适配:
导航路由定义
sealed class Screen(val route: String) { object Categories : Screen("categories") object Recommendations : Screen("recommendations/{categoryId}") { fun createRoute(categoryId: String) = "recommendations/$categoryId" } object Detail : Screen("detail/{recommendationId}") { fun createRoute(recommendationId: String) = "detail/$recommendationId" } }
导航宿主与页面实现
@Composable fun MyCityNavHost(viewModel: MyCityViewModel = viewModel()) { val navController = rememberNavController() NavHost(navController = navController, startDestination = Screen.Categories.route) { composable(Screen.Categories.route) { CategoriesScreen( categories = viewModel.allCategories, onCategoryClick = { category -> navController.navigate(Screen.Recommendations.createRoute(category.id)) } ) } composable(Screen.Recommendations.route) { backStackEntry -> val categoryId = backStackEntry.arguments?.getString("categoryId") ?: "" val recommendations = viewModel.allRecommendations.filter { it.categoryId == categoryId } RecommendationsScreen( recommendations = recommendations, onRecommendationClick = { recommendation -> navController.navigate(Screen.Detail.createRoute(recommendation.id)) }, onBackClick = { navController.popBackStack() } ) } composable(Screen.Detail.route) { backStackEntry -> val recommendationId = backStackEntry.arguments?.getString("recommendationId") ?: "" val recommendation = viewModel.allRecommendations.find { it.id == recommendationId } recommendation?.let { DetailScreen( recommendation = it, onBackClick = { navController.popBackStack() } ) } } } }
3. 适配Expanded模式:横向分栏布局
通过WindowSizeClass判断屏幕类型,Expanded模式下采用三栏布局同步ViewModel状态:
@Composable fun MyCityApp() { val viewModel: MyCityViewModel = viewModel() val windowSizeClass = calculateWindowSizeClass(LocalContext.current) when (windowSizeClass.widthSizeClass) { WindowWidthSizeClass.Expanded -> { Row(modifier = Modifier.fillMaxSize()) { // 左侧分类栏 CategoriesScreen( categories = viewModel.allCategories, selectedCategory = viewModel.selectedCategory.value, onCategoryClick = viewModel::onCategorySelected, modifier = Modifier.weight(1f) ) // 中间推荐列表栏 RecommendationsScreen( recommendations = viewModel.filteredRecommendations.value, selectedRecommendation = viewModel.selectedRecommendation.value, onRecommendationClick = viewModel::onRecommendationSelected, modifier = Modifier.weight(2f) ) // 右侧详情栏 viewModel.selectedRecommendation.value?.let { DetailScreen( recommendation = it, modifier = Modifier.weight(3f) ) } ?: EmptyDetailScreen(modifier = Modifier.weight(3f)) } } else -> { // Compact模式使用导航宿主 MyCityNavHost(viewModel) } } }
4. 关键细节处理
- 默认展示首个推荐:在
onCategorySelected方法中,切换分类后自动将选中推荐设为筛选列表的首个条目,保证Expanded模式下详情栏自动更新。 - 筛选逻辑自动化:通过
derivedStateOf监听分类变化,自动更新推荐列表,无需手动维护筛选结果。 - 状态全局同步:所有交互操作都通过ViewModel修改状态,确保两种模式下状态一致。
内容的提问来源于stack exchange,提问作者Alexandru Gheorghe
相关产品推荐
相关产品推荐

