'Floating toolbar for text selection Jetpack Compose

How can I customize a menu for selected text to a TextField in Jetpack Compose? I mean something like this:

Example

Didn't find anything in the official documentation or on the Internet about how to do this using Jetpack Compose.



Solution 1:[1]

You can provide a custom TextToolbar in the LocalTextToolbar. There, in the showMenu method, you must startActionMode as you would have done in the old Android. CustomTextToolbar:

typealias ActionCallback = () -> Unit

override fun showMenu(
    rect: Rect,

    onCopyRequested: ActionCallback?,
    onPasteRequested: ActionCallback?,
    onCutRequested: ActionCallback?,
    onSelectAllRequested: ActionCallback?

//        Since 1.2.0-alpha05 ActionCallback was removed
//        onCopyRequested: (() -> Unit)?,
//        onPasteRequested: (() -> Unit)?,
//        onCutRequested: (() -> Unit)?,
//        onSelectAllRequested: (() -> Unit)?
) {
    println("showMenu")
    view.startActionMode(TextActionModeCallback())
}

class TextActionModeCallback(
) : ActionMode.Callback {
    override fun onActionItemClicked(mode: ActionMode?, item: MenuItem?): Boolean {
        println("onActionItemClicked $mode $item")
        return true
    }

    override fun onCreateActionMode(mode: ActionMode?, menu: Menu?): Boolean {
        println("onActionItemClicked $mode $menu")
        return false
    }

    override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?): Boolean {
        println("onActionItemClicked $mode $menu")
        return true
    }

    override fun onDestroyActionMode(mode: ActionMode?) {
        println("onActionItemClicked $mode")
    }
}

As a ref how to implement it, check out compose source code: AndroidTextToolbar and TextActionModeCallback

Then you can use it like this:

CompositionLocalProvider(
    LocalTextToolbar provides CustomTextToolbar(LocalView.current)
) {
    var text by remember { mutableStateOf("") }
    TextField(value = text, onValueChange = { text = it })
}

To make this work for the entire application, you need to do it at the top of the composables tree, for example in setContent.

p.s. I had to define ActionCallback, because it's internal in compose. I believe it's a bug, so I've created this issue. It was fixed in Compose 1.2.0-alpha05.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1