TimeText

Functions summary

Unit
@Composable
TimeText(
    modifier: Modifier,
    timeSource: TimeSource,
    timeTextStyle: TextStyle,
    contentPadding: PaddingValues,
    startLinearContent: (@Composable () -> Unit)?,
    startCurvedContent: (CurvedScope.() -> Unit)?,
    endLinearContent: (@Composable () -> Unit)?,
    endCurvedContent: (CurvedScope.() -> Unit)?,
    textLinearSeparator: @Composable () -> Unit,
    textCurvedSeparator: CurvedScope.() -> Unit
)

Layout to show the current time and a label at the top of the screen.

Functions

@Composable
fun TimeText(
    modifier: Modifier = Modifier,
    timeSource: TimeSource = TimeTextDefaults.timeSource(timeFormat()),
    timeTextStyle: TextStyle = TimeTextDefaults.timeTextStyle(),
    contentPadding: PaddingValues = TimeTextDefaults.ContentPadding,
    startLinearContent: (@Composable () -> Unit)? = null,
    startCurvedContent: (CurvedScope.() -> Unit)? = null,
    endLinearContent: (@Composable () -> Unit)? = null,
    endCurvedContent: (CurvedScope.() -> Unit)? = null,
    textLinearSeparator: @Composable () -> Unit = { TextSeparator(textStyle = timeTextStyle) },
    textCurvedSeparator: CurvedScope.() -> Unit = { CurvedTextSeparator(curvedTextStyle = CurvedTextStyle(timeTextStyle)) }
): Unit

Layout to show the current time and a label at the top of the screen. If device has a round screen, then the time will be curved along the top edge of the screen, if rectangular - then the text and the time will be straight

This composable supports additional composable views to the left and to the right of the clock: Start Content and End Content. startCurvedContent, endCurvedContent and textCurvedSeparator are used for Round screens. startLinearContent, endLinearContent and textLinearSeparator are used for Square screens. For proper support of Square and Round screens both Linear and Curved methods should be implemented.

Note that Wear Material UX guidance recommends that time text should not be larger than 90 degrees of the screen edge on round devices and prefers short status messages be shown in start content only using the MaterialTheme.colors.primary color for the status message.

For more information, see the Curved Text guide.

A TimeText with a short app status message shown in the start content:

import androidx.wear.compose.foundation.CurvedTextStyle
import androidx.wear.compose.material.MaterialTheme
import androidx.wear.compose.material.Text
import androidx.wear.compose.material.TimeText
import androidx.wear.compose.material.TimeTextDefaults
import androidx.wear.compose.material.curvedText

val leadingTextStyle = TimeTextDefaults.timeTextStyle(color = MaterialTheme.colors.primary)

TimeText(
    startLinearContent = { Text(text = "ETA 12:48", style = leadingTextStyle) },
    startCurvedContent = {
        curvedText(text = "ETA 12:48", style = CurvedTextStyle(leadingTextStyle))
    },
)

An example of a TimeText with a different date and time format:

import androidx.wear.compose.material.Text
import androidx.wear.compose.material.TimeText
import androidx.wear.compose.material.TimeTextDefaults

TimeText(
    timeSource =
        TimeTextDefaults.timeSource(
            DateFormat.getBestDateTimePattern(Locale.getDefault(), "yyyy-MM-dd hh:mm")
        )
)

An example of a TimeText animating a message that is added or removed

import androidx.compose.animation.animateColor
import androidx.compose.animation.core.CubicBezierEasing
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.tween
import androidx.compose.animation.core.updateTransition
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clipToBounds
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.coerceAtMost
import androidx.compose.ui.unit.dp
import androidx.wear.compose.foundation.CurvedModifier
import androidx.wear.compose.foundation.CurvedTextStyle
import androidx.wear.compose.foundation.angularSize
import androidx.wear.compose.foundation.curvedColumn
import androidx.wear.compose.foundation.curvedRow
import androidx.wear.compose.foundation.sizeIn
import androidx.wear.compose.material.Button
import androidx.wear.compose.material.Text
import androidx.wear.compose.material.TimeText
import androidx.wear.compose.material.TimeTextDefaults
import androidx.wear.compose.material.curvedText

var showState by remember { mutableStateOf(false) }
val showTransition = updateTransition(showState)

val time = 350
val animatedColor by
    showTransition.animateColor(
        label = "animatedColor",
        transitionSpec = {
            tween(
                time,
                easing =
                    when {
                        false isTransitioningTo true ->
                            // Fade In
                            CubicBezierEasing(0.4f, 0.0f, 1.0f, 1.0f)
                        else ->
                            // Fade Out
                            CubicBezierEasing(0.0f, 0.0f, 0.2f, 1.0f)
                    },
            )
        },
    ) { state ->
        when (state) {
            true -> Color.White
            false -> Color.Transparent
        }
    }
val animateSize by
    showTransition.animateFloat(
        label = "animatedSize",
        transitionSpec = { tween(time, easing = CubicBezierEasing(0.4f, 0.0f, 0.2f, 1.0f)) },
    ) { state ->
        when (state) {
            true -> 1f
            false -> 0f
        }
    }

val text = "Long text to animate"

val curvedSeparatorSweep = 10f
val curvedTextSweep = 80f
val curvedAnimatedSweep = animateSize * (curvedSeparatorSweep + curvedTextSweep)
val curvedSeparatorGap = curvedAnimatedSweep.coerceAtMost(curvedSeparatorSweep) / 2f

val linearSeparatorSize = 10.dp
val linearTextSize = 70.dp
val linearAnimatedSize = animateSize * (linearSeparatorSize + linearTextSize)
val linearSeparatorGap = linearAnimatedSize.coerceAtMost(linearSeparatorSize) / 2f

val textStyle =
    TimeTextDefaults.timeTextStyle().copy(fontWeight = FontWeight.Normal, color = animatedColor)
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
    TimeText(
        // Curved
        textCurvedSeparator = {
            curvedColumn(modifier = CurvedModifier.angularSize(curvedSeparatorGap)) {}
            curvedText("·", style = CurvedTextStyle(textStyle))
            curvedColumn(modifier = CurvedModifier.angularSize(curvedSeparatorGap)) {}
        },
        startCurvedContent = {
            curvedRow(
                modifier =
                    CurvedModifier.sizeIn(
                        maxSweepDegrees =
                            (curvedAnimatedSweep - curvedSeparatorSweep).coerceAtLeast(0f)
                    )
            ) {
                curvedText(
                    text,
                    CurvedModifier.sizeIn(maxSweepDegrees = curvedTextSweep),
                    style = CurvedTextStyle(textStyle),
                    overflow = TextOverflow.Ellipsis,
                )
            }
        },
        // Linear
        textLinearSeparator = {
            Spacer(modifier = Modifier.width(linearSeparatorGap))
            Text("·", style = textStyle)
            Spacer(modifier = Modifier.width(linearSeparatorGap))
        },
        startLinearContent = {
            Box(
                modifier =
                    Modifier.clipToBounds()
                        .widthIn(
                            max = (linearAnimatedSize - linearSeparatorSize).coerceAtLeast(0.dp)
                        )
            ) {
                Text(
                    text,
                    maxLines = 1,
                    style = textStyle,
                    modifier =
                        Modifier.wrapContentWidth(align = Alignment.Start, unbounded = true)
                            .widthIn(max = linearTextSize),
                    overflow = TextOverflow.Ellipsis,
                )
            }
        },
    )
    Button(onClick = { showState = !showState }) { Text("Go!") }
}
Parameters
modifier: Modifier = Modifier

Current modifier.

timeSource: TimeSource = TimeTextDefaults.timeSource(timeFormat())

TimeSource which retrieves the current time and formats it.

timeTextStyle: TextStyle = TimeTextDefaults.timeTextStyle()

Optional textStyle for the time text itself

contentPadding: PaddingValues = TimeTextDefaults.ContentPadding

The spacing values between the container and the content

startLinearContent: (@Composable () -> Unit)? = null

a slot before the time which is used only on Square screens

startCurvedContent: (CurvedScope.() -> Unit)? = null

a slot before the time which is used only on Round screens

endLinearContent: (@Composable () -> Unit)? = null

a slot after the time which is used only on Square screens

endCurvedContent: (CurvedScope.() -> Unit)? = null

a slot after the time which is used only on Round screens

textLinearSeparator: @Composable () -> Unit = { TextSeparator(textStyle = timeTextStyle) }

a separator slot which is used only on Square screens

textCurvedSeparator: CurvedScope.() -> Unit = { CurvedTextSeparator(curvedTextStyle = CurvedTextStyle(timeTextStyle)) }

a separator slot which is used only on Round screens