Use Tooltips in Jetpack Compose

After I wrote my last article about Jetpack Compose, I said there that Jetpack Compose is lacking some (for my part) fundamental elements, and one among them is the tooltip.

On the time, there was no built-in composable to show tooltips and there have been a number of different options circling on-line. The issue with these options was that after Jetpack Compose launched newer variations, these options may break. So it wasn’t excellent and the neighborhood was left hoping that someday sooner or later, help can be added for tooltips.

I’m glad to say that since version 1.1.0 of Compose Material 3, we now have in-built tooltip help. 👏

Whereas this in itself is nice, greater than a 12 months has handed since that model was launched. And with subsequent variations, the API associated to tooltips modified drastically as effectively.

For those who go over the changelog, you will notice how the general public and inside APIs have modified. So keep in mind, that while you learn this text, issues might have continued to alter as the whole lot associated to Tooltips remains to be marked by the annotation ExperimentalMaterial3Api::class.

❗️ The model of fabric 3 used for this text is 1.2.1, which was launched on March sixth, 2024

We now have help for 2 several types of tooltips:

  1. Plain tooltip

  2. Wealthy media tooltip

Plain Tooltip

You should use the primary variety to supply details about an icon button that wouldn’t be clear in any other case. For instance, you need to use a plain tooltip to point to a consumer what the icon button represents.

Basic tooltip example

So as to add a tooltip to your software, you employ the TooltipBox composable. This composable takes a number of arguments:

enjoyable TooltipBox(
    positionProvider: PopupPositionProvider,
    tooltip: @Composable TooltipScope.() -> Unit,
    state: TooltipState,
    modifier: Modifier = Modifier,
    focusable: Boolean = true,
    enableUserInput: Boolean = true,
    content material: @Composable () -> Unit,
)

A few of these must be acquainted to you when you’ve got used Composables earlier than. I’ll spotlight those which have a selected use case right here:

  • positionProvider – Of PopupPositionProvider kind, and is used to calculate the place of the tooltip.

  • tooltip – That is the place you’ll be able to design the UI of how the tooltip will appear to be.

  • state – This holds the state that’s related to a selected Tooltip occasion. It exposes strategies like displaying/dismissing the tooltip and when instantiating an occasion of 1, you’ll be able to declare if the tooltip must be persistent or not (that means if it ought to maintain displaying on the display screen till a consumer performs a click on motion exterior the tooltip).

  • content material – That is the UI that the tooltip will show above/under.

Right here is an instance of instantiating a BasicTooltipBox with all of the related arguments crammed in:

@OptIn(ExperimentalFoundationApi::class, ExperimentalMaterial3Api::class)
@Composable
enjoyable BasicTooltip() {
    val tooltipPosition = TooltipDefaults.rememberPlainTooltipPositionProvider()
    val tooltipState = rememberBasicTooltipState(isPersistent = false)

    BasicTooltipBox(positionProvider = tooltipPosition,
        tooltip =  { Textual content("Hi there World") } ,
        state = tooltipState) {
        IconButton(onClick = { }) {
            Icon(imageVector = Icons.Stuffed.Favourite, 
                 contentDescription = "Your icon's description")
        }
    }
}

A basic tooltip

Jetpack Compose has a constructed at school known as TooltipDefaults. You should use this class that can assist you instantiate arguments that make up a TooltipBox. As an illustration, you possibly can use TooltipDefaults.rememberPlainTooltipPositionProvider to appropriately place the tooltip in relation to the anchor aspect.

Wealthy Tooltip

A wealthy media tooltip takes more room than a plain tooltip and can be utilized to supply extra context concerning the performance of an icon button. When the tooltip is proven, you’ll be able to add buttons and hyperlinks to it to supply additional clarification or definitions.

It’s instantiated in an identical manner as a plain tooltip, inside a TooltipBox, however you employ the RichTooltip composable.

TooltipBox(positionProvider = tooltipPosition,
        tooltip = {
                  RichTooltip(
                      title = { Textual content("RichTooltip") },
                      caretSize = caretSize,
                      motion = {
                          TextButton(onClick = {
                              scope.launch {
                                  tooltipState.dismiss()
                                  tooltipState.onDispose()
                              }
                          }) {
                              Textual content("Dismiss")
                          }
                      }
                  ) {
                        Textual content("That is the place an outline would go.")
                  }
        },
        state = tooltipState) {
        IconButton(onClick = {
            
        }) {
            Icon(imageVector = tooltipIcon,
                contentDescription = "Your icon's description",
                tint = iconColor)
        }
    }

A couple of issues to note a few Wealthy tooltip:

  1. A Wealthy tooltip has help for a caret.

  2. You possibly can add an motion (that’s, a button) to the tooltip to provide customers an possibility to search out out extra info.

  3. You possibly can add logic to dismiss the tooltip.

Edge Instances

Whenever you select to mark your tooltip state as persistent, it implies that as soon as the consumer interacts with the UI that reveals your tooltip, it can keep seen till the consumer presses anyplace else on the display screen.

For those who appeared on the instance of a Wealthy tooltip from above, you may need observed that now we have added a button to dismiss the tooltip as soon as it’s clicked.

There’s a downside that occurs as soon as a consumer presses that button. Because the dismiss motion is carried out on the tooltip, if a consumer needs to carry out one other lengthy press on the UI merchandise that invokes this tooltip, the tooltip gained’t be proven once more. Because of this the state of the tooltip is persistent on it being dismissed. So, how can we go about and resolve this?

Second long press does not trigger the tooltip

With a view to “reset” the state of the tooltip, now we have to name the onDispose technique that’s uncovered by way of the tooltip state. As soon as we do this, the tooltip state is reset and the tooltip might be proven once more when the consumer performs an extended press on the UI merchandise.

@OptIn(ExperimentalMaterial3Api::class)
@Composable
enjoyable RichTooltip() {
    val tooltipPosition = TooltipDefaults.rememberRichTooltipPositionProvider()
    val tooltipState = rememberTooltipState(isPersistent = true)
    val scope = rememberCoroutineScope()

    TooltipBox(positionProvider = tooltipPosition,
        tooltip = {
                  RichTooltip(
                      title = { Textual content("RichTooltip") },
                      caretSize = TooltipDefaults.caretSize,
                      motion = {
                          TextButton(onClick = {
                              scope.launch {
                                  tooltipState.dismiss()
                                  tooltipState.onDispose()  
                              }
                          }) {
                              Textual content("Dismiss")
                          }
                      }
                  ) {

                  }
        },
        state = tooltipState) {
        IconButton(onClick = {  }) {
            Icon(imageVector = Icons.Stuffed.Name, contentDescription = "Your icon's description")
        }
    }
}

onDispose solves the issue

One other state of affairs the place the tooltip state doesn’t reset is that if as a substitute of calling ourselves for the dismiss technique per a consumer’s motion, the consumer clicks exterior of the tooltip, inflicting it to be dismissed. This calls the dismiss technique behind the scenes and the tooltip state is ready to dismissed. Lengthy urgent on the UI aspect to see our tooltip once more will end in nothing.

The tooltip does not show again

Our logic that calls the tooltip’s onDispose technique doesn’t get triggered, so how can we reset the tooltip’s state?

At present, I haven’t been capable of determine this out. It may be associated to the tooltip’s MutatorMutex. Perhaps with upcoming releases, there might be an API for this. I did discover that if different tooltips are current on the display screen and they’re pressed, this resets the beforehand clicked upon tooltip.

25a81994-a508-4c71-8424-c45370a7999d

If you need to see the code featured right here, you’ll be able to go to this GitHub repository

If you need to see tooltips in an software, you’ll be able to test it out here.

References

Source link

Exit mobile version