Search code examples
android-jetpack-composeclipboard

Recompose when Clipboard contents change


Jetpack Compose has a ClipboardManager, allowing us to get or set the value on the clipboard. I notice however that getText() does not return a State, or any observable value.

@Composable fun ClipboardContents() {
  val cb = LocalClipboardManager.current
  Text(cb.getText())
}

How can I make sure that my Composable function responds to changes in the Clipboard?


Solution

  • You need to create State<AnnotatedString> that will be updated each time ClipboardManager clip has changed. This doesn't come out of the box you have to implement a functions that will observe for primary clip changes, then utilize that to create a state object that will trigger the recomposition.

    @Composable
    fun rememberClipboardText(): State<AnnotatedString?> {
        val clipboardManager = LocalClipboardManager.current
        val text = remember { mutableStateOf(clipboardManager.getText()) }
        onClipDataChanged {
            text.value = clipboardManager.getText()
        }
        return text
    }
    
    @SuppressLint("ComposableNaming")
    @Composable
    fun onClipDataChanged(onPrimaryClipChanged: ClipData?.() -> Unit) {
        val clipboardManager =
            LocalContext.current.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
        val callback = remember {
            ClipboardManager.OnPrimaryClipChangedListener {
                onPrimaryClipChanged(clipboardManager.primaryClip)
            }
        }
        DisposableEffect(clipboardManager) {
            clipboardManager.addPrimaryClipChangedListener(callback)
            onDispose {
                clipboardManager.removePrimaryClipChangedListener(callback)
            }
        }
    }
    
    

    then the usage will be something like this:

    @Composable
    fun ClipboardContents() {
        val clipBoardText by rememberClipboardText()
        clipBoardText?.let { Text(it) }
    }