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

如何修复Compose Material3中ModalBottomSheet与键盘的空隙问题

问题:Material3 1.1.1中ModalBottomSheet与键盘间出现空隙

在使用androidx.compose.material3:material3:1.1.1版本时,给TextField设置焦点后,键盘弹出时ModalBottomSheet与键盘之间会出现空隙。尝试过修改ModalBottomSheet的WindowInsets,以及在其内容中添加Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.systemBars))来修复,但都没有解决问题。最终通过实现一个类似Dialog的BottomSheet满足了需求,但ModalBottomSheet组件本身的问题仍未解决。

ModalBottomSheet与键盘间的空隙

相关实现代码

RenameBottomSheet 实现

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun RenameBottomSheet(
    newPersonalizedName: MutableState<String>,
    showState: MutableState<Boolean>,
    onConfirmClick: () -> Unit
) {
    val bottomSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
    val suffix = "${newPersonalizedName.value.length}/30"

    DragHandleBottomSheet(
        bottomSheetState = bottomSheetState,
        showState = showState,
        dragHandle = {
            BottomSheetHeader(
                iconResId = R.drawable.ic_edit,
                titleResId = R.string.rename
            )
        }) {
        Column(
            modifier = Modifier
                .background(Theme.colorScheme.bottomBarBackgroundColor)
                .padding(16.dp),
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            InputTextField(
                modifier = Modifier.fillMaxWidth(),
                text = newPersonalizedName.value,
                onValueChange = { if (it.length <= 30) newPersonalizedName.value = it },
                label = stringResource(id = R.string.rename_label),
                suffix = suffix
            )
            Spacer(modifier = Modifier.size(28.dp))
            Row(
                modifier = Modifier.fillMaxWidth(),
                horizontalArrangement = Arrangement.Center
            ) {
                ButtonWithoutBorder(
                    text = stringResource(id = R.string.cancel),
                    modifier = Modifier.weight(1f),
                    onClick = { showState.value = false }
                )
                Spacer(modifier = Modifier.size(12.dp))
                DefaultButton(
                    modifier = Modifier.weight(1f),
                    textResId = R.string.save,
                    onClick = onConfirmClick
                )
            }
        }
    }
}

DragHandleBottomSheet 实现

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun DragHandleBottomSheet(
    bottomSheetState: SheetState,
    showState: MutableState<Boolean>,
    dragHandle: @Composable (() -> Unit),
    content: @Composable ColumnScope.() -> Unit,
) {
    if (showState.value) {
        ModalBottomSheet(
            modifier = Modifier.imePadding(),
            onDismissRequest = { showState.value = false },
            sheetState = bottomSheetState,
            scrimColor = VtbTheme.colorScheme.scrim,
            containerColor = VtbTheme.colorScheme.background,
            shape = RoundedCornerShape(topStart = 24.dp, topEnd = 24.dp),
            tonalElevation = 0.dp,
            dragHandle = dragHandle,
        ) {
            content()
            Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.systemBars))
        }
    }
}

ButtonWithoutBorder 实现

@Composable
fun ButtonWithoutBorder(
    modifier: Modifier = Modifier,
    text: String? = null,
    textResId: Int? = null,
    isEnabled: Boolean = true,
    onClick: () -> Unit = {}
) {
    val buttonText = if (textResId != null) stringResource(id = textResId) else text.orEmpty()
    val alpha = if (isEnabled) 1f else 0.4f

    Box(
        modifier = modifier
            .requiredHeight(44.dp)
            .alpha(alpha)
            .background(
                color = Theme.colorScheme.blueSecondary,
                shape = Theme.shapes.compact
            )
            .clickable(
                interactionSource = remember { MutableInteractionSource() },
                indication = null,
                onClick = { onClick.invoke() }
            ),
        contentAlignment = Alignment.Center
    ) {
        Text(
            text = buttonText,
            style = Theme.typography.Regular_14.copy(
                color = Theme.colorScheme.secondaryButtonTextColorDefault
            ),
        )
    }
}

DefaultButton 实现

@Composable
fun DefaultButton(
    modifier: Modifier = Modifier,
    text: String? = null,
    textResId: Int? = null,
    isSelected: MutableState<Boolean> = mutableStateOf(true),
    onClick: () -> Unit = {}
) {
    val buttonText = if (textResId != null) stringResource(id = textResId) else text.orEmpty()

    Box(
        modifier = modifier
            .padding(4.dp)
            .clickable { onClick.invoke() }
            .requiredHeight(44.dp)
            .border(
                BorderStroke(if (isSelected.value) 0.dp else 1.dp, buttonColor),
                RoundedCornerShape(10.dp)
            )
            .background(
                if (isSelected.value) buttonColor else backgroundColor,
                RoundedCornerShape(10.dp)
            ),
        contentAlignment = Alignment.Center
    ) {
        Text(
            text = buttonText,
            fontSize = 14.sp,
            color = if (isSelected.value) Color.White else textColor
        )
    }
}

更新:替代方案

找到符合需求的实现方式,但未解决ModalBottomSheet组件本身的问题:

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun DialogLikeBottomSheet(
    showState: MutableState<Boolean>,
    shape: Shape = BottomSheetDefaults.ExpandedShape,
    dragHandle: (@Composable () -> Unit)? = null,
    content: @Composable () -> Unit,
) {
    if (showState.value) {
        val transitionState = remember {
            MutableTransitionState(false).apply { targetState = true }
        }

        Dialog(
            onDismissRequest = { showState.value = false },
            properties = DialogProperties(usePlatformDefaultWidth = false)
        ) {
            val dialogWindowProvider = LocalView.current.parent as DialogWindowProvider
            dialogWindowProvider.window.setGravity(Gravity.BOTTOM)

            AnimatedVisibility(
                visibleState = transitionState,
                enter = slideInVertically(initialOffsetY = { it }),
                exit = slideOutVertically(targetOffsetY = { it })
            ) {
                Surface(shape = shape) {
                    Column {
                        if (dragHandle != null) dragHandle()
                        content()
                    }
                }

            }
        }
    }
}

内容的提问来源于stack exchange,提问作者Игорь Иванов

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 22:47:33