Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 483
Vadym Yaroshchuk Avatar asked Mar 31 '26 23:03

Vadym Yaroshchuk


1 Answers

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:

override fun showMenu(
    rect: Rect,

    onCopyRequested: (() -> Unit)?,
    onPasteRequested: (() -> Unit)?,
    onCutRequested: (() -> Unit)?,
    onSelectAllRequested: (() -> Unit)?

// Before 1.2.0 ActionCallback has to be defined like this:
// typealias ActionCallback = () -> Unit
//
//  onCopyRequested: ActionCallback?,
//  onPasteRequested: ActionCallback?,
//  onCutRequested: ActionCallback?,
//  onSelectAllRequested: ActionCallback?
) {
    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.

like image 95
Philip Dukhov Avatar answered Apr 02 '26 12:04

Philip Dukhov



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!