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

Jetpack Compose中如何从Bottomsheet向Activity传递数据?

嘿,我明白你在实现Kindle式字体切换时遇到的BottomSheet到Activity的数据传递难题——我之前做类似阅读类App功能时也卡过壳,给你分享几个亲测好用的方案,都是Jetpack Compose里的标准玩法:

方案1:共享ViewModel(最推荐的Jetpack架构方案)

这是官方最推崇的跨组件状态共享方式,Activity和BottomSheet共用同一个ViewModel,状态变化会自动同步,完全不用手动传值。

首先定义一个共享的ViewModel,里面存着当前选中的字体类型:

class FontSettingsViewModel : ViewModel() {
    // 用MutableState保存选中的字体,Compose会自动监听变化
    val selectedFont = mutableStateOf("默认字体")

    // 切换字体的方法,供BottomSheet调用
    fun updateFont(fontName: String) {
        selectedFont.value = fontName
    }
}

然后在Activity里获取这个ViewModel,监听状态变化更新UI:

class ReaderActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            val viewModel = viewModel<FontSettingsViewModel>()
            val showBottomSheet = remember { mutableStateOf(false) }
            
            // 根据选中的字体更新正文样式
            Text(
                text = "阅读内容示例...",
                fontFamily = when(viewModel.selectedFont.value) {
                    "宋体" -> FontFamily.Serif
                    "黑体" -> FontFamily.SansSerif
                    "楷体" -> FontFamily.Cursive
                    else -> FontFamily.Default
                },
                modifier = Modifier.padding(16.dp)
            )

            // 触发显示BottomSheet的按钮
            Button(
                onClick = { showBottomSheet.value = true },
                modifier = Modifier.padding(16.dp)
            ) {
                Text("切换字体")
            }

            // 显示BottomSheet,传入同一个ViewModel
            if (showBottomSheet.value) {
                ModalBottomSheet(onDismissRequest = { showBottomSheet.value = false }) {
                    FontSettingsSheetContent(viewModel) {
                        showBottomSheet.value = false
                    }
                }
            }
        }
    }
}

最后是BottomSheet的内容实现,直接用ViewModel更新状态:

@Composable
fun FontSettingsSheetContent(
    viewModel: FontSettingsViewModel,
    onDismiss: () -> Unit
) {
    Column(modifier = Modifier.padding(16.dp)) {
        listOf("默认字体", "宋体", "黑体", "楷体").forEach { fontName ->
            Text(
                text = fontName,
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(vertical = 12.dp)
                    .clickable {
                        viewModel.updateFont(fontName)
                        onDismiss()
                    },
                color = if (viewModel.selectedFont.value == fontName) Color.Blue else Color.Black,
                fontSize = if (viewModel.selectedFont.value == fontName) 18.sp else 16.sp
            )
        }
    }
}
方案2:回调函数(简单直接,适合小型场景)

如果你的需求很简单,不想引入ViewModel,直接用lambda回调也能搞定,代码量更少。

先定义带回调的BottomSheet:

@Composable
fun FontSettingsBottomSheet(
    onFontSelected: (String) -> Unit,
    onDismiss: () -> Unit
) {
    ModalBottomSheet(onDismissRequest = onDismiss) {
        Column(modifier = Modifier.padding(16.dp)) {
            listOf("默认字体", "宋体", "黑体", "楷体").forEach { fontName ->
                Text(
                    text = fontName,
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(vertical = 12.dp)
                        .clickable {
                            onFontSelected(fontName)
                            onDismiss()
                        }
                )
            }
        }
    }
}

然后在Activity里调用BottomSheet时传入回调,直接处理选中的字体:

class ReaderActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            val selectedFont = remember { mutableStateOf("默认字体") }
            val showBottomSheet = remember { mutableStateOf(false) }
            
            Text(
                text = "阅读内容示例...",
                fontFamily = when(selectedFont.value) {
                    "宋体" -> FontFamily.Serif
                    "黑体" -> FontFamily.SansSerif
                    "楷体" -> FontFamily.Cursive
                    else -> FontFamily.Default
                },
                modifier = Modifier.padding(16.dp)
            )

            Button(
                onClick = { showBottomSheet.value = true },
                modifier = Modifier.padding(16.dp)
            ) {
                Text("切换字体")
            }

            if (showBottomSheet.value) {
                FontSettingsBottomSheet(
                    onFontSelected = { fontName ->
                        selectedFont.value = fontName
                    },
                    onDismiss = { showBottomSheet.value = false }
                )
            }
        }
    }
}
方案3:StateFlow(适合复杂状态流场景)

如果你的字体设置还关联其他状态(比如字号、行间距),用StateFlow来管理会更灵活,适合多状态的复杂场景。

ViewModel里用StateFlow:

class FontSettingsViewModel : ViewModel() {
    private val _selectedFont = MutableStateFlow("默认字体")
    val selectedFont = _selectedFont.asStateFlow()

    fun updateFont(fontName: String) {
        _selectedFont.value = fontName
    }
}

Activity里用collectAsState()监听状态:

@Composable
fun ReaderScreen(viewModel: FontSettingsViewModel = viewModel()) {
    val selectedFont by viewModel.selectedFont.collectAsState()
    val showBottomSheet = remember { mutableStateOf(false) }
    
    // 后续UI逻辑和方案1一致,这里省略重复代码
}

小提示

  • 用ViewModel时,确保Activity和BottomSheet获取的是同一个实例——直接用viewModel()(无参数)默认就是和Activity生命周期绑定的,不用额外处理。
  • 用回调的时候,记得用remember保存状态变量,避免Compose重组导致状态丢失。

内容的提问来源于stack exchange,提问作者Komino Sakie

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 12:08:10