Keyboard & IME in Compose: Soft Keyboard Control & Focus Management
Managing the soft keyboard and input focus in Jetpack Compose requires understanding imePadding, SoftwareKeyboardController, and FocusManager. This guide covers practical patterns for professional keyboard handling.
Handling IME Padding
The soft keyboard pushes content up automatically, but you can customize this behavior:
Column(
modifier = Modifier
.fillMaxSize()
.imePadding() // Adds padding when keyboard is visible
) {
TextField(
value = text,
onValueChange = { text = it },
modifier = Modifier.fillMaxWidth()
)
}
Use imePadding() alongside navigationBarsPadding() and statusBarsPadding() for proper inset handling:
Column(
modifier = Modifier
.fillMaxSize()
.statusBarsPadding()
.navigationBarsPadding()
.imePadding()
) {
// Your content
}
Controlling Keyboard Visibility
val keyboardController = LocalSoftwareKeyboardController.current
Button(
onClick = {
keyboardController?.hide()
}
) {
Text("Hide Keyboard")
}
Or show the keyboard on demand:
val focusRequester = remember { FocusRequester() }
LaunchedEffect(Unit) {
focusRequester.requestFocus()
keyboardController?.show()
}
TextField(
value = text,
onValueChange = { text = it },
modifier = Modifier.focusRequester(focusRequester)
)
KeyboardOptions & KeyboardType
TextField(
value = email,
onValueChange = { email = it },
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Email,
imeAction = ImeAction.Done
),
modifier = Modifier.fillMaxWidth()
)
Common keyboard types:
-
KeyboardType.Email- Shows @ symbol -
KeyboardType.Number- Numeric keypad -
KeyboardType.Phone- Phone number layout -
KeyboardType.Password- Hides input -
KeyboardType.Decimal- Number with decimal point
Focus Management with KeyboardActions
val focusManager = LocalFocusManager.current
val focusRequester = remember { FocusRequester() }
Column {
TextField(
value = name,
onValueChange = { name = it },
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next),
keyboardActions = KeyboardActions(
onNext = { focusRequester.requestFocus() }
),
modifier = Modifier.fillMaxWidth()
)
TextField(
value = email,
onValueChange = { email = it },
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
keyboardActions = KeyboardActions(
onDone = { focusManager.clearFocus() }
),
modifier = Modifier
.fillMaxWidth()
.focusRequester(focusRequester)
)
}
Programmatic Focus with FocusRequester
@Composable
fun LoginForm() {
var email by remember { mutableStateOf("") }
var password by remember { mutableStateOf("") }
val focusRequester = remember { FocusRequester() }
val focusManager = LocalFocusManager.current
Column(modifier = Modifier.padding(16.dp)) {
TextField(
value = email,
onValueChange = { email = it },
label = { Text("Email") },
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Email,
imeAction = ImeAction.Next
),
keyboardActions = KeyboardActions(
onNext = { focusRequester.requestFocus() }
),
modifier = Modifier.fillMaxWidth()
)
TextField(
value = password,
onValueChange = { password = it },
label = { Text("Password") },
visualTransformation = PasswordVisualTransformation(),
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Password,
imeAction = ImeAction.Done
),
keyboardActions = KeyboardActions(
onDone = { focusManager.clearFocus() }
),
modifier = Modifier
.fillMaxWidth()
.focusRequester(focusRequester)
)
Button(onClick = { /* Login */ }, modifier = Modifier.align(Alignment.End)) {
Text("Sign In")
}
}
}
Best Practices
-
Always use
imePadding()to avoid content being hidden by the keyboard -
Chain focus with
ImeAction.Nextto create smooth multi-field forms -
Use
ImeAction.Doneon the last field to dismiss the keyboard - Test on real devices - emulator keyboard behavior differs
-
Combine
KeyboardActionswithFocusRequesterfor precise focus control
8 Android App Templates → https://myougatheax.gumroad.com
Top comments (0)