You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Jetpack Compose:如何将Composable作为参数传递并运行

封装可复用的抽屉导航组件(Jetpack Compose)

核心思路

把抽屉的UI逻辑、状态控制封装成高阶Composable,通过参数接收需要包裹的页面内容,彻底消除导航代码里的重复逻辑。


第一步:封装抽屉基础组件

先写通用的抽屉容器,把抽屉状态、切换逻辑、抽屉内容、页面内容都作为参数传入:

@Composable
fun DrawerWrapper(
    drawerState: DrawerState,
    onDrawerToggle: () -> Unit,
    drawerContent: @Composable () -> Unit,
    pageTitle: String,
    content: @Composable () -> Unit // 接收需要包裹的页面Composable
) {
    ModalNavigationDrawer(
        drawerState = drawerState,
        drawerContent = drawerContent,
        content = {
            // 内置统一的顶部栏(带抽屉切换按钮),避免每个页面重复写
            Scaffold(
                topBar = {
                    TopAppBar(
                        title = { Text(pageTitle) },
                        navigationIcon = {
                            IconButton(onClick = onDrawerToggle) {
                                Icon(Icons.Default.Menu, contentDescription = "打开抽屉")
                            }
                        }
                    )
                }
            ) { paddingValues ->
                // 给页面内容添加内边距,避免被顶部栏遮挡
                Box(modifier = Modifier.padding(paddingValues)) {
                    content()
                }
            }
        }
    )
}

第二步:状态管理方案

根据你的需求选择局部或全局状态:

1. 局部状态(单页面独立控制抽屉)

如果每个页面的抽屉状态不需要共享,就在页面内部创建状态:

@Composable
fun HomeScreen() {
    val drawerState = rememberDrawerState(DrawerValue.Closed)
    val scope = rememberCoroutineScope()

    DrawerWrapper(
        drawerState = drawerState,
        onDrawerToggle = {
            scope.launch {
                drawerState.apply { if (isClosed) open() else close() }
            }
        },
        pageTitle = "首页",
        drawerContent = {
            // 自定义抽屉菜单
            LazyColumn {
                item { Text("我的收藏", modifier = Modifier.padding(16.dp)) }
                item { Text("设置", modifier = Modifier.padding(16.dp)) }
            }
        }
    ) {
        // 这里放原HomeScreen的所有内容
        Column(modifier = Modifier.fillMaxSize(), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally) {
            Text("首页内容区域")
        }
    }
}

2. 全局状态(多页面共享抽屉状态)

如果多个页面需要保持抽屉状态一致(比如打开抽屉切换页面后保持打开),用ViewModel统一管理:

class DrawerViewModel : ViewModel() {
    val drawerState = DrawerState(DrawerValue.Closed)

    fun toggleDrawer() {
        viewModelScope.launch {
            drawerState.apply { if (isClosed) open() else close() }
        }
    }
}

// 在导航宿主中使用全局状态
@Composable
fun AppNavigation() {
    val navController = rememberNavController()
    val drawerViewModel = viewModel<DrawerViewModel>()

    DrawerWrapper(
        drawerState = drawerViewModel.drawerState,
        onDrawerToggle = { drawerViewModel.toggleDrawer() },
        pageTitle = when(navController.currentDestination?.route) {
            "home" -> "首页"
            "profile" -> "个人中心"
            else -> "默认标题"
        },
        drawerContent = {
            // 全局统一的抽屉菜单,点击可跳转页面
            LazyColumn {
                item {
                    TextButton(onClick = {
                        navController.navigate("home")
                        drawerViewModel.toggleDrawer()
                    }) { Text("首页") }
                }
                item {
                    TextButton(onClick = {
                        navController.navigate("profile")
                        drawerViewModel.toggleDrawer()
                    }) { Text("个人中心") }
                }
            }
        }
    ) {
        // 导航宿主,内部页面无需再处理抽屉逻辑
        NavHost(navController = navController, startDestination = "home") {
            composable("home") { HomeScreenContent() } // 只写页面核心内容
            composable("profile") { ProfileScreenContent() }
        }
    }
}

第三步:适配部分页面需要抽屉的场景

如果不是所有页面都需要抽屉,直接在需要的页面路由里单独包裹DrawerWrapper即可:

NavHost(navController = navController, startDestination = "login") {
    // 不需要抽屉的页面,直接写内容
    composable("login") { LoginScreen() }
    
    // 需要抽屉的页面,用DrawerWrapper包裹
    composable("home") {
        val drawerState = rememberDrawerState(DrawerValue.Closed)
        val scope = rememberCoroutineScope()
        
        DrawerWrapper(
            drawerState = drawerState,
            onDrawerToggle = { scope.launch { drawerState.toggle() } },
            pageTitle = "首页",
            drawerContent = { /* 抽屉菜单 */ }
        ) {
            HomeScreenContent()
        }
    }
}

内容的提问来源于stack exchange,提问作者Dolev Dublon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 16:50:25