Field Note · android / mobile-architecture

IME Options on Android

A practical Android and Jetpack Compose guide to keyboard actions, focus movement, and form completion.

Android keyboard showing an IME action button

Keyboard actions are part of a form’s interaction contract, not a cosmetic detail. On a phone, on Android TV, or with a physical keyboard, a useful IME action can remove an unnecessary focus change and make repeated data entry much less tiring.

Choose the action from the user’s next step

Action Use it when
None The keyboard should expose no special action.
Default The input method should choose the default behavior.
Done The user has completed the form or field group.
Go The field starts navigation to a destination.
Next / Previous Focus should move through a known field sequence.
Search The value submits a search.
Send The value sends a message or payload.

The important question is: what is the next meaningful state after this value is entered? Next should move to the next field; it should not silently submit a partially complete form.

Jetpack Compose

Set the visual keyboard contract with KeyboardOptions, then implement the transition with KeyboardActions:

val focusManager = LocalFocusManager.current

TextField(
    value = email,
    onValueChange = onEmailChanged,
    keyboardOptions = KeyboardOptions(
        keyboardType = KeyboardType.Email,
        imeAction = ImeAction.Next,
    ),
    keyboardActions = KeyboardActions(
        onNext = { focusManager.moveFocus(FocusDirection.Down) },
    ),
)

For the last field, prefer Done or the domain action that is actually available. Keep the submit action available as a visible button too: hardware keyboards, accessibility services, and test environments do not all expose the same IME UI.

Views and XML

The equivalent XML contract is android:imeOptions, with an editor listener for the action:

<com.google.android.material.textfield.TextInputEditText
    android:id="@+id/et_email"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:imeOptions="actionSearch"
    android:inputType="textEmailAddress"
    android:maxLines="1" />
binding.etEmail.setOnEditorActionListener { _, actionId, _ ->
    if (actionId == EditorInfo.IME_ACTION_SEARCH) {
        viewModel.search(binding.etEmail.text.toString())
        true
    } else {
        false
    }
}

Returning true only when the action was handled avoids swallowing an event that another part of the form still needs.

A small test matrix

Before calling the form done, test the action on the emulator and with an external keyboard. Check that Next moves focus, Done dismisses or completes the intended step, Search performs the same operation as the visible search button, and validation errors leave focus and the error message understandable. This is especially valuable in field workflows where the same form is filled dozens of times a day.

The principle scales beyond a single EditText: a good mobile flow makes the next state obvious, available, and testable.

DOCUframe MobileAccess