Build apps faster with our new App builder! Check it out →

handwritingHandler

Android

Modifier in Compose Foundation

Configures an element to act as a stylus handwriting handler which can handle text input from a handwriting session which was triggered by stylus handwriting on a handwriting detector.

When this element gains focus, if there is an ongoing stylus handwriting delegation which was triggered by stylus handwriting on a handwriting detector, this element will receive text input from the handwriting session via its input connection.

A common use case is a component which looks like a text input field but does not actually support text input itself, and clicking on this fake text input field causes a real text input field to be shown. To support handwriting initiation in this case, a [handwritingDetector] modifier can be applied to the fake text input field to configure it as a detector, and this modifier can be applied to the real text input field. The callback implementation for the fake text field's [handwritingDetector] modifier is typically the same as the onClick implementation its [clickable] modifier, which shows and focuses the real text input field.

This function returns a no-op modifier on API levels below Android U (34) as stylus handwriting is not supported.

Last updated:

Installation

dependencies {
   implementation("androidx.compose.foundation:foundation:1.8.0-alpha04")
}

Overloads


fun Modifier.handwritingHandler(): Modifier

Code Example

HandwritingDetectorSample

@Composable
fun HandwritingDetectorSample() {
    var openDialog by remember { mutableStateOf(false) }
    val focusRequester = remember { FocusRequester() }

    Column(
        Modifier.imePadding().requiredWidth(300.dp).verticalScroll(rememberScrollState()),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text(
            "This is not an actual text field, but it is a handwriting detector so you can use " +
                "a stylus to write here."
        )
        Spacer(Modifier.size(16.dp))
        Text(
            "Fake text field",
            Modifier.fillMaxWidth()
                .handwritingDetector { openDialog = !openDialog }
                .padding(4.dp)
                .border(
                    1.dp,
                    MaterialTheme.colors.onSurface.copy(alpha = ContentAlpha.disabled),
                    RoundedCornerShape(4.dp)
                )
                .padding(16.dp),
            color = MaterialTheme.colors.onSurface.copy(alpha = ContentAlpha.medium)
        )
    }

    if (openDialog) {
        Dialog(onDismissRequest = { openDialog = false }) {
            Card(modifier = Modifier.width(300.dp), shape = RoundedCornerShape(16.dp)) {
                Column(
                    modifier = Modifier.padding(24.dp),
                    verticalArrangement = Arrangement.Center,
                    horizontalAlignment = Alignment.CenterHorizontally
                ) {
                    Text("This text field is a handwriting handler.")
                    Spacer(Modifier.size(16.dp))
                    val state = remember { TextFieldState() }
                    BasicTextField(
                        state = state,
                        modifier =
                            Modifier.fillMaxWidth()
                                .focusRequester(focusRequester)
                                .handwritingHandler(),
                        decorator = { innerTextField ->
                            Box(
                                Modifier.padding(4.dp)
                                    .border(
                                        1.dp,
                                        MaterialTheme.colors.onSurface,
                                        RoundedCornerShape(4.dp)
                                    )
                                    .padding(16.dp)
                            ) {
                                innerTextField()
                            }
                        }
                    )
                }
            }

            val windowInfo = LocalWindowInfo.current
            LaunchedEffect(windowInfo) {
                snapshotFlow { windowInfo.isWindowFocused }
                    .collect { isWindowFocused ->
                        if (isWindowFocused) {
                            focusRequester.requestFocus()
                        }
                    }
            }
        }
    }
}
by @alexstyl