Compose for Wear OS is now available in a developer preview. Building a Wear OS application with Compose not only makes it easy to follow the Material You guidelines, but also takes advantage of the advantages of Compose. Out of the box, it helps developers quickly build better Wear OS applications with less code. This article will help you better understand how to build with Compose by looking at the main composables of Wear Compose.

If you prefer to see this in video, check it out here.

Main application interface and notification interface

Mobile apps often need to be developed for a variety of different types of interfaces. Typically, the main interface hosting an app is composed of fragments, activities, and Views. In the Compose world, it’s composed of composable items. In addition, you also need to develop for additional notification interfaces, meaning that you need to remind users of important information outside of the main app interface, or allow them to continue doing something they just did, such as tracking a run or playing music, once they start the main app. If you use widgets, you can also use such interfaces to provide information to users.

Different application interfaces in Wear OS

Wear OS has a variety of interfaces. When creating a complete Wear OS application experience, you need to consider the following:

  • Overlay is similar to the main interface of mobile applications. It used to consist of activities, views and fragments, but now consists of composable items. It is suitable for long or complex interactions.

  • The Notification interface also conforms to mobile application development guidelines;

  • A user taps on the dial to open an associated app or perform an independent operation, such as a water logging function that records how many times a day you drink from your cup.

  • Tiles provide more space to display content, and users can slide on the dial in any direction to quickly access information and perform operations.

In this article we will focus on Overlay interfaces and quickly demonstrate a few Wear composables to see how they work and how they are similar to mobile platforms.

Adding dependencies

Before using Wear Compose, we need to make sure we have the right dependency, which is slightly different from the mobile version of Compose. On the mobile version, the main dependencies used are Material, Foundation, UI, Runtime, and Compiler. You can also choose to use Navigation and Animation dependencies. But in Wear, you can use the same UI dependencies, and the Runtime, Compiler, and Animation are all the same. In addition, some other aspects are the same, such as the tool and some of the Compose design concepts, such as using two-way data streams.

For example, you need to use Wear Compose Material instead of Material. Technically speaking, the mobile version of Material can be used directly, but it is not optimized for some of the characteristics of Wear. Similarly we also recommend using Wear Navigation instead of Navigation.

Although we recommend using the Wear Compose Material directly, you can still use Material Ripple and Material Icons Extended, among others. With the correct Wear dependencies added, you are ready to start development.

View the dependency on the Wear Compose document page.

Introduce the Wear OS Material library

The Compose Wear OS Material library provides many of the same composable items that are available on mobile platforms. You can replace Material themes and customize colors, fonts, and more, but they are optimized for the watch. Let’s take a look at some common composable items.

Button

Buttons are compact interface elements that allow users to perform operations or make choices by clicking on them.

The following code makes it easy to add a Button, although the style is different from the mobile version but the code is the same. We initialize a Button composable item in the code and declare some parameters, called Modifier. You can use the Modifier to change many properties, such as onClick, SAME, enabled here. If you also want to add an icon to the Button, Use the Icon modifier containing Painter, contentDescription, and Modifier:

Button(
    modifier = Modifier.size (ButtonDefaults.LargeButtonSize),
    onClick = {... },
    enabled = enabledState
) {
    Icon(
        painter = painterResource (id = R.drawable.ic_phone),
        contentDescription = "phone",
        modifier = Modifier
            .size(24. dp)
            .wrapContentSize(align = Alignment.Center),
    )
}
Copy the code

△ Button combinable item code

With the above code, we can create a nice little Button that looks like this:

△ Effect of Button code

Card

Card is flexible for presenting content and operations on a single topic.

On the left side of the picture, Card shows some ICONS and text. In the middle interface, only text is retained. On the right side, a picture is used as the background.

Delta Card case

In Wear OS, there are two types of cards: AppCard and TitleCard. TitleCard focuses on text display. In this paper, we will focus on AppCard. The following example code creates an AppCard and customizes the contents with Image, Text, and Column successively:

AppCard(appImage = {Image(painter = painterResource(id = r.rawable.ic_message),...) }, appName = { Text ("Messages") },
    time = { Text ("12m" ) },
    title = { Text("Kim Green"}, onClick = {... }, body = { Column(modifier = Modifier.fillMaxWidth()) { Text("On my way!")}},Copy the code

△ Card combinable item code

With the above code, we created a nice Card that looks like this:

△ Effect of Card code

To get a nice card display with a different look, you only need to tweak the code slightly.

Chip

Designed for quick one-click operation, the Chip is especially useful for Wear devices with limited screen space, and the various Chip variants also allow you to be creative.

Here’s the code and implementation for the Chip composable, which you’ll find quite easy to use and similar to some of the code above:

Chip (modifier = modifier. The align (Alignment. CenterHorizontally), onClick = {... }, enabled = enabledState, label = { Text( text ="1 minute Yoga",
            maxLines = 1,
            overflow = TextOverflow.Ellipsis
        )
    },
    icon = {
        Icon(
            painter = painterResource (id = R.drawable.ic_yoga),
            contentDescription= "yoga icon",
            modifier = Modifier
                .size(24.dp)
                .wrapContentSize(align = Alignment.Center),
        )
    },
)
Copy the code

△ Chip combinable item code

△ Chip code effect

ToggleChip

ToggleChip is similar to Chip except that the user uses radio buttons, toggle switches, check boxes:

The ToggleChip use case is shown below:

Delta ToggleChip cases

As shown on the left, the sound can be switched on and off with a tap; On the right, ToggleChip is split, providing two different clickable areas that can be turned off with a button on the right, and the app on the left to edit the alarm. The code is pretty much the same:

ToggleChip(
    modifier = Modifier.height(32.Dp) checked = checkedState, onCheckedChange = {... } label = { Text( text ="Sound",
            maxLines = 1,
            overflow = TextOverflow.Ellipsis
        )
    }
)
Copy the code

Delta ToggleChip code

△ ToggleChip code effect

CurvedText and TimeText

CurvedText is optimized for circular screens, which are important for circular devices, and TimeText is based on composable items created by CurvedText, which handles all the text display work for you in terms of time.

The following code example shows how to create TimeText as CurvedText:

var textBeforeTime by rememberSaveable { mutableStateOf("ETA 99 hours")}// First create a prefix string that is displayed before the time
TimeText(  
// Create a TimeText composable
    leadingCurvedContent = {
        BasicCurvedText(
            text = textBeforeTime,
            style = TimeTextDefaults.timeCurvedTextStyle()
        )
    },
// Specify leadingCurvedContent, which displays the text before the time text, as CurvedText on the curved device.
    leadingLinearContent = {
        Text(
            text = textBeforeTime,
            style = TimeTextDefaults.timeTextStyle()
        )
    },
// Specify leadingLinearContent to display text in front of time text, general display, for non-curved devices.Copy the code

Delta TimeText code

From the above code, we can see that the time text on the circular screen looks like this:

△ TimeText display effect

ScalingLazyColumn

Lists are components that are used in almost every application, and they vertically represent contiguous interface elements. But because the screen space of Wear OS watch devices is very small at the top and bottom of the screen, Material Design introduced the new ScalingLazyColumn for display of zoom and transparency, which helps you to view the contents of the list in a small space.

△ ScalingLazyColumn display effect

The figure above shows the effect of a ScalingLazyColumn. You can see that as the elements slide in, a row in the list grows to full size near the center, and as the element slides out, it gets smaller and smaller (and more transparent) until it disappears completely. This effect is very good for displaying content. Content is easier for users to read.

The underlying ScalingLazyColumn is implemented by LazyColumn, which only processes what is about to be displayed on the screen. It can handle large amounts of data efficiently, and it can be displayed with zoom and transparency, so it should be the default component of Wear OS.

Example code for ScalingLazyColumn looks like this:

val scalingLazyListState: ScalingLazyListState = rememberScalingLazyListState()
 
ScalingLazyColumn(
    modifier = Modifier.fillMaxSize(),
    verticalArrangement = Arrangement.spacedBy(6.dp),
    state = scalingLazyListState,
) {
    items(messageList.size) { message ->
        Card(...) {...}
    }
    item {
        Card(...) {...}
    }
}
Copy the code

△ ScalingLazyColumn sample code

SwipeToDismissBox

This familiar Box component is viewed as a container in the interface and is used on the mobile side, but a proprietary version of Wear, SwipeToDismissBox, is used in your layout and, as the name implies, swipes to close. In Wear OS, the main gesture is swipe, and swipe to the right using SwipeToDismissBox, which is the same as clicking the “back” button.

val state = rememberSwipeToDismissBoxState()
 
SwipeToDismissBox(
    state = state,
){ isBackground ->
    if (isBackground) {
        Box(modifier = Modifier. fillMaxSize().background(MaterialTheme.colors.secondaryVariant))
    } else {
        Column(
            modifier = Modifier.fillMaxSize().background(MaterialTheme.colors.primary),
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.Center,
        ){
            Text ("Swipe to dismiss", color = MaterialTheme.colors.onPrimary)
        }
    }
}
Copy the code

△ SwipeToDismissBox sample code

In the code above, you use the state attribute for SwipeToDismissBox, which is different from mobile. The isBackground callback value is obtained through the passed state, which indicates whether the process is sliding back or not, and you can display different things depending on the state. Here is the final look:

△ SwipeToDismissBox code effect

So far, we’ve covered some of the composables of Wear OS. If you’re familiar with composable development on mobile, you’ll probably find that it’s pretty much the same in Wear OS. In other words, what you learned from Composing can be applied directly to Wear OS development.

The use of Scaffold

Scaffolding enables you to implement interfaces with a basic Material Design layout structure, It provides slots for the most common top-level Material components, such as TopBar, BottomBar, FloatingActionButton, and Drawer. With Scaffold, you can ensure that these components are properly placed and work together. There is also a dedicated version of Wear OS that provides the following three main components in addition to the same Content component as the mobile version:

△ Three main components of a Wear Scaffold

  1. TimeText: You can place time at the top of the screen, which we’ve already covered, in the section on TimeText above;

  2. Vignette: Can provide you with beautiful halo effects around the screen, as shown in the image above;

  3. PositionIndicator: Also known as a scroll indicator, is an indicator on the right side of the screen that displays the position of the current indicator based on the type of status object you pass in. This is placed in the Scaffold because the screen is curved so the position indicator needs to be in the middle of the dial, not just in the middle of the viewport. Otherwise, the indicator may be truncated.

Scaffold design

△ Scaffold design level

The first thing you need to do is set up the App. The second thing you need to do is set up the MaterialTheme to define the look and feel of some applications. The next thing you need to think about is how to place the Scaffold. Finally, the definition of Content. The sequence is the same on the mobile end. First we set the Theme. Then we look at the scaffolding code.

// positionIndicator is outside of Content so raise state to the level above that val scalingLazyListState: ScalingLazyListState = rememberScalingLazyListState() MaterialTheme { Scaffold( modifier = Modifier.fillMaxSize(), timeText = {... }, vignette = { Vignette (vignettePosition = VignettePosition.TopAndBottom) }, PositionIndicator = {// Check state to see if it is rolling, if not, Does not show the if (scalingLazyListState. IsScrollInProgress) {/ / PositionIndicator need to use the state, PositionIndicator(scalingLazyListState = scalingLazyListState)}}) {// Set content... }}Copy the code

Sample code for scaffolding

In the above code, since the positionIndicator is outside the content, state is raised to the level above that Scaffold to prevent it from being truncated on the screen. While scrolling, you can make more room on the screen by checking the scrolling state, hiding the time display, and turning the Vignette effect off or on depending on the state. PositionIndicator supports a variety of scrolling options, in this case we use scalingLazyListState, as well as a number of other cool options, see the documentation for details. The Modifier and TimeText must not be too much to introduce, and vignette Settings are actually very simple.

Navigation

I mentioned at the beginning of this article that you need to use the Wear Navigation dependency to replace Navigation. Again, technically you can still use Navigation, but you may encounter various problems. Therefore, it is recommended that you directly use the Wear Navigation optimized for Wear.

Delta Navigation design

The controller Navigation design is similar to that of the controller; it uses the same design as mobile, but SwipeDismissableNavHost is added under and above the Content. This allows you to seamlessly dismissablenavHost. You can write code directly using the same knowledge you use for mobile application development.

MaterialTheme { Scaffold(...) {val navController = rememberSwipeDismissableNavController()
 
        SwipeDismissableNavHost(
            navController = navController,
            startDestination = Screen.MainScreen.route
        ) {
            composable(route = Screen.MainScreen.route){
                MyListScreen(...)
            }
            composable(route = Screen.DetailsScreen. route + "/"$ID}",...). { MyDetailScreen(...) }}}}Copy the code

△ Navigation sample code

In the above code, the MaterialTheme and Scaffold are the same as before, but we create a navController, And use the rememberSwipeDismissableNavController SwipeDismissable version, the name is very famous, but it is easy to understand its function. Then use SwipeDismissableNavHost to transfer startDestination and its path to the controller, and then set the home screen content. You’ll find that the code is basically the same as it is on the mobile side, making it very easy to understand.

conclusion

In Wear OS, be sure to use appropriate dependencies, replace Material and add Foundation dependencies, and also replace Navigation if you use Navigation. In addition, all of your Compose building knowledge can be applied directly to Wear Compose, and your mobile development experience will help you quickly build a beautiful Wear interface.

For more details, please see:

  • Welcome to the | Wear OS version Compose a developer preview
  • Wear an overview of the OS
  • Documentation guide: Using Jetpack Compose on Wear OS
  • Codelab: Start writing applications for Wear OS
  • Making the sample

Please click here to submit your feedback to us, or share your favorite content or questions. Your feedback is very important to us, thank you for your support!