官方 Focus 文档偏 API;本文写 键盘链路 与 edge-to-edge 下的Insets 组合战。
配套示例


FocusInsetsLabScreen.kt:ImeAction.Next 切焦点、Modifier.imePadding()。
1. FocusRequester:谁 request、何时合法?
- remember { FocusRequester() } 与字段 一一对应;不要在循环里 remember 多个 requester 却共享逻辑,读屏会乱。
- requestFocus() 在 尚未 attached 时抛异常:常见写法是 LaunchedEffect(Unit) { focusRequester.requestFocus() } 放在 已挂载 之后;或用户点击后再请求。
- 大表单:少量固定输入框可用多个 FocusRequester;字段很多时优先用 LocalFocusManager.current.moveFocus(FocusDirection.Next),减少 requester 数量和错绑风险。
本示例:用键盘 Next 从第一框跳到第二框;避免在 onCreate 粗暴抢焦点打断用户。
2. KeyboardOptions / KeyboardActions
- ImeAction.Next / Done 要与 实际可聚焦项 对齐;onNext 里 second.requestFocus() 是最小演示。
- 多行输入:ImeAction 与 singleLine 冲突时,键盘右下角行为会怪异,先统一产品语义。
- Done 行为:ImeAction.Done 通常要配合提交、清焦点或隐藏键盘;只改键盘图标而不处理动作,会让用户觉得按钮失效。
避坑
- 只写 KeyboardActions 却 没对应 focusRequester:键盘显示「下一步」但无响应。
- BasicTextField 自己拼装饰:要重复实现 cursor handle / selection / IME 成本,优先 TextField/OutlinedTextField 除非你真的需要极致定制。
3. WindowInsets 与 imePadding()
- Modifier.imePadding():把 键盘高度 转成底部 padding,减轻遮挡;与 enableEdgeToEdge() 同屏时,常与 statusBarsPadding / navigationBarsPadding 组合。
- 全屏沉浸 + 键盘:不要期待「一个 padding 解决所有机型」;要准备 Scaffold 内 innerPadding + 业务区滚动 的组合方案。
- Insets 消费:imePadding() 等 insets padding modifier 会把对应 insets 作为 padding 应用到当前节点,并影响子树继续看到的剩余 insets。多层叠加时要明确谁消费系统栏、谁处理 IME。
- 可滚动表单:键盘只是把底部空间让出来,不保证当前焦点字段自动滚到可见区域。长表单可结合 BringIntoViewRequester 或滚动容器,在获得焦点时把字段带入视口。
避坑
- 多层 imePadding() 嵌套:底部空白叠罗汉。
- WebView / SurfaceView 与 Compose 键盘:焦点竞争 要单独开 issue 级方案,不在此篇假装一次搞定。
4. BringIntoViewRequester:什么时候需要?
imePadding() 解决的是「键盘出现后底部留空间」,BringIntoViewRequester 解决的是「某个输入框被遮住时,把它滚到可见范围」。两者常一起出现,但职责不同。
典型思路:
val bringIntoViewRequester = remember { BringIntoViewRequester() }
val scope = rememberCoroutineScope()
OutlinedTextField(
value = value,
onValueChange = onValueChange,
modifier = Modifier
.bringIntoViewRequester(bringIntoViewRequester)
.onFocusEvent { state ->
if (state.isFocused) {
scope.launch { bringIntoViewRequester.bringIntoView() }
}
},
)
生产里要结合表单滚动容器、焦点切换节奏和 IME 动画验证;不要只靠一层 imePadding() 期望所有机型都自然滚对。
5. 自检清单
参考答案(复习用)
源码仓库:ComposeDemo(分支 main)
相关推荐
《Measure、Place 与 Constraints 约束传递》
《Material3 组件选择、状态管理与避坑指南》




