如何让Jetpack Compose的ModalBottomSheetLayout适配WebView内容尺寸
解决Jetpack Compose中ModalBottomSheetLayout自适应WebView内容高度的问题
要让ModalBottomSheetLayout根据WebView加载的内容自动调整高度,核心是让Compose感知到WebView的实际内容高度,再动态调整底部弹窗的尺寸。以下是具体实现方案:
1. 改造WebView,传递内容高度给Compose
修改WebView Composable,新增回调监听页面加载完成事件,获取内容高度并同步给Compose状态:
@SuppressLint("SetJavaScriptEnabled") @Composable fun WebView(url: String, onContentHeightReady: (Int) -> Unit) { AndroidView(factory = { context -> WebView(context).apply { // 先设置为WRAP_CONTENT,让WebView根据内容自身调整高度 layoutParams = LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT) settings.builtInZoomControls = true settings.javaScriptEnabled = true settings.setSupportZoom(true) webChromeClient = WebChromeClient() webViewClient = object : WebViewClient() { override fun onPageFinished(view: WebView, url: String) { super.onPageFinished(view, url) // 将WebView内容高度转换为dp单位,同步给Compose val dpHeight = (view.contentHeight * view.resources.displayMetrics.density).toInt() onContentHeightReady(dpHeight) } override fun shouldOverrideUrlLoading( view: WebView?, request: WebResourceRequest? ): Boolean { return true } override fun onReceivedError( view: WebView?, request: WebResourceRequest?, error: WebResourceError? ) { // 可根据需求添加错误处理逻辑 } } loadUrl(url) } }) }
2. 调整ModalBottomSheetLayout配置
修改BottomSheetScreen,新增状态保存WebView内容高度,同时关闭半展开状态、限制弹窗最大高度:
@OptIn(ExperimentalMaterialApi::class) @Composable fun BottomSheetScreen(url: String, content: @Composable (ModalBottomSheetState) -> Unit) { val bottomSheetState = rememberModalBottomSheetState( initialValue = ModalBottomSheetValue.Hidden, animationSpec = tween( durationMillis = 500, easing = FastOutLinearInEasing ), confirmValueChange = { true }, skipHalfExpanded = true // 关闭半展开状态,直接自适应内容高度 ) // 保存WebView内容高度,初始值为0 val webViewContentHeight = remember { mutableStateOf(0) } // 获取屏幕高度,用于限制弹窗最大高度 val screenHeight = LocalConfiguration.current.screenHeightDp.dp ModalBottomSheetLayout( sheetState = bottomSheetState, sheetShape = RoundedCornerShape(18.dp), sheetContent = { // 限制弹窗最大高度为屏幕的90%,避免内容过长时占满整个屏幕 Box(modifier = Modifier.heightIn(max = screenHeight * 0.9f)) { WebView(url = url) { height -> webViewContentHeight.value = height } } }, // 用WebView内容高度作为弹窗展开高度,初始为0 sheetPeekHeight = webViewContentHeight.value.dp.takeIf { it > 0 } ?: 0.dp ) { content(bottomSheetState) } }
关键细节说明
- WebView布局参数:将
MATCH_PARENT改为WRAP_CONTENT,让WebView先根据内容自身调整高度,再把高度同步给Compose。 - 内容高度转换:
WebView.contentHeight返回的是像素值,乘以屏幕密度转换为dp,保证在不同设备上显示一致。 - 弹窗高度限制:通过
heightIn(max = screenHeight * 0.9f)避免内容过长时弹窗占满屏幕,保留顶部可操作区域。 - 关闭半展开:设置
skipHalfExpanded = true,让弹窗直接展开到内容高度,不需要半展开的中间状态。
内容的提问来源于stack exchange,提问作者Slava
相关产品推荐
相关产品推荐

