Also, my current tooltip implementation using `mou...
# compose-web
s
Also, my current tooltip implementation using
mouseEnter
and
mouseExit
has some issues, as I couldn't find built-in support. It has its flaws. What’s the best way to implement proper tooltips?
d
I use
Modifier.hoverable(interactionSource, true)
for checking hovers. You could try it for your tooltip implementation.
Copy code
val interactionSource = remember { MutableInteractionSource() }

LaunchedEffect(Unit) {
    interactionSource.interactions.collect { interaction ->
        when (interaction) {
            is HoverInteraction.Enter -> {}
            is HoverInteraction.Exit -> {}
        }
    }
}
Its probably possible to put a delay there as well, to show tooltips delayed.
k
It might be overkill but this is the composable I made for it that works well for me
Copy code
import androidx.compose.foundation.background
import androidx.compose.foundation.hoverable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsHoveredAsState
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.BasicText
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Popup
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch

@Composable
fun Tooltip(
    tooltipText: String?,
    modifier: Modifier = Modifier,
    backgroundColor: Color = Color.Black,
    textColor: Color = Color.White,
    textStyle: TextStyle = MaterialTheme.typography.bodyMedium,
    padding: Int = 8,
    delayMillis: Long = 500L,
    content: @Composable () -> Unit
) {
    var size by remember { mutableStateOf(IntSize.Zero) }
    var isTooltipVisible by remember { mutableStateOf(false) }
    val interactionSource = remember { MutableInteractionSource() }
    val isHovered by interactionSource.collectIsHoveredAsState()
    val coroutineScope = rememberCoroutineScope()
    var currentJob by remember { mutableStateOf<Job?>(null) }

    LaunchedEffect(isHovered) {
        if (isHovered) {
            currentJob = coroutineScope.launch {
                delay(delayMillis)
                isTooltipVisible = true
            }
        } else {
            currentJob?.cancel()
            currentJob = null
            isTooltipVisible = false
        }
    }

    Box(
        modifier = modifier
            .hoverable(interactionSource)
            .onGloballyPositioned { coordinates ->
                size = coordinates.size
            }
    ) {
        content()

        if (isTooltipVisible && tooltipText != null) {
            Popup(
                alignment = Alignment.Center,
                offset = IntOffset(0, -size.height.plus(10)),
            ) {
                Box(
                    modifier = Modifier
                        .background(
                            color = backgroundColor,
                            shape = MaterialTheme.shapes.small
                        )
                        .padding(padding.dp)
                ) {
                    BasicText(
                        text = tooltipText,
                        style = textStyle.copy(color = textColor)
                    )
                }
            }
        }
    }
}
And then how I use it:
Copy code
Tooltip(tooltipText = "Copy Response to Clipboard") {
    Icon(
        painter = painterResource(Res.drawable.copy_fill),
        contentDescription = "Copy to Clipboard",
        tint = Color.Black
    )
}
And then it looks like:
s
Thank you 🙏🏻