Jetpack Compose for Wear OS
Learn about Jetpack Compose for Wear OS by building a dedicated app to manage breath-holding times, including a stopwatch to track new records and save them in the collection. In this tutorial, you’ll get to know all the essential components, such as Inputs, Dialogs, Progress Indicators and Page Indicators. You’ll also learn when to use a Vignette and a TimeText. By Lena Stepanova.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Jetpack Compose for Wear OS
20 mins
Adding a Vignette
Open OneBreathApp.kt and look at the parameters in Scaffold()
again.
Set vignette
parameter to:
vignette = {
if (currentBackStackEntry?.destination?.route == Destination.Records.route) {
Vignette(vignettePosition = VignettePosition.TopAndBottom)
}
}
This condition means the vignette will be there only for the RecordsListScreen()
. A vignette is a UI feature that dims an edge part of the screen. In your case, it’s TopAndBottom
, as specified in vignettePosition
.
Compare the record list screen with and without the vignette:
See the difference? In the right-hand version, the edges are slightly darker.
TimeText
Another essential Wear OS UI component is TimeText()
. Still in OneBreathApp.kt, replace the empty timeText
parameter in Scaffold()
with:
timeText = {
if (currentBackStackEntry?.destination?.route == Destination.TrainingDayDetails.route) { // 1
TimeText(
startLinearContent = { // 2
Text(
text = selectedDay?.date.toFormattedString(),
color = colorPrimary,
style = textStyle
)
},
startCurvedContent = { // 3
curvedText(
text = selectedDay?.date.toFormattedString(),
color = colorPrimary,
style = CurvedTextStyle(textStyle)
)
}
)
} else TimeText() // 4
}
Here’s a breakdown of this code:
- You only want to show an additional text before the time in the training day details screen. This additional text will hold the date of the record.
-
TimeText()
adapts to round and square watches. For square watches, it usesTimeTextDefaults.timeTextStyle()
. - For round watches, use
CurvedTextStyle()
. - All the screens except the training day details screen will still show the current time on top.
Build and run. You’ll see the current time on top now. Tap on one of the green chips. In the training day details screen, you’ll also see the date:
Progress Indicator
Wouldn’t it be nice to have something like a clock hand for the stopwatch? You can do that with a Wear OS CircularProgressIndicator.
Go to StopWatchScreen.kt and add the following to the top of the Box()
, right above Column()
:
CircularProgressIndicator(
progress = duration.toProgress(),
modifier = Modifier
.fillMaxSize()
.padding(all = 1.dp)
)
This indicator will recompose every second and show the current duration of your breath hold. It’s usually recommended to leave a gap for the TimeText()
by adding startAngle
and endAngle
parameters, but in OneBreath you’ll sacrifice those to make the indicator resemble a clock hand.
Build and run the app and start the stopwatch. You’ll see the clock ticking:
This CircularProgressIndicator()
is determinate, but you can also use its indeterminate version to show a loading indicator – just leave out the progress parameter. It would look like this:
Page Indicator
While you’re still running the app, go to the record list screen and tap on one of the training day items. Here, in the details screen, you can page through all your breath holds on that day. Would be nice to know what page you’re on, right? A HorizontalPageIndicator will help you with that.
Go to TrainingDayDetailsScreen.kt. In SwipeToDismissBox()
, add this below val pagerState = rememberPagerState()
:
val pageIndicatorState: PageIndicatorState = remember {
object : PageIndicatorState {
override val pageOffset: Float
get() = 0f
override val selectedPage: Int
get() = selectedPage
override val pageCount: Int
get() = maxPages
}
}
Here, you create a PageIndicatorState
that connects the HorizontalPager()
and HorizontalPageIndicator()
. The selectedPage
is set when you scroll through the pager. The pageCount
is the total number of attempts on the training day. The pageOffset
is 0f
in this case, but you can use it to animate the indicator.
To use it, add HorizontalPageIndicator()
right below HorizontalPager
:
HorizontalPageIndicator(
pageIndicatorState = pageIndicatorState,
selectedColor = colorAccent
)
Build and run. Pick a training day from the list. You’ll see a paging indicator at the bottom:
HorizontalPageIndicator()
is an example of a horizontal paging indicator. If you need a vertical indicator, you can use a PositionIndicator()
. Check out the official materials for more components.
Where to Go From Here?
Download the completed project files by clicking the Download Materials button at the top or bottom of the tutorial.
Congratulations! You can now track your breath-holding records with the app you’ve just built. Now, take a deep breath and set a new personal record! :]
If you’re interested in learning more about various Wear OS Composables, check out the official documentation documentation, as well as the Horologist library for advanced date and time pickers. And if you enjoy Wear OS development, don’t miss out on the Creating Tiles for Wear OS video course.
We hope you enjoyed this tutorial. If you have any questions or comments, please join the forum discussion below!