Bermigrasi dari Material 2.5 ke Material 3 di Compose untuk Wear OS

Material 3 Expressive adalah evolusi berikutnya dari Desain Material. Desain ini mencakup tema, komponen, dan fitur personalisasi yang diperbarui seperti warna dinamis.

Panduan ini berfokus pada migrasi dari library Jetpack Wear Compose Material 2.5 (androidx.wear.compose) ke library Jetpack Wear Compose Material 3 (androidx.wear.compose.material3) untuk aplikasi.

Pendekatan

Untuk memigrasikan kode aplikasi dari M2.5 ke M3, ikuti pendekatan yang sama seperti yang dijelaskan dalam panduan migrasi ponsel Material Compose, khususnya:

Dependensi

M3 memiliki paket dan versi terpisah untuk M2.5:

M2.5

implementation("androidx.wear.compose:compose-material:1.4.0")

M3

implementation("androidx.wear.compose:compose-material3:1.6.0")

Lihat versi M3 terbaru di halaman rilis Wear Compose Material 3.

Library Wear Compose Foundation versi 1.6.0 memperkenalkan beberapa komponen baru yang didesain untuk bekerja dengan komponen Material 3. Demikian pula, SwipeDismissableNavHost dari library Wear Compose Navigation memiliki animasi yang diperbarui saat dijalankan di Wear OS 6 (level API 36) atau yang lebih tinggi. Saat mengupdate ke Wear Compose Material 3, sebaiknya update juga library Wear Compose Foundation dan Navigation:

implementation("androidx.wear.compose:compose-foundation:1.6.0")
implementation("androidx.wear.compose:compose-navigation:1.6.0")

Tema

Pada M2.5 dan M3, composable tema diberi nama MaterialTheme, tetapi paket dan parameter impornya berbeda. Di M3, parameter Colors telah diganti namanya menjadi ColorScheme dan MotionScheme telah diperkenalkan untuk menerapkan transisi.

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 = ColorScheme(),
        typography = Typography(),
        shapes = Shapes(),
        motionScheme = MotionScheme.standard(),
        content = { /*content here*/ }
    )

Warna

Sistem warna di M3 sangat berbeda dengan M2.5. Jumlah parameter warna telah meningkat, memiliki nama berbeda, dan dipetakan berbeda ke komponen M3. Di Compose, ini berlaku untuk class Colors M2.5, class ColorScheme M3, dan fungsi terkait:

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
    )

Tabel berikut menjelaskan perbedaan utama antara M2.5 dan M3:

M2.5 M3
Color Telah diganti namanya menjadi ColorScheme
13 warna 28 warna
T/A Tema warna dinamis baru
T/A Warna tersier baru untuk ekspresi yang lebih banyak

Tema warna dinamis

Fitur baru di M3 adalah tema warna dinamis. Jika pengguna mengubah warna tampilan jam, warna di UI akan berubah agar cocok.

Gunakan fungsi dynamicColorScheme untuk menerapkan skema warna dinamis dan menyediakan defaultColorScheme sebagai penggantian jika skema warna dinamis tidak tersedia.

@Composable
fun myApp() {
    val dynamicColorScheme = dynamicColorScheme(LocalContext.current)
    MaterialTheme(colorScheme = dynamicColorScheme ?: myBrandColors) {}
}

internal val myBrandColors: ColorScheme = ColorScheme( /* Specify colors here */)

Tipografi

Sistem tipografi di M3 berbeda dengan M2.5 dan mencakup fitur berikut:

  • Sembilan gaya teks baru
  • Font fleksibel, yang memungkinkan penyesuaian skala huruf untuk berbagai ketebalan, lebar, dan kebulatan
  • AnimatedText, yang menggunakan font fleksibel

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
)

Font fleksibel

Dengan Font Fleksibel, desainer dapat menentukan lebar dan ketebalan huruf untuk ukuran tertentu.

Gaya teks

TextStyle berikut tersedia di M3. Warna ini digunakan secara default oleh berbagai komponen M3.

Tipografi TextStyle
Tampilan displayLarge, displayMedium, displaySmall
Judul titleLarge, titleMedium, titleSmall
Label labelLarge, labelMedium, labelSmall
Isi bodyLarge, bodyMedium, bodySmall, bodyExtraSmall
Angka numeralExtraLarge, numeralLarge, numeralMedium, numeralSmall, numeralExtraSmall
Busur arcLarge, arcMedium, arcSmall

Bentuk

Sistem bentuk di M3 berbeda dengan M2.5. Jumlah parameter bentuk telah meningkat, diberi nama berbeda, dan dipetakan berbeda ke komponen M3. Ukuran bentuk berikut tersedia:

  • Ekstra kecil
  • Kecil
  • Sedang
  • Besar
  • Ekstra besar

Di Compose, ini berlaku untuk class Shapes M2 dan class Shapes M3:

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
)

Gunakan pemetaan parameter Bentuk dari Bermigrasi dari Material 2 ke Material 3 di Compose sebagai titik awal.

Perubahan bentuk

M3 memperkenalkan Shape Morphing: bentuk kini berubah sebagai respons terhadap interaksi.

Perilaku Perubahan Bentuk tersedia sebagai variasi pada sejumlah tombol bulat, lihat daftar tombol berikut yang mendukung Perubahan Bentuk:

Tombol Fungsi perubahan bentuk
IconButton IconButtonDefaults.animatedShape menganimasikan tombol ikon saat ditekan
IconToggleButton IconToggleButtonDefaults.animatedShape menganimasikan tombol toggle ikon saat ditekan dan
IconToggleButtonDefaults.variantAnimatedShapes menganimasikan tombol toggle ikon saat ditekan dan dicentang/dihapus centangnya
TextButton TextButtonDefaults.animatedShape menganimasikan tombol teks saat ditekan
TextToggleButton TextToggleButtonDefaults.animatedShapes menganimasikan tombol teks saat ditekan dan TextToggleButtonDefaults.variantAnimatedShapes menganimasikan tombol teks saat ditekan dan dicentang/dihapus centangnya

Komponen dan Tata Letak

Sebagian besar komponen dan tata letak dari M2.5 tersedia di M3. Namun, beberapa komponen dan tata letak M3 tidak ada di M2.5. Selain itu, beberapa komponen M3 memiliki lebih banyak variasi dibandingkan dengan yang setara di M2.5.

Meskipun beberapa komponen memerlukan pertimbangan khusus, pemetaan fungsi berikut direkomendasikan sebagai titik awal:

Material 2.5 Material 3
androidx.wear.compose.material.dialog.Alert androidx.wear.compose.material3.AlertDialog
androidx.wear.compose.material.Button androidx.wear.compose.material3.IconButton atau androidx.wear.compose.material3.TextButton
androidx.wear.compose.material.Card androidx.wear.compose.material3.Card
androidx.wear.compose.material.TitleCard androidx.wear.compose.material3.TitleCard
androidx.wear.compose.material.AppCard androidx.wear.compose.material3.AppCard
androidx.wear.compose.material.Checkbox Tidak ada M3 yang setara, migrasikan ke androidx.wear.compose.material3.CheckboxButton atau androidx.wear.compose.material3.SplitCheckboxButton
androidx.wear.compose.material.Chip androidx.wear.compose.material3.Button atau
androidx.wear.compose.material3.OutlinedButton atau
androidx.wear.compose.material3.FilledTonalButton atau
androidx.wear.compose.material3.ChildButton
androidx.wear.compose.material.CompactChip androidx.wear.compose.material3.CompactButton
androidx.wear.compose.material.InlineSlider androidx.wear.compose.material3.Slider
androidx.wear.compose.material.LocalContentAlpha() Telah dihapus karena tidak digunakan oleh Text atau Icon di Material 3
androidx.wear.compose.material.PositionIndicator androidx.wear.compose.material3.ScrollIndicator
androidx.wear.compose.material.RadioButton Tidak ada M3 yang setara, migrasikan ke androidx.wear.compose.material3.RadioButton atau androidx.wear.compose.material3.SplitRadioButton
androidx.wear.compose.material.SwipeToRevealCard androidx.wear.compose.material3.SwipeToReveal
androidx.wear.compose.material.SwipeToRevealChip androidx.wear.compose.material3.SwipeToReveal
android.wear.compose.material.Scaffold androidx.wear.compose.material3.AppScaffold dan androidx.wear.compose.material3.ScreenScaffold
androidx.wear.compose.material.SplitToggleChip Tidak ada padanan M3, bermigrasi ke androidx.wear.compose.material3.SplitCheckboxButton, androidx.wear.compose.material3.SplitSwitchButton, atau androidx.wear.compose.material3.SplitRadioButton
androidx.wear.compose.material.Switch Tidak ada padanan M3, bermigrasi ke androidx.wear.compose.material3.SwitchButton atau androidx.wear.compose.material3.SplitSwitchButton
androidx.wear.compose.material.ToggleButton androidx.wear.compose.material3.IconToggleButton atau androidx.wear.compose.material3.TextToggleButton
androidx.wear.compose.material.ToggleChip androidx.wear.compose.material3.CheckboxButton atau
androidx.wear.compose.material3.RadioButton atau
androidx.wear.compose.material3.SwitchButton
androidx.wear.compose.material.Vignette Dihapus karena tidak disertakan dalam desain Material 3 Expressive untuk Wear OS

Berikut daftar lengkap semua komponen Material 3:

Material 3 Komponen yang setara dengan Material 2.5 (jika tidak baru di M3)
androidx.wear.compose.material3.AlertDialog androidx.wear.compose.material.dialog.Alert
androidx.wear.compose.material3.AnimatedPage Baru
androidx.wear.compose.material3.AnimatedText Baru
androidx.wear.compose.material3.AppScaffold android.wear.compose.material.Scaffold (dengan androidx.wear.compose.material3.ScreenScaffold )
androidx.wear.compose.material3.Button androidx.wear.compose.material.Chip
androidx.wear.compose.material3.ButtonGroup Baru
androidx.wear.compose.material3.Card androidx.wear.compose.material.Card
androidx.wear.compose.material3.CheckboxButton androidx.wear.compose.material.ToggleChip dengan kontrol tombol kotak centang
androidx.wear.compose.material3.ChildButton androidx.wear.compose.material.Chip (hanya jika tidak diperlukan latar belakang)
androidx.wear.compose.material3.CircularProgressIndicator androidx.wear.compose.material.CircularProgressIndicator
androidx.wear.compose.material3.CompactButton androidx.wear.compose.material.CompactChip
androidx.wear.compose.material3.ConfirmationDialog androidx.wear.compose.material.dialog.Confirmation
androidx.wear.compose.material3.curvedText androidx.wear.compose.material.curvedText
androidx.wear.compose.material3.DatePicker Baru
androidx.wear.compose.material3.Dialog androidx.wear.compose.material.dialog.Dialog
androidx.wear.compose.material3.EdgeButton Baru
androidx.wear.compose.material3.FadingExpandingLabel Baru
androidx.wear.compose.material3.FilledTonalButton androidx.wear.compose.material.Chip saat latar belakang tombol tonal diperlukan
androidx.wear.compose.material3.HorizontalPageIndicator androidx.wear.compose.material.HorizontalPageIndicator
androidx.wear.compose.material3.HorizontalPagerScaffold Baru
androidx.wear.compose.material3.Icon androidx.wear.compose.material.Icon
androidx.wear.compose.material3.IconButton androidx.wear.compose.material.Button
androidx.wear.compose.material3.IconToggleButton androidx.wear.compose.material.ToggleButton
androidx.wear.compose.material3.LevelIndicator Baru
androidx.wear.compose.material3.LinearProgressIndicator Baru
androidx.wear.compose.material3.ListHeader androidx.wear.compose.material.ListHeader
androidx.wear.compose.material3.ListSubHeader Baru
androidx.wear.compose.material3.MaterialTheme androidx.wear.compose.material.MaterialTheme
androidx.wear.compose.material3.OpenOnPhoneDialog Baru
androidx.wear.compose.material3.Picker androidx.wear.compose.material.Picker
androidx.wear.compose.material3.PickerGroup androidx.wear.compose.material.PickerGroup
androidx.wear.compose.material3.RadioButton androidx.wear.compose.material.ToggleChip dengan kontrol tombol pilihan
androidx.wear.compose.material3.ScreenScaffold android.wear.compose.material.Scaffold (dengan androidx.wear.compose.material3.AppScaffold)
androidx.wear.compose.material3.ScrollIndicator androidx.wear.compose.material.PositionIndicator
androidx.wear.compose.material3.scrollAway androidx.wear.compose.material.scrollAway
androidx.wear.compose.material3.SegmentedCircularProgressIndicator Baru
androidx.wear.compose.material3.Slider androidx.wear.compose.material.InlineSlider
androidx.wear.compose.material3.SplitRadioButton androidx.wear.compose.material.SplitToggleChip
androidx.wear.compose.material3.SplitCheckboxButton androidx.wear.compose.material.SplitToggleChip
androidx.wear.compose.material3.SplitSwitchButton androidx.wear.compose.material.SplitToggleChip
androidx.wear.compose.material3.Stepper androidx.wear.compose.material.Stepper
androidx.wear.compose.material3.SwipeToDismissBox androidx.wear.compose.material.SwipeToDismissBox
androidx.wear.compose.material3.SwipeToReveal androidx.wear.compose.material.SwipeToRevealCard dan androidx.wear.compose.material.SwipeToRevealChip
androidx.wear.compose.material3.SwitchButton androidx.wear.compose.material.ToggleChip dengan kontrol tombol pengaktifan
androidx.wear.compose.material3.Text androidx.wear.compose.material.Text
androidx.wear.compose.material3.TextButton androidx.wear.compose.material.Button
androidx.wear.compose.material3.TextToggleButton androidx.wear.compose.material.ToggleButton
androidx.wear.compose.material3.TimeText androidx.wear.compose.material.TimeText
androidx.wear.compose.material3.VerticalPagerScaffold Baru

Terakhir, daftar beberapa komponen yang relevan dari library Wear Compose Foundation:

Wear Compose Foundation 1.6.0
androidx.wear.compose.foundation.hierarchicalFocusGroup Digunakan untuk menganotasi composable dalam aplikasi, untuk melacak bagian komposisi yang aktif dan mengoordinasikan fokus.
androidx.wear.compose.foundation.pager.HorizontalPager Pager yang dapat di-scroll secara horizontal, dibangun berdasarkan komponen Compose Foundation dengan peningkatan khusus Wear untuk meningkatkan performa dan kepatuhan terhadap panduan Wear OS.
androidx.wear.compose.foundation.pager.VerticalPager Pager yang dapat di-scroll secara vertikal, dibangun di komponen Compose Foundation dengan peningkatan khusus Wear untuk meningkatkan performa dan kepatuhan terhadap panduan Wear OS.
androidx.wear.compose.foundation.lazy.TransformingLazyColumn Dapat digunakan sebagai pengganti ScalingLazyColumn untuk menambahkan efek transformasi scroll ke setiap item.

Tombol

Tombol di M3 berbeda dengan M2.5. Chip M2.5 telah digantikan oleh Button. Implementasi Button memberikan nilai default untuk Text maxLines dan textAlign. Nilai default tersebut dapat diganti dalam elemen Text.

M2.5

import androidx.wear.compose.material.Chip

//M2.5 Buttons
Chip(...)
CompactChip(...)
Button(...)

M3

//M3 Buttons
Button(onClick = { }){}
CompactButton(onClick = { }){}
IconButton(onClick = { }){}
TextButton(onClick = { }){}

M3 juga menyertakan variasi tombol baru. Lihat di ringkasan Referensi API Compose Material 3.

M3 memperkenalkan tombol baru: EdgeButton. EdgeButton tersedia dalam 4 ukuran berbeda: ekstra kecil, kecil, sedang, dan besar. Implementasi EdgeButton memberikan nilai default untuk maxLines bergantung pada ukuran yang dapat disesuaikan.

Jika Anda menggunakan TransformingLazyColumn atau ScalingLazyColumn, teruskan EdgeButton ke ScreenScaffold sehingga EdgeButton berubah bentuk saat men-scroll, bukan menambahkan EdgeButton sebagai item daftar terakhir. Lihat kode berikut untuk memeriksa cara menggunakan EdgeButton dengan ScreenScaffold dan TransformingLazyColumn.

val state = rememberTransformingLazyColumnState()
ScreenScaffold(
    scrollState = state,
    contentPadding =
        rememberResponsiveColumnPadding(
            first = ColumnItemType.ListHeader
        ),
    edgeButton = {
        EdgeButton(
            onClick = { }
        ) {
            Text(stringResource(R.string.show))
        }
    }
){ contentPadding ->
    TransformingLazyColumn(state = state, contentPadding = contentPadding,){
        // additional code here
    }
}

Scaffold

Scaffold di M3 berbeda dengan M2.5. Di M3, AppScaffold dan composable ScreenScaffold baru telah menggantikan Scaffold. AppScaffold dan ScreenScaffold menata struktur layar dan mengoordinasikan transisi komponen ScrollIndicator dan TimeText.

AppScaffold memungkinkan elemen layar statis seperti TimeText tetap terlihat selama transisi dalam aplikasi seperti geser untuk menutup. ​​Komponen ini menyediakan slot untuk konten aplikasi utama, yang biasanya disediakan oleh komponen navigasi seperti SwipeDismissableNavHost

Anda mendeklarasikan satu AppScaffold untuk Aktivitas dan menggunakan ScreenScaffold untuk setiap Layar. AppScaffold menambahkan komponen TimeTextdefault ke layar. Anda dapat menggantinya jika ingin menyesuaikannya menggunakan parameter timeText.

M2.5

import androidx.wear.compose.material.Scaffold

Scaffold {...}

M3

    AppScaffold {
        val navController = rememberSwipeDismissableNavController()
        SwipeDismissableNavHost(
            navController = navController,
            startDestination = "message_list"
        ) {
            composable("message_list") {
                MessageList(onMessageClick = { id ->
                    navController.navigate("message_detail/$id")
                })
            }
            composable("message_detail/{id}") {
                MessageDetail(id = it.arguments?.getString("id")!!)
            }
        }
    }
}

// Implementation of one of the screens in the navigation
@Composable
fun MessageDetail(id: String) {
    // .. Screen level content goes here
    val scrollState = rememberTransformingLazyColumnState()

    val padding = rememberResponsiveColumnPadding(
        first = ColumnItemType.BodyText
    )

    ScreenScaffold(
        scrollState = scrollState,
        contentPadding = padding
    ) { scaffoldPaddingValues ->
        // Screen content goes here
        // ...

Jika Anda menggunakan HorizontalPager dengan HorizontalPagerIndicator, Anda dapat bermigrasi ke HorizontalPagerScaffold. HorizontalPagerScaffold ditempatkan dalam AppScaffold. AppScaffold dan HorizontalPagerScaffold menata struktur Pager dan mengoordinasikan transisi komponen HorizontalPageIndicator dan TimeText.

HorizontalPagerScaffold menampilkan HorizontalPageIndicator di bagian akhir tengah layar secara default dan mengoordinasikan tampilan dan penyembunyian TimeText dan HorizontalPageIndicator sesuai dengan apakah Pager sedang dipaginasi, yang ditentukan oleh PagerState.

Ada juga komponen AnimatedPage baru, yang menganimasikan halaman dalam Pager dengan efek penskalaan dan scrim berdasarkan posisinya.

AppScaffold {
    val pagerState = rememberPagerState(pageCount = { 10 })
    val columnState = rememberTransformingLazyColumnState()
    val contentPadding = rememberResponsiveColumnPadding(
        first = ColumnItemType.ListHeader,
        last = ColumnItemType.BodyText,
    )
    HorizontalPagerScaffold(pagerState = pagerState) {
        HorizontalPager(
            state = pagerState,
        ) { page ->
            AnimatedPage(pageIndex = page, pagerState = pagerState) {
                ScreenScaffold(
                    scrollState = columnState,
                    contentPadding = contentPadding
                ) { contentPadding ->
                    TransformingLazyColumn(
                        state = columnState,
                        contentPadding = contentPadding
                    ) {
                        item {
                            ListHeader(
                                modifier = Modifier.fillMaxWidth()
                            ) {
                                Text(text = "Pager sample")
                            }
                        }
                        item {
                            if (page == 0) {
                                Text(text = "Page #$page. Swipe right")
                            }
                            else{
                                Text(text = "Page #$page. Swipe left and right")
                            }
                        }
                    }
                }

            }
        }
    }
}

Terakhir, M3 memperkenalkan VerticalPagerScaffold yang mengikuti pola yang sama dengan HorizontalPagerScaffold:

AppScaffold {
    val pagerState = rememberPagerState(pageCount = { 10 })

    VerticalPagerScaffold(pagerState = pagerState) {
        VerticalPager(
            state = pagerState
        ) { page ->
            AnimatedPage(pageIndex = page, pagerState = pagerState) {
                ScreenScaffold {
                    ///…
                }
            }
        }
    }
}

Placeholder

Ada beberapa perubahan API antara M2.5 dan M3. Placeholder.PlaceholderDefaults kini menyediakan dua pengubah:

  • Modifier.placeholder, yang digambar sebagai pengganti konten yang belum dimuat
  • Efek berkilau placeholder Modifier.placeholderShimmer yang memberikan efek berkilau placeholder yang berjalan dalam loop animasi saat menunggu data dimuat.

Lihat tabel berikut untuk mengetahui perubahan tambahan pada komponen Placeholder.

M2.5 M3
PlaceholderState.startPlaceholderAnimation Telah dihapus
PlaceholderState.placeholderProgression Telah dihapus
PlaceholderState.isShowContent Telah diganti namanya menjadi !PlaceholderState.isVisible
PlaceholderState.isWipeOff Telah dihapus
PlaceholderDefaults.painterWithPlaceholderOverlayBackgroundBrush Telah dihapus
PlaceholderDefaults.placeholderBackgroundBrush Telah dihapus
PlaceholderDefaults.placeholderChipColors Telah dihapus

SwipeDismissableNavHost

SwipeDismissableNavHost adalah bagian dari wear.compose.navigation. Saat komponen ini digunakan dengan M3, M3 MaterialTheme akan memperbarui LocalSwipeToDismissBackgroundScrimColor dan LocalSwipeToDismissContentScrimColor.

TransformingLazyColumn

TransformingLazyColumn adalah bagian dari wear.compose.lazy.foundation dan menambahkan dukungan untuk menskalakan dan mengubah animasi pada item daftar selama scrolling, sehingga meningkatkan pengalaman pengguna. Sangat direkomendasikan agar aplikasi bermigrasi dari ScalingLazyColumn ke TransformingLazyColumn

Mirip dengan ScalingLazyColumn, rememberTransformingLazyColumnState() menyediakan rememberTransformingLazyColumnState() untuk membuat TransformingLazyColumnState yang diingat di seluruh komposisi.

Untuk menambahkan animasi penskalaan dan pengubahan bentuk, tambahkan kode berikut ke setiap item daftar:

  • Modifier.transformedHeight, yang memungkinkan Anda menghitung tinggi item yang diubah menggunakan TransformationSpec, Anda dapat menggunakan rememberTransformationSpec() kecuali jika Anda memerlukan penyesuaian lebih lanjut.
  • SurfaceTransformation

Untuk memverifikasi bahwa padding sudah benar di bagian atas dan bawah daftar, gunakan pengubah minimumVerticalContentPadding.

val columnState = rememberTransformingLazyColumnState()
val transformationSpec = rememberTransformationSpec()
ScreenScaffold(
    scrollState = columnState
) { contentPadding ->
    TransformingLazyColumn(
        state = columnState,
        contentPadding = contentPadding
    ) {
        item {
            ListHeader(
                modifier = Modifier
                    .fillMaxWidth()
                    .transformedHeight(this, transformationSpec)
                    .minimumVerticalContentPadding(ListHeaderDefaults.minimumTopListContentPadding),
                transformation = SurfaceTransformation(transformationSpec)
            ) {
                Text(text = "Header")
            }
        }
        // ... other items
        item {
            Button(
                modifier = Modifier
                    .fillMaxWidth()
                    .transformedHeight(this, transformationSpec)
                    .minimumVerticalContentPadding(ButtonDefaults.minimumVerticalListContentPadding),
                transformation = SurfaceTransformation(transformationSpec),
                onClick = { /* ... */ },
                icon = {
                    Icon(
                        imageVector = Icons.Default.Build,
                        contentDescription = "build",
                    )
                },
            ) {
                Text(
                    text = "Build",
                    maxLines = 1,
                    overflow = TextOverflow.Ellipsis,
                )
            }
        }
    }
}

Untuk mempelajari migrasi dari M2.5 ke M3 di Compose lebih lanjut, lihat referensi tambahan berikut.

Contoh

Referensi API dan kode sumber

Desain