Introduction
Getting started with WearOs in compose is going to be very easy and a nutshell. If you have already used Jetpack Compose to develop apps, you can start creating code for Wear OS with ease.
Compose knowledge can offer you an advantage in this situation, but you don't have to be an all-arounder to start.
Here, we'll start by comprehending the project's overall needs before learning about Wear OS's fundamental parts and features. With the help of this article, you can easily create a simple app for Wear OS.
Project Set Up
Create a new Project in the Android Studio by Selecting the Wear OS in the sidebar and then choosing the Empty Wear App like the below image.
Explore the Project
Let's see how the project is organized by android studio regarding Wear OS.
main > theme/
This folder contains the Color, Type, and Theme files used by Compose for the theme.
res > strings/
This folder contains two strings.xml files, one for round and the second for square devices. This string will compile according to the device specification.
build.gradle
It contains a basic app configuration. It includes the dependencies necessary to create a Composable Wear OS App.
main > AndroidManifest.xml
It includes the elements necessary to create a Wear OS application. This is the same as a non-Compose app and similar to a mobile app, so we won't review this.
main > MainActivity.kt
It contains code for creating an app with Compose. It also contains the top-level composables (like the Scaffold) for our app.
Dependencies
dependencies {
implementation 'androidx.core:core-ktx:1.9.0'
implementation 'com.google.android.gms:play-services-wearable:18.0.0'
implementation platform('androidx.compose:compose-bom:2023.03.00')
//compose for wear os
implementation 'androidx.wear.compose:compose-material:1.0.0'
implementation 'androidx.wear.compose:compose-foundation:1.0.0'
//General compose
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.1'
implementation 'androidx.activity:activity-compose:1.5.1'
implementation 'androidx.compose.ui:ui'
implementation 'androidx.compose.ui:ui-tooling-preview'
// Testing
androidTestImplementation platform('androidx.compose:compose-bom:2023.03.00')
androidTestImplementation 'androidx.compose.ui:ui-test-junit4'
debugImplementation 'androidx.compose.ui:ui-tooling'
debugImplementation 'androidx.compose.ui:ui-test-manifest'
//added
implementation "androidx.compose.material:material-icons-extended:$compose_version"
implementation "androidx.wear.compose:compose-material:$wear_compose_version"
}
Starting with Entry Point
Open MainActivity in the project. This is a pretty simple class that extends ComponentActivity and uses setContent { WearApp() } to create the UI. This ought to look familiar to you based on what you've already learned about Compose. The UI is currently being put up.
Locate the WearApp() composable function by scrolling down. You should notice a lot of TODOs strewn throughout the code before we discuss the actual function. Each of these denotes a step in this article. For the time being, ignore them.
It should resemble the following.
WearOsExampleTheme {
val listState = rememberLazyListState()
val contentModifier = Modifier
.fillMaxWidth()
.padding(bottom = 8.dp)
val iconModifier = Modifier
.size(24.dp)
.wrapContentSize(align = Alignment.Center)
LazyColumn(
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(
top = 32.dp,
start = 8.dp,
end = 8.dp,
bottom = 32.dp
),
verticalArrangement = Arrangement.Center,
state = listState
) {
// TO-DO: PlayButton
item { ButtonExample(contentModifier, iconModifier) }
// TO-DO: Text
item { TextExample(contentModifier) }
// TO-DO: Card
item { CardExample(contentModifier, iconModifier) }
// TO-DO: Chip
item { ChipExample(contentModifier, iconModifier) }
// TO-DO: Toggle Chip
item { ToggleChipExample(contentModifier) }
}
}
For the Wear composable, we have created some Modifiers so we don't have to define them every time. The content is primarily being centered, and some padding added. Then we created a lazy column which is used to show a list of items in a verticle way.
Now we are going to cover some of the components (which are in todo). So without further waiting, let's implement them!
1. Button Component
In the Main Activity, create a new composable function with ButtonExample or paste the following code.
@Composable
fun ButtonExample(
modifier: Modifier = Modifier, iconModifier: Modifier = Modifier
) {
Row(
modifier = modifier, horizontalArrangement = Arrangement.Center
) {
// Button
Button(
modifier = Modifier.size(ButtonDefaults.LargeButtonSize),
onClick = { /* ... */ },
) {
Icon(
imageVector = Icons.Rounded.PlayArrow,
contentDescription = "Play Music",
modifier = iconModifier
)
}
}
}
This will generate a button in the centre which will look like below
Passing the modifier into the row makes it in the centre. While the button uses the large button size and optimizes the size for wear os and now we set the icon and click event to empty lambda.
2. Text composable
We define only the text composable here. its modifier will be as we define previously, the text will be centre aligned, setting a colour to primary and finally setting the string.
@Composable
fun TextExample(modifier: Modifier = Modifier) {
Text(
modifier = modifier,
textAlign = TextAlign.Center,
color = MaterialTheme.colors.primary,
text = "Play Music on "+stringResource(R.string.device_shape)
)
}
3. Card composable
In Wear OS we have two types of cards - AppCard and TitleCard.
@Composable
fun CardExample(
modifier: Modifier = Modifier, iconModifier: Modifier = Modifier
) {
AppCard(modifier = modifier,
appImage = {
Icon(
imageVector = Icons.Rounded.Message,
contentDescription = "triggers open message action",
modifier = iconModifier
)
},
appName = { Text("Messages") },
time = { Text("4m") },
title = { Text("Food Delivery") },
onClick = { /* ... */ }) {
Text("Arriving on time!")
}
}
The title has fewer parameters to add, so we are using AppCard here. In the AppCard, we set its modifier, icon, and text composable's parameter and finally set the main text content at the end. At this point now, the Card will look like this.
4. Chip composable
As Other composable chips also have the same parameters, we define the modifier as passing the top-level modifier. The label has a row scope, so we find the text here, and for an icon that has a box scope, we define an icon here. The code is as follows.
@Composable
fun ChipExample(
modifier: Modifier = Modifier, iconModifier: Modifier = Modifier
) {
Chip(
modifier = modifier,
onClick = { /* ... */ },
label = {
Text(
text = "5 minute Meditation", maxLines = 1, overflow = TextOverflow.Ellipsis
)
},
icon = {
Icon(
imageVector = Icons.Rounded.SelfImprovement,
contentDescription = "triggers meditation action",
modifier = iconModifier
)
},
)
}
5. Toggle composable
Here we define a ToggleChip composable, which uses a switch to toggle around the button. Define a mutable state variable to store the checked variable.
To keep it simple, we set the modifier, state, and switch. We set a lambda on changing the state and a label to set the text composable because the label has a row scope.
Let's see what our app looks like at this moment.
Migrate to ScalingLazyColumn
LazyColumn is so common to show lists in mobile apps, but in the case of wear devices, there is so less space to show list items. Therefore WearOs has its own implementation of lazyColumn called ScalingLazyColumn,
To improve the user's ability to comprehend the material, ScalingLazyColumn extends LazyColumn to support both scaling and transparency at the top and bottom of the screen. Take note of how the object scales up to its full size as it approaches the center and then decreases in size as it moves away (while also becoming more translucent). You can see an example below.
Now in the WearApp() change rememberLazyListState() to ScalingLazyListState() like below.
val listState = rememberLazyListState()
//to
val listState = ScalingLazyListState()
Now go to the LazyColumn() and change this to ScalingLazyColumn(), and add a parameter autoCentering to AutoCenteringParams(itemIndex = 0).
delete contentPadding and verticalArrangement entirely as ScalingLazyColumn already offers default options that ensure a better default visual appearance.
ScalingLazyColumn(
modifier = Modifier.fillMaxSize(),
autoCentering = AutoCenteringParams(itemIndex = 0),
state = listState
) {
//Todos list
}
Output
Till now, we have seen some of the compostables used in the wear os. Let's look at some advanced topics that make Wear OS look and feel really smooth and fancy.
Adding a Scaffold- The Fundamental Layout Blueprint
Scaffold is like a container that provides a basic layout structure to our app screen; it contains some basic parameters like the Top App bar, Snack bar, Floating Action Button, shape, and other elements with modifiers and content.
In the case of wear os, we have timeText, vignette, positionIndicator, and pageIndicator with modifier and content. These look like the below image.
To add a scaffold, add the following code at the top of our WearApp() function.
Scaffold(
timeText = {
//todo
},
vignette = {
//todo
},
positionIndicator = {
//todo
},
) {
//previous code
}
Add Time Text
Incorporating TimeText is a straightforward process. TimeText employs curved text behind the scenes, providing developers with a convenient method to display the time without manually arranging the composable or engaging with time-related classes.
Here is how we can add Time Text.
timeText = {
TimeText(modifier = Modifier.scrollAway(listState))
},
Add Vignette
A Vignette effect delicately blurs the upper and lower edges of the wearable screen, especially when a scrollable screen is presented. Developers possess the flexibility to indicate whether the blurring should be applied to the upper, lower, or both edges, based on the specific use scenario.
Here is how we can add Vignette.
vignette = {
Vignette(vignettePosition = VignettePosition.TopAndBottom)
},
Add a Position Indicator
The PositionIndicator, alternatively referred to as the Scrolling Indicator, is a visual cue positioned on the right side of the screen. It signifies the present location within the content, contingent upon the nature of the state object you provide. In our context, we'll be utilizing the ScalingLazyListState.
Here is how we can add a Position Indicator.
positionIndicator = {
PositionIndicator(
scalingLazyListState = listState
)
}
Here is how our app will look alike after adding the above effects.
Summary
Embark on an exploration of Wear OS app development empowered by Compose, where complexity is replaced by simplicity. This article delves into the practical aspects of setting up the development environment.
From there, readers are guided through the process of creating a simple wearable app, gradually unraveling the powerful capabilities of Compose. Through real-world examples and code snippets, the article demonstrates how to design intuitive user interfaces, implement interactive elements, and optimize the app for the unique form factor of Wear OS devices.
Congratulations! You've successfully completed a UI demonstration featuring a majority of the Wear OS compostables.