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

Compose中ModalBottomSheet在API29及以下版本的imePadding问题

解决Compose ModalBottomSheet在API29及以下的键盘适配问题

问题描述

在API29及以下的Android系统中,Compose的ModalBottomSheet无法正确识别imePadding:键盘弹出时会遮挡底部内容,且不会触发重组;但API30及以上版本表现正常,键盘弹出会触发重组,Sheet可滚动显示全部内容。已在Manifest中为Activity添加adjustResize属性,该属性对普通页面有效,但对ModalBottomSheet无效。


解决方案

方案1:手动监听键盘高度,动态调整内边距

API29及以下系统不会自动通知Compose键盘高度变化,通过原生View的全局布局监听获取键盘高度,动态给Sheet内容添加底部内边距。

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun FilterScreen(
    component: SearchComponent,
    onDismiss: () -> Unit
) {
    val sheetState = rememberModalBottomSheetState(
        skipPartiallyExpanded = true
    )
    
    val countriesIS = remember { MutableInteractionSource() }
    val filterState by component.filterState.collectAsState()

    var showBottomSheet by remember { mutableStateOf(false) }
    val keyboardHeight = remember { mutableStateOf(0.dp) }
    val context = LocalContext.current

    // 监听键盘高度变化
    DisposableEffect(Unit) {
        val rootView = (context as Activity).window.decorView.rootView
        val layoutListener = ViewTreeObserver.OnGlobalLayoutListener {
            val displayFrame = Rect()
            rootView.getWindowVisibleDisplayFrame(displayFrame)
            val screenHeight = rootView.rootView.height
            val keyboardPx = screenHeight - displayFrame.bottom
            // 过滤误判(比如系统导航栏高度)
            keyboardHeight.value = if (keyboardPx > screenHeight * 0.15) {
                (keyboardPx / context.resources.displayMetrics.density).dp
            } else {
                0.dp
            }
        }
        rootView.viewTreeObserver.addOnGlobalLayoutListener(layoutListener)
        onDispose {
            rootView.viewTreeObserver.removeOnGlobalLayoutListener(layoutListener)
        }
    }

    ModalBottomSheet(
        onDismissRequest = {
            showBottomSheet = false
            onDismiss()
        },
        sheetState = sheetState,
        shape = RoundedCornerShape(topEnd = 50.dp, topStart = 50.dp),
        containerColor = Color.White
    ) {
        Column(
            modifier = Modifier
                .fillMaxHeight()
                .verticalScroll(rememberScrollState())
                // 替换imePadding为动态计算的键盘高度
                .padding(bottom = keyboardHeight.value),
        ) {
            AutocompleteTextEdit(
                labelText = Strings.getString("citizenship"),
                interactionSource = countriesIS,
                text = filterState.countriesString,
                items = component.countries,
                onValueChange = { newText -> component.onEvent(SearchEvents.UpdateCountries(newText)) },
            )
            Spacer(Modifier.requiredHeight(12.dp))
            Row {
                DefaultButton(
                    modifier = Modifier
                        .weight(1f)
                        .requiredHeight(mainButtonHeight)
                        .padding(start = mainSideMargin, end = halfMainSideMargin),
                    text = Strings.getString("clear"),
                    isFilled = false,
                ) {
                    component.onEvent(SearchEvents.ClearFilter)
                }
                DefaultButton(
                    modifier = Modifier
                        .weight(1f)
                        .requiredHeight(mainButtonHeight)
                        .padding(start = 8.dp, end = mainSideMargin),
                    text = Strings.getString("search"),
                ) {
                    component.onEvent(SearchEvents.ApplyFilter)
                }
            }
            Spacer(Modifier.requiredHeight(250.dp))
        }
    }
}

方案2:手动获取WindowInsets键盘参数

部分低版本Compose对imePadding的处理存在bug,可手动提取键盘Insets并应用:

@Composable
private fun KeyboardPadding(): Modifier {
    val imeInsets = WindowInsets.ime.getInsets(LocalWindowInsets.current)
    val keyboardBottom = remember { mutableStateOf(0.dp) }
    LaunchedEffect(imeInsets) {
        keyboardBottom.value = imeInsets.bottom.toDp()
    }
    return Modifier.padding(bottom = keyboardBottom.value)
}

// 使用时替换原有的imePadding:
Column(
    modifier = Modifier
        .fillMaxHeight()
        .verticalScroll(rememberScrollState())
        .then(KeyboardPadding()),
) {
    // 原有内容不变
}

方案3:动态限制Sheet最大高度

结合屏幕高度和键盘高度,强制Sheet的最大高度为可用窗口高度,避免内容被遮挡:

@Composable
fun FilterScreen(
    component: SearchComponent,
    onDismiss: () -> Unit
) {
    val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
    val configuration = LocalConfiguration.current
    val screenHeight = remember(configuration) { configuration.screenHeightDp.dp }
    val keyboardHeight = remember { mutableStateOf(0.dp) }
    // 同方案1的键盘监听逻辑...

    ModalBottomSheet(
        onDismissRequest = { showBottomSheet = false; onDismiss() },
        sheetState = sheetState,
        shape = RoundedCornerShape(topEnd = 50.dp, topStart = 50.dp),
        containerColor = Color.White,
        // 限制Sheet最大高度
        modifier = Modifier.heightIn(max = screenHeight - keyboardHeight.value)
    ) {
        Column(
            modifier = Modifier
                .fillMaxHeight()
                .verticalScroll(rememberScrollState()),
        ) {
            // 原有内容不变
        }
    }
}

方案说明

方案1是最可靠的适配方式,直接通过原生View监听获取键盘高度,不受Compose版本或系统API限制;方案2、3可根据你的Compose版本尝试,若方案1生效优先选用。

内容的提问来源于stack exchange,提问作者Al Sh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 23:15:57