Pantrix UI

Badged Box

Puts badge on content, with its middle on a corner of it, and takes no room for it.

import com.pantrixui.components.elements.badge.PantrixBadgedBox

Examples

On the corner of an icon

PantrixBadgedBox sets the badge on a corner of its content: by default the top corner on the side where lines end.

The badge is hidden from screen readers; the icon's own description says the number ("Inbox, 5 unread"). When the number gets down to zero no badge is put there at all: it is the caller that does this, not the badge. Raise the number with the buttons: past 99 it reads "99+", and the pill grows away from the icon.

var unread by rememberSaveable { mutableIntStateOf(START_UNREAD) }
Row(
    horizontalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.lg),
    verticalAlignment = Alignment.CenterVertically
) {
    PantrixBadgedBox(badge = { if (unread > 0) PantrixBadge(count = unread, tagKey = "unread") }) {
        PantrixIcon(
            imageVector = PantrixIcons.Inbox,
            contentDescription = if (unread > 0) "Inbox, $unread unread" else "Inbox",
            tagKey = "inbox"
        )
    }
    PantrixButton(
        text = "+1",
        onClick = { unread += 1 },
        variant = ButtonVariant.Outline,
        size = ButtonSize.Small,
        tagKey = "one_more"
    )
    PantrixButton(
        text = "+50",
        onClick = { unread += MANY },
        variant = ButtonVariant.Outline,
        size = ButtonSize.Small,
        tagKey = "many_more"
    )
    PantrixButton(
        text = "Mark as read",
        onClick = { unread = 0 },
        variant = ButtonVariant.Text,
        size = ButtonSize.Small,
        tagKey = "read"
    )
}

private const val START_UNREAD = 5

private const val MANY = 50

On avatars

On round content the badge stands on the circle, not on the corner of the box: overlap = BadgeOverlap.Circular. alignment says which corner; the status dot is at the bottom, on the side where lines end.

The status dots are made larger with BadgeStyle(dotSize = 12.dp): large enough to be made out on an avatar. The avatar's description says what the dot means ("İbrahim Öztürk, online"); the dot is hidden from screen readers.

val statusDot = BadgeStyle(dotSize = 12.dp)
Row(horizontalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.xl)) {
    PantrixBadgedBox(
        badge = { PantrixBadge(count = 2, tagKey = "messages") },
        overlap = BadgeOverlap.Circular
    ) {
        PantrixAvatar(contentDescription = "Ayşe Yılmaz, 2 new messages", initials = "AY", tagKey = "badged_ayse")
    }
    PantrixBadgedBox(
        badge = { PantrixBadge(tone = BadgeTone.Success, style = statusDot, tagKey = "online") },
        alignment = Alignment.BottomEnd,
        overlap = BadgeOverlap.Circular
    ) {
        PantrixAvatar(
            contentDescription = "İbrahim Öztürk, online",
            initials = "İÖ",
            tone = AvatarTone.Info,
            tagKey = "badged_ibrahim"
        )
    }
    PantrixBadgedBox(
        badge = { PantrixBadge(tone = BadgeTone.Neutral, style = statusDot, tagKey = "offline") },
        alignment = Alignment.BottomEnd,
        overlap = BadgeOverlap.Circular
    ) {
        PantrixAvatar(
            contentDescription = "ilker kaya, offline",
            initials = "ik",
            tone = AvatarTone.Success,
            tagKey = "badged_ilker"
        )
    }
    PantrixBadgedBox(
        badge = { PantrixBadge(count = MANY * 3, tagKey = "team") },
        overlap = BadgeOverlap.Circular
    ) {
        PantrixAvatar(
            contentDescription = "Team, more than 99 new messages",
            icon = PantrixIcons.Person,
            tone = AvatarTone.Primary,
            tagKey = "badged_team"
        )
    }
}

private const val MANY = 50

Beside a text

A badge can stand on a text too. PantrixBadgedBox keeps the baseline of its content: with alignByBaseline the "new" label stands on the same line as the title beside it, and the dot on the label's corner.

Row(horizontalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.sm)) {
    PantrixText(
        text = "Campaigns",
        modifier = Modifier.alignByBaseline(),
        style = PantrixTheme.typography.callout
    )
    PantrixBadgedBox(
        badge = { PantrixBadge(tone = BadgeTone.Primary, tagKey = "new") },
        modifier = Modifier.alignByBaseline()
    ) {
        PantrixText(text = "new", style = PantrixTheme.typography.footnote)
    }
}

API

badge

Puts badge on content, with its middle on a corner of it, and takes no room for it.

ParameterTypeDefaultWhat it is
badge@Composable () -> Unitwhat is put on the content: usually a PantrixBadge. Nothing, where there is nothing to show.
modifierModifierModifierthe box's modifier.
alignmentAlignmentAlignment.TopEndwhere on content the middle of the badge is.
overlapBadgeOverlapBadgeOverlap.Rectangularwhether that is on the box around content, or on the circle in it.
content@Composable () -> Unitwhat the badge belongs to.

Accessibility