We just launched Compose Examples featuring over 150+ components! Check it out →

handwritingDetector

Android

Modifier in Compose Foundation

Configures an element to act as a handwriting detector which detects stylus handwriting and delegates handling of the recognised text to another element.

Stylus movement on the element will start a handwriting session, and trigger the [callback]. The [callback] implementation is expected to show and focus a text input field with a [handwritingHandler] modifier which can handle the recognized text from the handwriting session.

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, this modifier can be applied to the fake text input field to configure it as a detector, and a [handwritingHandler] modifier can be applied to the real text input field. The [callback] implementation is typically the same as the onClick implementation for the fake text field's [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.handwritingDetector(callback: () -> Unit)

Parameters

namedescription
callbacka callback which will be triggered when stylus handwriting is detected

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