Material 3 Expressive is the next evolution of Material Design. It includes updated theming, components, and personalization features like dynamic color.
This guide focuses on migrating from the Wear Compose Material 2.5 (androidx.wear.compose) Jetpack library to the Wear Compose Material 3 (androidx.wear.compose.material3) Jetpack library for apps.
Approaches
For migrating your app code from M2.5 to M3, follow the same approach described in the Compose Material migration phone guidance, in particular:
- You should not use both M2.5 and M3 in a single app long-term
- Adopt a phased approach
Dependencies
M3 has a separate package and version to M2.5:
M2.5
implementation("androidx.wear.compose:compose-material:1.4.0")
M3
implementation("androidx.wear.compose:compose-material3:1.5.0-beta01")
See the latest M3 versions on the Wear Compose Material 3 releases page.
Wear Compose Foundation library version 1.5.0-beta01 introduces
some new components that are designed to work with Material 3
components. Similarly, SwipeDismissableNavHost
from Wear Compose Navigation
library has an updated animation when running on Wear OS 6 (API level 36) or
higher. When updating to Wear Compose Material 3 version, we
suggest to also update the Wear Compose Foundation and Navigation libraries:
implementation("androidx.wear.compose:compose-foundation:1.5.0-beta01")
implementation("androidx.wear.compose:compose-navigation:1.5.0-beta01")
Theming
In both M2.5 and M3, the theme composable is named MaterialTheme
, but the
import packages and parameters differ. In M3, the Colors
parameter has been
renamed to ColorScheme
and MotionScheme
has been introduced for implementing
transitions.
M2.5
import androidx.wear.compose.material.MaterialTheme
MaterialTheme(
colors = AppColors,
typography = AppTypography,
shapes = AppShapes,
content = content
)
M3
import androidx.wear.compose.material3.MaterialTheme
MaterialTheme(
colorScheme = AppColorScheme,
typography = AppTypography,
shapes = AppShapes,
motionScheme = AppMotionScheme,
content = content
)
Color
The color system in M3 is significantly different from M2.5. The number of color
parameters has increased, they have different names, and they map differently to
M3 components. In Compose, this applies to the M2.5 Colors
class, the M3
ColorScheme
class, and related functions:
M2.5
import androidx.wear.compose.material.Colors
val appColorScheme: Colors = Colors(
// M2.5 Color parameters
)
M3
import androidx.wear.compose.material3.ColorScheme
val appColorScheme: ColorScheme = ColorScheme(
// M3 ColorScheme parameters
)
The following table describes the key differences between M2.5 and M3:
M2.5 |
M3 |
---|---|
|
has been renamed to |
13 Colors |
28 Colors |
N/A |
new dynamic color theming |
N/A |
new tertiary colors for more expression |
Dynamic Color Theming
A new feature in M3 is dynamic color theming. If users change the watch face colors, the colors in the UI change to match.
Use the dynamicColorScheme
function to implement dynamic color scheme
and provide a defaultColorScheme
as a fallback in case dynamic color scheme is
not available.
@Composable
fun myApp() {
val myColorScheme = myBrandColors()
val dynamicColorScheme = dynamicColorScheme(LocalContext.current)
MaterialTheme(colorScheme = dynamicColorScheme ?: myBrandColors) {...}
}
Typography
The typography system in M3 is different to M2 and it includes the following features:
- Nine new text styles
- Flex fonts, which allow for customization of the type scales for different weights, widths, and roundness
AnimatedText
, which uses flex fonts
M2.5
import androidx.wear.compose.material.Typography
val Typography = Typography(
// M2.5 TextStyle parameters
)
M3
import androidx.wear.compose.material3.Typography
val Typography = Typography(
// M3 TextStyle parameters
)
Flex Fonts
Flex Fonts allow designers to specify the type width and weight for specific sizes.
Text Styles
The following TextStyles are available in M3. These are employed by default by M3 various components.
Typography |
TextStyle |
---|---|
Display |
displayLarge, displayMedium, displaySmall |
Title |
titleLarge, titleMedium, titleSmall |
Label |
labelLarge, labelMedium, labelSmall |
Body |
bodyLarge, bodyMedium, bodySmall, bodyExtraSmall |
Numeral |
numeralExtraLarge, numeralLarge, numeralMedium, numeralSmall, numeralExtraSmall |
Arc |
arcLarge, arcMedium, arcSmall |
Shape
The shape system in M3 is different to M2. The number of shape parameters has increased, they're named differently, and they map differently to M3 components. The following shape sizes are available:
- Extra-small
- Small
- Medium
- Large
- Extra-large
In Compose, this applies to the M2 Shapes class and the M3 Shapes class:
M2.5
import androidx.wear.compose.material.Shapes
val Shapes = Shapes(
// M2.5 Shapes parameters
)
M3
import androidx.wear.compose.material3.Shapes
val Shapes = Shapes(
// M3 Shapes parameters
)
Use the Shapes parameter mapping from Migrate from Material 2 to Material 3 in Compose as a starting point.
Shape morphing
M3 introduces Shape Morphing: shapes now morph in response to interactions.
Shape Morphing behavior is available as a variation on a number of round buttons, see below:
Buttons |
Shape morphing function |
---|---|
|
IconButtonDefaults.animatedShape() animates the icon button on press |
|
IconToggleButtonDefaults.animatedShape() animates the icon toggle button on press and IconToggleButtonDefaults.variantAnimatedShapes() animates the icon toggle button on press and check/uncheck |
|
TextButtonDefaults.animatedShape() animates the text button on press |
|
TextToggleButtonDefaults.animatedShapes() animates the text toggle on press and TextToggleButtonDefaults.variantAnimatedShapes() animates the text toggle on press and check/uncheck |
Components and Layout
Most components and layouts from M2.5 are available in M3. However, some M3 components and layouts didn't exist in M2.5. Furthermore, some M3 components have more variations than their equivalents in M2.5.
While some components require special considerations, the following function mappings are recommended as a starting point:
Here is a full list of all the Material 3 components:
Material 3 |
Material 2.5 equivalent component (if not new in M3) |
---|---|
New |
|
New |
|
android.wear.compose.material.Scaffold (with androidx.wear.compose.material3.ScreenScaffold ) |
|
New |
|
androidx.wear.compose.material.ToggleChip with a checkbox toggle control |
|
androidx.wear.compose.material.Chip (only when no background is required) |
|
New |
|
New |
|
New |
|
androidx.wear.compose.material.Chip when a tonal button background is required |
|
New |
|
New |
|
New |
|
New |
|
New |
|
androidx.wear.compose.material.ToggleChip with a radio button toggle control |
|
android.wear.compose.material.Scaffold (with androidx.wear.compose material3.AppScaffold) |
|
androidx.wear.compose.material3.SegmentedCircularProgressIndicator |
New |
androidx.wear.compose.material.SwipeToRevealCard and androidx.wear.compose.material.SwipeToRevealChip |
|
androidx.wear.compose.material.ToggleChip with a switch toggle control |
|
New |
And finally a list of some relevant components from Wear Compose Foundation library version 1.5.0-beta01:
Wear Compose Foundation 1.5.0-beta |
|
---|---|
Used to annotate composables in an application, to keep track of the active part of the composition and coordinate focus. |
|
A horizontally scrolling pager, built on the Compose Foundation components with Wear-specific enhancements to improve performance and adherence to Wear OS guidelines. |
|
A vertically scrolling pager, built on the Compose Foundation components with Wear-specific enhancements to improve performance and adherence to Wear OS guidelines. |
|
Can be used instead of ScalingLazyColumn to add scroll transform effects to each item. |
|
Buttons
Buttons in M3 are different from M2.5. The M2.5 Chip has been replaced by
Button. Button
implementation provides default values for Text
maxLines
and textAlign
. Those default values can be overridden in the Text
element.
M2.5
import androidx.wear.compose.material.Chip
//M2.5 Buttons
Chip(...)
CompactChip(...)
Button(...)
M3
import androidx.wear.compose.material3.Button
//M3 Buttons
Button(...)
CompactButton(...)
IconButton(...)
TextButton(...)
M3 also includes new button variations. Check them out on the Compose Material 3 API reference overview.
M3 introduces a new button: EdgeButton. EdgeButton
is available in 4
different sizes: extra small, small, medium, and large. EdgeButton
implementation
provide a default value for maxLines
depending on the size which can be customized.
If you are using TransformingLazyColumn
and ScalingLazyColumn
,
pass the EdgeButton
into the ScreenScaffold
so that it morphs, changing its
shape with scrolling. See the code below to check how to use EdgeButton
with
ScreenScaffold
and TransformingLazyColumn
.
import androidx.wear.compose.material3.EdgeButton
import androidx.wear.compose.material3.ScreenScaffold
ScreenScaffold(
scrollState = state,
contentPadding = contentPadding,
edgeButton = {
EdgeButton(...)
}
){ contentPadding ->
TransformingLazyColumn(state = state, contentPadding = contentPadding,){
// additional code here
}
}
Scaffold
Scaffold in M3 is different from M2.5. In M3, AppScaffold
and the new
ScreenScaffold
composable have replaced Scaffold. AppScaffold
and
ScreenScaffold
lay out the structure of a screen and coordinate transitions of
the ScrollIndicator
and TimeText
components.
AppScaffold
allows static screen elements such as TimeText
to remain visible
during in-app transitions such as swipe-to-dismiss. It provides a slot for the
main application content, which will usually be supplied by a navigation
component such as SwipeDismissableNavHost
You declare one AppScaffold
for Activity and use a ScreenScaffold
for each
Screen.
M2.5
import androidx.wear.compose.material.Scaffold
Scaffold {...}
M3
import androidx.wear.compose.material3.AppScaffold
import androidx.wear.compose.material3.ScreenScaffold
AppScaffold {
// Define the navigation hierarchy within the AppScaffold,
// such as using SwipeDismissableNavHost.
SwipeDismissableNavHost(...) {
composable("home") {
HomeScreen()
}
//other screens
}
}
fun HomeScreen() {
val scrollState = rememberScrollState()
ScreenScaffold(scrollState = scrollState) {
//rest of the screen code
}
}
If you are using a HorizontalPager
with HorizontalPagerIndicator, you
can migrate to HorizontalPagerScaffold
. HorizontalPagerScaffold is placed
within an AppScaffold
. AppScaffold
and HorizontalPagerScaffold
lay out the
structure of a Pager and coordinate transitions of the HorizontalPageIndicator
andvTimeText
components.
HorizontalPagerScaffold
displays the HorizontalPageIndicator
at the
center-end of the screen by default and coordinates showing/hiding TimeText
and HorizontalPageIndicator
according to whether the Pager
is being paged,
this is determined by the PagerState
.
There's also a new AnimatedPage
component, which animates a page within a
Pager with a scaling and scrim effect based on its position.
import androidx.wear.compose.material3.AppScaffold
import androidx.wear.compose.material3.HorizontalPagerScaffold
import androidx.wear.compose.material3.ScreenScaffold
import androidx.wear.compose.foundation.pager.HorizontalPager
import androidx.wear.compose.foundation.pager.rememberPagerState
AppScaffold {
val pagerState = rememberPagerState(pageCount = { 10 })
HorizontalPagerScaffold(pagerState = pagerState) {
HorizontalPager(
state = pagerState,
) { page ->
AnimatedPage(pageIndex = page, pagerState = pagerState) {
ScreenScaffold {
…
}
}
Finally, M3 introduces a VerticalPagerScaffold which follows the same
pattern as the HorizontalPagerScaffold
:
import androidx.wear.compose.material3.AppScaffold
import androidx.wear.compose.material3.HorizontalPagerScaffold
import androidx.wear.compose.material3.ScreenScaffold
import androidx.wear.compose.foundation.pager.VerticalPager
import androidx.wear.compose.foundation.pager.rememberPagerState
AppScaffold {
val pagerState = rememberPagerState(pageCount = { 10 })
VerticalPagerScaffold(pagerState = pagerState) {
VerticalPager(
state = pagerState
) { page ->
AnimatedPage(pageIndex = page, pagerState = pagerState){
ScreenScaffold {
…
}
}
Placeholder
There are some API changes between M2.5 and M3. Placeholder.PlaceholderDefaults
now provides two modifiers:
Modifier.placeholder
, which is drawn instead of content that is not yet loaded- A placeholder shimmer effect
Modifier.placeholderShimmer
which provides a placeholder shimmer effect which runs in an animation loop while waiting for the data to load.
See below for additional changes to the Placeholder
component.
M2.5 |
M3 |
---|---|
|
Has been removed |
|
Has been removed |
|
Has been renamed to |
|
Has been removed |
|
has been removed |
|
Has been removed |
|
Has been removed |
SwipeDismissableNavHost
SwipeDismissableNavHost
is part of wear.compose.navigation
. When this
component is used with M3, the M3 MaterialTheme updates the
LocalSwipeToDismissBackgroundScrimColor
and
LocalSwipeToDismissContentScrimColor
.
TransformingLazyColumn
TransformingLazyColumn
is part of wear.compose.lazy.foundation
and adds
support for scaling and morphing animations on list items during scrolling ,
enhancing the user experience.
Similarly to ScalingLazyColumn
, it provides
rememberTransformingLazyColumnState()
to create a
TransformingLazyColumnState
that is remembered across compositions.
For adding scaling and morphing animations, add the following to each list item:
Modifier.transformedHeight
, which allows you to calculate transformed height of the items using aTransformationSpec
, you can userememberTransformationSpec()
unless you need further customization.- A
SurfaceTransformation
import androidx.wear.compose.material3.AppScaffold
import androidx.wear.compose.material3.ScreenScaffold
import androidx.wear.compose.material3.SurfaceTransformation
import androidx.wear.compose.material3.lazy.rememberTransformationSpec
import androidx.wear.compose.material3.lazy.transformedHeight
import androidx.wear.compose.foundation.lazy.rememberTransformingLazyColumnState
import androidx.wear.compose.foundation.lazy.TransformingLazyColumn
val state = rememberTransformingLazyColumnState()
val transformationSpec = rememberTransformationSpec()
AppScaffold {
ScreenScaffold(state) { contentPadding ->
TransformingLazyColumn(state = state, contentPadding = contentPadding) {
items(count = 50) {
Button(
onClick = {},
modifier =
Modifier.fillMaxWidth().transformedHeight(this, transformationSpec),
transformation = SurfaceTransformation(transformationSpec),
) {
Text("Item $it")
}
}
}
}
}
Useful links
To learn more about migrating from M2.5 to M3 in Compose, consult the following additional resources.
Samples
Wear OS samples in the Material3 branch on GitHub