Penerapan tema di Compose dengan Material 3

1. Pengantar

Dalam codelab ini, Anda akan mempelajari tentang cara menerapkan tema aplikasi di Jetpack Compose menggunakan Desain Material 3. Anda juga akan mempelajari elemen penyusun utama Skema warna, tipografi, dan bentuk Desain Material 3, yang membantu Anda menerapkan tema aplikasi dengan cara yang dipersonalisasi dan mudah diakses.

Selain itu, Anda akan menjelajahi dukungan tema dinamis beserta berbagai tingkat penekanan.

Yang akan Anda pelajari

Dalam codelab ini, Anda akan mempelajari:

  • Aspek utama tema Material 3
  • Skema warna Material 3 dan cara membuat tema untuk aplikasi Anda
  • Cara mendukung tema dinamis dan terang/gelap untuk aplikasi Anda
  • Tipografi dan bentuk untuk mempersonalisasi aplikasi Anda
  • Komponen Material 3 dan penyesuaian untuk menyesuaikan gaya aplikasi Anda

Yang akan Anda build

Dalam codelab ini, Anda akan menerapkan tema sebuah aplikasi program email yang disebut Reply. Anda memulai dengan aplikasi tanpa gaya, menggunakan tema dasar bawaan, serta akan menerapkan apa yang telah Anda pelajari untuk memberi tema aplikasi dan mendukung tema gelap.

d15db3dc75a9d00f.png

Titik awal default dari aplikasi kami dengan tema dasar bawaan.

Anda akan membuat tema dengan skema warna, tipografi, dan bentuk, lalu menerapkannya ke daftar email dan halaman detail aplikasi Anda. Anda juga akan menambahkan dukungan tema dinamis ke aplikasi. Di akhir codelab, Anda akan mendapatkan dukungan untuk tema warna dan dinamis untuk aplikasi Anda.

Material 3 terang

Titik akhir dari penerapan tema codelab dengan tema warna terang dan tema dinamis terang.

Material 3 gelap

Titik akhir dari penerapan tema codelab dengan tema warna gelap dan tema dinamis gelap.

Yang akan Anda butuhkan

2. Mempersiapkan

Pada langkah ini, Anda akan mendownload kode lengkap aplikasi Reply yang akan Anda rancang dalam codelab ini.

Mendapatkan kode

Kode untuk codelab ini dapat ditemukan di repositori GitHub codelab-android-compose. Untuk melakukan clone kode ini, jalankan:

$ git clone https://github.com/android/codelab-android-compose

Atau, Anda dapat mendownload dua file ZIP:

Melihat aplikasi contoh

Kode yang baru saja Anda download berisi kode untuk semua codelab Compose yang tersedia. Untuk menyelesaikan codelab ini, buka project ThemingCodelab di dalam Android Studio.

Sebaiknya Anda memulai dengan kode di cabang utama dan mengikuti codelab langkah demi langkah sesuai kemampuan Anda. Anda dapat menjalankan salah satu versi tersebut di Android Studio kapan saja dengan mengubah cabang git project.

Mempelajari kode mulai

Kode utama berisi paket UI, yang memiliki paket dan file utama berikut yang akan berinteraksi dengan Anda:

  • MainActivity.kt – Aktivitas titik entri tempat Anda memulai aplikasi Reply.
  • com.example.reply.ui.theme – Paket ini berisi tema, tipografi, dan skema warna. Anda akan menambahkan tema Material dalam paket ini.
  • com.example.reply.ui.components – Berisi komponen kustom aplikasi seperti Item Daftar, Panel Aplikasi, dll. Anda akan menerapkan tema ke komponen ini.
  • ReplyApp.kt – Ini adalah fungsi Composable utama kami tempat pohon UI akan dimulai. Anda akan menerapkan tema tingkat atas dalam file ini.

Codelab ini akan berfokus pada file paket ui.

3. Penerapan Tema Material 3

Jetpack Compose menawarkan implementasi Desain Material—sistem desain komprehensif untuk membuat antarmuka digital. Komponen Desain Material (Tombol, Kartu, Tombol Akses, dll.) dibuat di atas Tema Material yang merupakan cara sistematis untuk menyesuaikan Desain Material agar lebih mencerminkan merek produk Anda.

Tema Material 3 terdiri dari subsistem berikut untuk menambahkan tema ke aplikasi Anda: skema warna, tipografi, dan bentuk. Saat Anda menyesuaikan nilai ini, perubahan akan otomatis tercermin dalam komponen M3 yang digunakan untuk mem-build aplikasi. Mari pelajari setiap subsistem dan terapkan di aplikasi contoh.

Subsistem desain Material: Warna, Tipografi, dan Bentuk.

Subsistem warna, tipografi, dan bentuk Material 3.

4. Skema warna

Fondasi skema warna merupakan kumpulan lima warna utama yang masing-masing terkait dengan palet tonal 13 tone yang digunakan oleh komponen Material 3.

Lima warna utama bawaan untuk membuat tema M3.

Lima warna utama bawaan untuk membuat tema M3.

Setiap warna aksen (utama, sekunder, dan tersier) disediakan dalam empat warna kompatibel yang berbeda dengan warna berbeda untuk penyandingan, penentuan penekanan, dan ekspresi visual.

Empat warna tonal dari warna aksen dasar bawaan primer, sekunder, dan tersier.

Empat warna tonal dari warna aksen dasar bawaan primer, sekunder, dan tersier.

Demikian pula, warna netral juga dibagi menjadi empat nuansa kompatibel yang digunakan untuk permukaan dan latar belakang. Hal ini juga penting untuk menekankan ikon teks saat ditempatkan di permukaan mana pun.

Empat warna tonal warna netral bawaan.

Empat warna tonal warna netral bawaan.

Baca selengkapnya tentang Skema warna dan peran warna.

Membuat skema warna

Meskipun Anda dapat membuat ColorScheme kustom secara manual, akan jauh lebih mudah untuk membuatnya menggunakan warna sumber dari merek. Alat Builder Tema Material memungkinkan Anda melakukannya, dan jika ingin, mengekspor kode tema Compose.

Anda dapat memilih warna yang diinginkan, tetapi untuk kasus penggunaan, Anda akan menggunakan warna utama Reply default #825500. Klik warna Primer di bagian kiri Warna inti dan tambahkan kode di pemilih warna.

294f73fc9d2a570e.png

Menambahkan kode warna utama di Builder Tema Material.

Setelah menambahkan warna utama di Builder Tema Material, Anda akan melihat tema berikut dan opsi untuk mengekspor di sudut kanan atas. Untuk codelab ini, Anda mengekspor tema di Jetpack Compose.

Builder Tema Material dengan opsi untuk mengekspor di sudut kanan atas.

Builder Tema Material dengan opsi untuk mengekspor di sudut kanan atas.

Warna primer #825500 menghasilkan tema berikut yang akan Anda tambahkan ke aplikasi. Material 3 memberikan berbagai peran warna untuk mengekspresikan status, keterlihatan, dan penekanan komponen secara fleksibel.

Skema warna terang dan gelap yang diekspor dari warna primer.

Skema warna terang dan gelap yang diekspor dari warna primer.

File yang dihasilkan The Color.kt berisi warna tema Anda dengan semua peran yang ditentukan untuk warna tema terang dan gelap.

Color.kt

package com.example.reply.ui.theme
import androidx.compose.ui.graphics.Color

val md_theme_light_primary = Color(0xFF825500)
val md_theme_light_onPrimary = Color(0xFFFFFFFF)
val md_theme_light_primaryContainer = Color(0xFFFFDDB3)
val md_theme_light_onPrimaryContainer = Color(0xFF291800)
val md_theme_light_secondary = Color(0xFF6F5B40)
val md_theme_light_onSecondary = Color(0xFFFFFFFF)
val md_theme_light_secondaryContainer = Color(0xFFFBDEBC)
val md_theme_light_onSecondaryContainer = Color(0xFF271904)
val md_theme_light_tertiary = Color(0xFF51643F)
val md_theme_light_onTertiary = Color(0xFFFFFFFF)
val md_theme_light_tertiaryContainer = Color(0xFFD4EABB)
val md_theme_light_onTertiaryContainer = Color(0xFF102004)
val md_theme_light_error = Color(0xFFBA1A1A)
val md_theme_light_errorContainer = Color(0xFFFFDAD6)
val md_theme_light_onError = Color(0xFFFFFFFF)
val md_theme_light_onErrorContainer = Color(0xFF410002)
val md_theme_light_background = Color(0xFFFFFBFF)
val md_theme_light_onBackground = Color(0xFF1F1B16)
val md_theme_light_surface = Color(0xFFFFFBFF)
val md_theme_light_onSurface = Color(0xFF1F1B16)
val md_theme_light_surfaceVariant = Color(0xFFF0E0CF)
val md_theme_light_onSurfaceVariant = Color(0xFF4F4539)
val md_theme_light_outline = Color(0xFF817567)
val md_theme_light_inverseOnSurface = Color(0xFFF9EFE7)
val md_theme_light_inverseSurface = Color(0xFF34302A)
val md_theme_light_inversePrimary = Color(0xFFFFB951)
val md_theme_light_shadow = Color(0xFF000000)
val md_theme_light_surfaceTint = Color(0xFF825500)
val md_theme_light_outlineVariant = Color(0xFFD3C4B4)
val md_theme_light_scrim = Color(0xFF000000)

val md_theme_dark_primary = Color(0xFFFFB951)
val md_theme_dark_onPrimary = Color(0xFF452B00)
val md_theme_dark_primaryContainer = Color(0xFF633F00)
val md_theme_dark_onPrimaryContainer = Color(0xFFFFDDB3)
val md_theme_dark_secondary = Color(0xFFDDC2A1)
val md_theme_dark_onSecondary = Color(0xFF3E2D16)
val md_theme_dark_secondaryContainer = Color(0xFF56442A)
val md_theme_dark_onSecondaryContainer = Color(0xFFFBDEBC)
val md_theme_dark_tertiary = Color(0xFFB8CEA1)
val md_theme_dark_onTertiary = Color(0xFF243515)
val md_theme_dark_tertiaryContainer = Color(0xFF3A4C2A)
val md_theme_dark_onTertiaryContainer = Color(0xFFD4EABB)
val md_theme_dark_error = Color(0xFFFFB4AB)
val md_theme_dark_errorContainer = Color(0xFF93000A)
val md_theme_dark_onError = Color(0xFF690005)
val md_theme_dark_onErrorContainer = Color(0xFFFFDAD6)
val md_theme_dark_background = Color(0xFF1F1B16)
val md_theme_dark_onBackground = Color(0xFFEAE1D9)
val md_theme_dark_surface = Color(0xFF1F1B16)
val md_theme_dark_onSurface = Color(0xFFEAE1D9)
val md_theme_dark_surfaceVariant = Color(0xFF4F4539)
val md_theme_dark_onSurfaceVariant = Color(0xFFD3C4B4)
val md_theme_dark_outline = Color(0xFF9C8F80)
val md_theme_dark_inverseOnSurface = Color(0xFF1F1B16)
val md_theme_dark_inverseSurface = Color(0xFFEAE1D9)
val md_theme_dark_inversePrimary = Color(0xFF825500)
val md_theme_dark_shadow = Color(0xFF000000)
val md_theme_dark_surfaceTint = Color(0xFFFFB951)
val md_theme_dark_outlineVariant = Color(0xFF4F4539)
val md_theme_dark_scrim = Color(0xFF000000)

val seed = Color(0xFF825500)

File yang dihasilkan The Theme.kt berisi penyiapan skema warna terang dan gelap serta tema aplikasi. Ini juga berisi fungsi composable tema utama, AppTheme().

Theme.kt

package com.example.reply.ui.theme

import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.runtime.Composable

private val LightColors = lightColorScheme(
   primary = md_theme_light_primary,
   onPrimary = md_theme_light_onPrimary,
   primaryContainer = md_theme_light_primaryContainer,
   onPrimaryContainer = md_theme_light_onPrimaryContainer,
   secondary = md_theme_light_secondary,
   onSecondary = md_theme_light_onSecondary,
   secondaryContainer = md_theme_light_secondaryContainer,
   onSecondaryContainer = md_theme_light_onSecondaryContainer,
   tertiary = md_theme_light_tertiary,
   onTertiary = md_theme_light_onTertiary,
   tertiaryContainer = md_theme_light_tertiaryContainer,
   onTertiaryContainer = md_theme_light_onTertiaryContainer,
   error = md_theme_light_error,
   errorContainer = md_theme_light_errorContainer,
   onError = md_theme_light_onError,
   onErrorContainer = md_theme_light_onErrorContainer,
   background = md_theme_light_background,
   onBackground = md_theme_light_onBackground,
   surface = md_theme_light_surface,
   onSurface = md_theme_light_onSurface,
   surfaceVariant = md_theme_light_surfaceVariant,
   onSurfaceVariant = md_theme_light_onSurfaceVariant,
   outline = md_theme_light_outline,
   inverseOnSurface = md_theme_light_inverseOnSurface,
   inverseSurface = md_theme_light_inverseSurface,
   inversePrimary = md_theme_light_inversePrimary,
   surfaceTint = md_theme_light_surfaceTint,
   outlineVariant = md_theme_light_outlineVariant,
   scrim = md_theme_light_scrim,
)

private val DarkColors = darkColorScheme(
   primary = md_theme_dark_primary,
   onPrimary = md_theme_dark_onPrimary,
   primaryContainer = md_theme_dark_primaryContainer,
   onPrimaryContainer = md_theme_dark_onPrimaryContainer,
   secondary = md_theme_dark_secondary,
   onSecondary = md_theme_dark_onSecondary,
   secondaryContainer = md_theme_dark_secondaryContainer,
   onSecondaryContainer = md_theme_dark_onSecondaryContainer,
   tertiary = md_theme_dark_tertiary,
   onTertiary = md_theme_dark_onTertiary,
   tertiaryContainer = md_theme_dark_tertiaryContainer,
   onTertiaryContainer = md_theme_dark_onTertiaryContainer,
   error = md_theme_dark_error,
   errorContainer = md_theme_dark_errorContainer,
   onError = md_theme_dark_onError,
   onErrorContainer = md_theme_dark_onErrorContainer,
   background = md_theme_dark_background,
   onBackground = md_theme_dark_onBackground,
   surface = md_theme_dark_surface,
   onSurface = md_theme_dark_onSurface,
   surfaceVariant = md_theme_dark_surfaceVariant,
   onSurfaceVariant = md_theme_dark_onSurfaceVariant,
   outline = md_theme_dark_outline,
   inverseOnSurface = md_theme_dark_inverseOnSurface,
   inverseSurface = md_theme_dark_inverseSurface,
   inversePrimary = md_theme_dark_inversePrimary,
   surfaceTint = md_theme_dark_surfaceTint,
   outlineVariant = md_theme_dark_outlineVariant,
   scrim = md_theme_dark_scrim,
)

@Composable
fun AppTheme(
   useDarkTheme: Boolean = isSystemInDarkTheme(),
   content: @Composable() () -> Unit
) {
   val colors = if (!useDarkTheme) {
       LightColors
   } else {
       DarkColors
   }

   MaterialTheme(
       colorScheme = colors,
       content = content
   )
}

Elemen inti untuk menerapkan tema ke Jetpack Compose adalah composable MaterialTheme.

Anda menggabungkan composable MaterialTheme() dalam fungsi AppTheme(), yang menggunakan dua parameter:

  • useDarkTheme - parameter ini terikat dengan fungsi isSystemInDarkTheme() untuk mengamati setelan tema sistem dan menerapkan tema terang atau gelap. Jika ingin aplikasi tetap menggunakan tema terang atau gelap secara manual, Anda dapat meneruskan nilai boolean ke useDarkTheme.
  • content - konten tempat tema akan diterapkan.

Theme.kt

@Composable
fun AppTheme(
   useDarkTheme: Boolean = isSystemInDarkTheme(),
   content: @Composable() () -> Unit
) {
   val colors = if (!useDarkTheme) {
       LightColors
   } else {
       DarkColors
   }

   MaterialTheme(
       colorScheme = colors,
       content = content
   )
}

Jika Anda mencoba menjalankan aplikasi sekarang, Anda akan melihat bahwa aplikasi terlihat sama. Meskipun Anda telah mengimpor skema warna baru dengan warna tema baru, Anda masih melihat tema dasar bawaan karena Anda belum menerapkan tema ke aplikasi Compose.

Aplikasi dengan tema dasar bawaan saat tidak ada tema yang diterapkan.

Aplikasi dengan tema dasar bawaan saat tidak ada tema yang diterapkan.

Untuk menerapkan tema baru, di MainActivity.kt, gabungkan composable utama ReplyApp dengan fungsi tema utama, AppTheme().

MainActivity.kt

setContent {
   val uiState by viewModel.uiState.collectAsStateWithLifecycle()

   AppTheme {
       ReplyApp(/*..*/)
   }
}

Anda juga akan mengupdate fungsi pratinjau untuk melihat tema yang diterapkan ke pratinjau aplikasi. Gabungkan composable ReplyApp di dalam ReplyAppPreview() dengan AppTheme untuk menerapkan tema ke pratinjau.

Anda memiliki tema sistem terang dan gelap yang ditentukan dalam parameter pratinjau sehingga Anda akan melihat kedua pratinjau.

MainActivity.kt

@Preview(
   uiMode = Configuration.UI_MODE_NIGHT_YES,
   name = "DefaultPreviewDark"
)
@Preview(
   uiMode = Configuration.UI_MODE_NIGHT_NO,
   name = "DefaultPreviewLight"
)
@Composable
fun ReplyAppPreview() {
   AppTheme {
       ReplyApp(
           replyHomeUIState = ReplyHomeUIState(
               emails = LocalEmailsDataProvider.allEmails
           )
       )
   }
}

Jika menjalankan aplikasi sekarang, Anda akan melihat pratinjau aplikasi dengan warna tema yang diimpor, bukan tema dasar bawaan.

fddf7b9cc99b1fe3.png be7a661b4553167b.png

Aplikasi dengan tema dasar bawaan (Kiri).

Aplikasi dengan tema warna yang diimpor (Kanan).

674cec6cc12db6a0.png

Pratinjau aplikasi terang dan gelap dengan tema warna yang diimpor.

Material 3 mendukung skema warna terang dan gelap. Anda hanya menggabungkan aplikasi dengan tema yang diimpor; komponen Material 3 menggunakan peran warna default.

Mari kita pelajari peran dan penggunaan warna sebelum Anda mulai menambahkannya ke aplikasi.

Peran dan aksesibilitas warna

Setiap peran warna dapat digunakan di berbagai tempat, bergantung pada status, keterlihatan, dan penekanan komponen.

1f184a05ea57aa84.png

Peran warna dari warna primer, sekunder, dan tersier.

Primer adalah warna dasar, yang digunakan untuk komponen utama seperti tombol yang menarik dan status aktif.

Warna tombol sekunder digunakan untuk komponen yang kurang menarik di UI, seperti chip filter.

Warna tombol tersier digunakan untuk memberikan aksen yang kontras, dan warna netral digunakan untuk latar belakang dan permukaan di aplikasi.

Sistem warna Material memberikan nilai tone standar dan pengukuran yang dapat digunakan untuk memenuhi rasio kontras yang dapat diakses. Gunakan on-primary di atas primer, penampung on-primary di atas penampung primer, serta hal yang sama untuk warna aksen dan netral lainnya untuk memberikan kontras yang dapat diakses oleh pengguna.

Untuk mengetahui informasi selengkapnya, lihat peran dan aksesibilitas warna.

Elevasi tonal dan bayangan

Material 3 mewakili elevasi terutama menggunakan overlay warna tonal. Ini adalah cara baru untuk membedakan penampung dan permukaan satu sama lain—meningkatkan elevasi tonal menggunakan warna yang lebih menarik—selain bayangan.

Elevasi tonal dengan elevasi bayangan Elevasi tonal di level 2 yang mengambil warna dari slot warna utama.

Overlay elevasi dalam tema gelap juga berubah menjadi overlay warna tonal di Desain Material 3. Warna overlay berasal dari slot warna utama.

M3 Surface—composable pendukung di balik sebagian besar komponen M3—mencakup dukungan untuk elevasi tonal dan bayangan:

Surface(
   modifier = modifier,
   tonalElevation = {..}
   shadowElevation = {..}
) {
   Column(content = content)
}

Menambahkan warna ke aplikasi

Jika menjalankan aplikasi, Anda dapat melihat warna yang diekspor dan ditampilkan di aplikasi tempat komponen mengambil warna default. Kini setelah mengetahui peran warna dan penggunaan, mari kita terapkan tema aplikasi dengan peran warna yang benar.

be7a661b4553167b.png

Aplikasi dengan komponen dan tema warna yang menggunakan peran warna default.

Warna permukaan

Di layar utama, Anda akan memulai dengan menggabungkan composable aplikasi utama di Surface() untuk memberikan dasar bagi konten aplikasi yang akan ditempatkan di atasnya. Buka MainActivity.kt dan gabungkan composable ReplyApp() dengan Surface.

Anda juga akan memberikan elevasi tonal 5.dp untuk memberikan warna tonal slot utama ke permukaan, yang akan membantu memberikan kontras terhadap item daftar dan kotak penelusuran di atasnya. Secara default, elevasi tonal dan bayangan untuk permukaan adalah 0.dp.

MainActivity.kt

AppTheme {
   Surface(tonalElevation = 5.dp) {
       ReplyApp(
           replyHomeUIState = uiState,
          // other parameters
         )
   }
}

Jika Anda menjalankan aplikasi sekarang dan melihat halaman Daftar dan Detail, Anda akan melihat permukaan tonal yang diterapkan ke seluruh aplikasi.

be7a661b4553167b.png e70d762495173610.png

Latar belakang aplikasi tanpa permukaan dan warna tonal (Kiri).

Latar belakang aplikasi dengan warna permukaan dan tonal yang diterapkan (Kanan).

Warna panel aplikasi

Kotak penelusuran kustom di bagian atas tidak memiliki latar belakang yang jelas sebagai permintaan desain. Secara default, opsi ini kembali ke permukaan dasar default. Anda dapat memberikan latar belakang untuk memberikan pemisahan yang jelas.

5779fc399d8a8187.png

Kotak penelusuran kustom tanpa latar belakang (Kiri).

Kotak penelusuran kustom dengan latar belakang (Kanan).

Kini Anda akan mengedit ui/components/ReplyAppBars.kt yang berisi panel aplikasi. Anda akan menambahkan MaterialTheme.colorScheme.background ke Modifier Composable Row.

ReplyAppBars.kt

@Composable
fun ReplySearchBar(modifier: Modifier = Modifier) {
   Row(
       modifier = modifier
           .fillMaxWidth()
           .padding(16.dp)
           .background(MaterialTheme.colorScheme.background),
       verticalAlignment = Alignment.CenterVertically
   ) {
       // Search bar content
   }
}

Anda kini akan melihat pemisahan yang jelas antara permukaan tonal dan panel aplikasi dengan warna latar belakang.

b1b374b801dadc06.png

Kotak penelusuran dengan warna latar belakang di atas permukaan tonal.

Warna tombol tindakan mengambang

70ceac87233fe466.png

FAB besar tanpa penerapan tema (Kiri).

FAB besar bertema dengan warna tersier (Kanan).

Di layar utama, Anda dapat meningkatkan tampilan tombol tindakan mengambang (FAB) sehingga dapat tampil menarik sebagai tombol pesan ajakan (CTA). Untuk menerapkannya, Anda akan menerapkan warna aksen tersier.

Dalam file ReplyListContent.kt, perbarui containerColor untuk warna FAB menjadi tertiaryContainer dan warna konten menjadi onTertiaryContainer untuk mempertahankan aksesibilitas dan kontras warna.

ReplyListContent.kt

ReplyInboxScreen(/*..*/) {
// Email list content
  LargeFloatingActionButton(
    containerColor = MaterialTheme.colorScheme.tertiaryContainer,
    contentColor = MaterialTheme.colorScheme.onTertiaryContainer
  ){
   /*..*/
  }
}

Jalankan aplikasi untuk melihat tema FAB Anda. Untuk codelab ini, Anda menggunakan LargeFloatingActionButton.

Warna kartu

Daftar email di layar utama menggunakan komponen kartu. Secara default, ini adalah Kartu yang diisi yang menggunakan warna varian permukaan untuk warna penampung guna memberikan pemisah yang jelas antara warna permukaan dan kartu. Compose juga menyediakan implementasi ElevatedCard dan OutlinedCard.

Anda dapat lebih lanjut menyoroti beberapa item penting dengan memberikan tone warna sekunder. Anda akan mengubah ui/components/ReplyEmailListItem.kt dengan memperbarui warna penampung kartu menggunakan CardDefaults.cardColors() untuk email penting:

ReplyEmailListItem.kt

Card(
   modifier =  modifier
       .padding(horizontal = 16.dp, vertical = 4.dp)
       .semantics { selected = isSelected }
       .clickable { navigateToDetail(email.id) },
   colors = CardDefaults.cardColors(
       containerColor = if (email.isImportant)
           MaterialTheme.colorScheme.secondaryContainer
       else MaterialTheme.colorScheme.surfaceVariant
   )
){
  /*..*/
}

5818200be0b01583.png 9367d40023db371d.png

Tandai item daftar menggunakan warna penampung sekunder di permukaan tonal.

Warna item daftar detail

Kini Anda memiliki tema layar utama. Lihat halaman detail dengan mengklik salah satu item daftar email.

7a9ea7cf3e91e9c7.png 79b3874aeca4cd1.png

Halaman detail default tanpa item daftar bertema (Kiri).

Item daftar detail dengan penerapan tema latar belakang (Kanan).

Tidak ada warna yang diterapkan pada item daftar Anda sehingga kembali ke warna permukaan tonal default. Anda akan menerapkan warna latar belakang ke item daftar untuk membuat pemisahan dan menambahkan padding untuk memberikan jarak di sekitar latar belakang.

ReplyEmailThreadItem.kt

@Composable
fun ReplyEmailThreadItem(
   email: Email,
   modifier: Modifier = Modifier
) {
   Column(
       modifier = modifier
           .fillMaxWidth()
           .padding(16.dp)
           .background(MaterialTheme.colorScheme.background)
           .padding(20.dp)
    ) {
      // List item content
    }
}

Anda dapat melihat bahwa, hanya dengan memberikan latar belakang, Anda memiliki pemisah yang jelas antara permukaan tonal dan item daftar.

Anda kini memiliki halaman beranda dan halaman detail dengan peran warna dan penggunaan yang benar . Mari kita lihat bagaimana aplikasi Anda dapat memanfaatkan warna dinamis untuk memberikan pengalaman yang lebih dipersonalisasi dan kohesif.

5. Menambahkan warna dinamis di aplikasi

Warna dinamis adalah bagian penting dari Material 3, tempat algoritma memperoleh warna kustom dari wallpaper pengguna untuk diterapkan ke aplikasi dan UI sistemnya.

Tema dinamis membuat aplikasi Anda lebih dipersonalisasi. Hal ini juga memberi pengguna pengalaman yang kohesif dan lancar dengan tema sistem.

Warna dinamis tersedia di Android 12 dan yang lebih baru. Jika warna dinamis tersedia, Anda dapat menyiapkan skema warna dinamis menggunakan dynamicDarkColorScheme() atau dynamicLightColorScheme(). Jika tidak, Anda harus beralih kembali ke penggunaan ColorScheme terang atau gelap default.

Ganti kode fungsi AppTheme di file Theme.kt dengan kode di bawah ini:

Theme.kt

@Composable
fun AppTheme(
   useDarkTheme: Boolean =  isSystemInDarkTheme(),
   content: @Composable () -> Unit
) {
   val context = LocalContext.current
   val colors = when {
       (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) -> {
           if (useDarkTheme) dynamicDarkColorScheme(context)
           else dynamicLightColorScheme(context)
       }
       useDarkTheme -> DarkColors
       else -> LightColors
   }

      MaterialTheme(
       colorScheme = colors,
       content = content
     )
}

fecc63b4c6034236.png

Tema dinamis yang diambil dari wallpaper Android 13.

Saat menjalankan aplikasi sekarang, Anda akan melihat tema dinamis yang diterapkan menggunakan wallpaper Android 13 default.

Anda mungkin juga ingin status bar diberi gaya secara dinamis bergantung pada skema warna yang digunakan untuk tema aplikasi Anda.

1095e2b2c1ffdc14.png

Aplikasi tanpa penerapan warna status bar (Kiri).

Aplikasi dengan penerapan warna status bar (Kanan).

Untuk memperbarui warna status bar, bergantung pada warna utama tema Anda, tambahkan warna status bar setelah pilihan skema warna dalam composable AppTheme:

Theme.kt

@Composable
fun AppTheme(
   useDarkTheme: Boolean =  isSystemInDarkTheme(),
   content: @Composable () -> Unit
) {

 // color scheme selection code

 // Add primary status bar color from chosen color scheme.
 val view = LocalView.current
 if (!view.isInEditMode) {
    SideEffect {
        val window = (view.context as Activity).window
        window.statusBarColor = colors.primary.toArgb()
        WindowCompat
            .getInsetsController(window, view)
            .isAppearanceLightStatusBars = useDarkTheme
    }
 }

  MaterialTheme(
    colorScheme = colors,
     content = content
   )
}

Saat menjalankan aplikasi, Anda akan melihat status bar yang mengambil tema warna utama. Anda juga dapat mencoba tema dinamis terang dan gelap dengan mengubah tema gelap sistem.

69093b5bce31fd43.png

Tema terang (kiri) dan gelap (kanan) diterapkan dengan wallpaper default Android 13.

Sejauh ini, Anda telah menerapkan warna ke aplikasi yang telah meningkatkan tampilan aplikasi. Namun, Anda dapat melihat bahwa semua teks dalam aplikasi memiliki ukuran yang sama sehingga Anda kini dapat menambahkan tipografi ke aplikasi.

6. Tipografi

Desain Material 3 menentukan skala jenis. Penamaan dan pengelompokannya telah disederhanakan untuk: tampilan, judul, judul, isi, dan label, dengan setiap ukuran besar, sedang, dan kecil.

999a161dcd9b0ec4.png

Skala jenis Material 3.

Menentukan tipografi

Compose menyediakan class Typography M3—bersama dengan class TextStyle dan font-related yang ada—untuk memodelkan skala jenis Material 3.

Konstruktor Tipografi menawarkan setelan default untuk setiap gaya sehingga Anda dapat menghilangkan parameter apa pun yang tidak ingin disesuaikan. Untuk informasi selengkapnya, lihat gaya dan nilai default tipografi.

Anda akan menggunakan lima gaya tipografi di aplikasi Anda: headlineSmall, titleLarge, bodyLarge, bodyMedium, dan labelMedium. Gaya ini akan meliputi layar utama dan layar detail.

Layar yang menampilkan penggunaan tipografi gaya judul, label, dan isi.

Layar yang menampilkan penggunaan tipografi gaya judul, label, dan isi.

Selanjutnya, buka paket ui/theme dan buka Type.kt. Tambahkan kode berikut untuk memberikan implementasi Anda sendiri untuk beberapa gaya teks, bukan nilai default:

Type.kt

val typography = Typography(
   headlineSmall = TextStyle(
       fontWeight = FontWeight.SemiBold,
       fontSize = 24.sp,
       lineHeight = 32.sp,
       letterSpacing = 0.sp
   ),
   titleLarge = TextStyle(
       fontWeight = FontWeight.Normal,
       fontSize = 18.sp,
       lineHeight = 28.sp,
       letterSpacing = 0.sp
   ),
   bodyLarge = TextStyle(
       fontWeight = FontWeight.Normal,
       fontSize = 16.sp,
       lineHeight = 24.sp,
       letterSpacing = 0.15.sp
   ),
   bodyMedium = TextStyle(
       fontWeight = FontWeight.Medium,
       fontSize = 14.sp,
       lineHeight = 20.sp,
       letterSpacing = 0.25.sp
   ),
   labelMedium = TextStyle(
       fontWeight = FontWeight.SemiBold,
       fontSize = 12.sp,
       lineHeight = 16.sp,
       letterSpacing = 0.5.sp
   )
)

Tipografi Anda kini telah ditentukan. Untuk menambahkannya ke tema Anda, teruskan ke composable MaterialTheme() di dalam AppTheme:

Theme.kt

@Composable
fun AppTheme(
   useDarkTheme: Boolean = isSystemInDarkTheme(),
   content: @Composable() () -> Unit
) {
  // dynamic theming content

   MaterialTheme(
       colorScheme = colors,
       typography = typography,
       content = content
   )
}

Menangani tipografi

Sama seperti warna, Anda akan mengakses gaya tipografi untuk tema saat ini menggunakan MaterialTheme.typography. Tindakan ini memberi Anda instance tipografi untuk menggunakan semua tipografi yang ditentukan dalam Type.kt.

Text(
   text = "Hello M3 theming",
   style = MaterialTheme.typography.titleLarge
)

Text(
   text = "you are learning typography",
   style = MaterialTheme.typography.bodyMedium
)

Produk Anda mungkin tidak memerlukan 15 gaya default dari jenis huruf Desain Material. Dalam codelab ini, lima ukuran akan dipilih, sedangkan ukuran lainnya dihilangkan.

Karena Anda belum menerapkan tipografi ke composable Text(), semua teks akan kembali ke Typography.bodyLarge secara default.

Tipografi daftar layar utama

Selanjutnya, terapkan tipografi ke fungsi ReplyEmailListItem di ui/components/ReplyEmailListItem.kt untuk membuat perbedaan antara judul dan label:

ReplyEmailListItem.kt

Text(
   text = email.sender.firstName,
   style = MaterialTheme.typography.labelMedium
)

Text(
   text = email.createdAt,
   style = MaterialTheme.typography.labelMedium
)

Text(
   text = email.subject,
   style = MaterialTheme.typography.titleLarge,
   modifier = Modifier.padding(top = 12.dp, bottom = 8.dp),
)

Text(
   text = email.body,
   maxLines = 2,
   style = MaterialTheme.typography.bodyLarge,
   overflow = TextOverflow.Ellipsis
)

90645c0765167bb7.png 6c4af2f412c18bfb.png

Layar utama tanpa penerapan tipografi (Kiri).

Layar utama dengan penerapan tipografi (Kanan).

Tipografi daftar detail

Demikian pula, Anda akan menambahkan tipografi di layar detail dengan memperbarui semua composable teks ReplyEmailThreadItem di ui/components/ReplyEmailThreadItem.kt:

ReplyEmailThreadItem.kt

Text(
   text = email.sender.firstName,
   style = MaterialTheme.typography.labelMedium
)

Text(
   text = stringResource(id = R.string.twenty_mins_ago),
   style = MaterialTheme.typography.labelMedium
)

Text(
   text = email.subject,
   style = MaterialTheme.typography.bodyMedium,
   modifier = Modifier.padding(top = 12.dp, bottom = 8.dp),
)

Text(
   text = email.body,
   style = MaterialTheme.typography.bodyLarge,
   color = MaterialTheme.colorScheme.onSurfaceVariant
)

543ac09e43d8761.png 3412771e95a45f36.png

Layar detail tanpa penerapan tipografi (Kiri).

Layar detail dengan penerapan tipografi (Kanan).

Menyesuaikan tipografi

Dengan Compose, sangat mudah untuk menyesuaikan gaya teks Anda atau menyediakan font kustom Anda. Anda dapat mengubah TextStyle untuk menyesuaikan jenis font, jenis font, spasi huruf, dll.

Anda akan mengubah gaya teks di file theme/Type.kt, yang akan tercermin ke semua komponen yang menggunakannya.

Update fontWeight ke SemiBold dan lineHeight ke 32.sp untuk titleLarge, yang digunakan untuk subjek di item daftar. Hal ini akan lebih menekankan pada subjek dan memberikan pemisahan yang jelas.

Type.kt

...
titleLarge = TextStyle(
   fontWeight = FontWeight.SemiBold,
   fontSize = 18.sp,
   lineHeight = 32.sp,
   letterSpacing = 0.0.sp
),
...

f8d2212819eb0b61.png

Menerapkan tipografi kustom ke teks subjek.

7. Bentuk

Permukaan material dapat ditampilkan dalam berbagai bentuk. Bentuk mampu menarik perhatian, mengidentifikasi komponen, mengomunikasikan status, dan mengekspresikan merek.

Menentukan bentuk

Compose menyediakan class Shapes dengan parameter yang diperluas untuk menerapkan bentuk M3 baru. Skala bentuk M3, mirip dengan skala jenis, memungkinkan berbagai bentuk ekspresif di seluruh UI.

Ada berbagai ukuran bentuk dalam skala bentuk:

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

Secara default, setiap bentuk memiliki nilai default yang dapat diganti. Untuk aplikasi, Anda akan menggunakan bentuk media untuk memodifikasi item daftar, tetapi Anda juga dapat mendeklarasikan bentuk lainnya. Buat file baru bernama Shape.kt dalam paket ui/theme dan tambahkan kode untuk bentuk:

Shape.kt

package com.example.reply.ui.theme

import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Shapes
import androidx.compose.ui.unit.dp

val shapes = Shapes(
   extraSmall = RoundedCornerShape(4.dp),
   small = RoundedCornerShape(8.dp),
   medium = RoundedCornerShape(16.dp),
   large = RoundedCornerShape(24.dp),
   extraLarge = RoundedCornerShape(32.dp)
)

Setelah Anda menentukan shapes, teruskan ke MaterialTheme M3 seperti yang Anda lakukan untuk warna dan tipografi:

Theme.kt

@Composable
fun AppTheme(
   useDarkTheme: Boolean = isSystemInDarkTheme(),
   content: @Composable() () -> Unit
) {
  // dynamic theming content

   MaterialTheme(
       colorScheme = colors,
       typography = typography,
       shapes = shapes,
       content = content
   )
}

Menangani bentuk

Sama seperti warna dan tipografi, Anda dapat menerapkan bentuk ke komponen Material menggunakan MaterialTheme.shape, yang memberi Anda instance Shape untuk mengakses bentuk Material.

Banyak komponen Material telah menerapkan bentuk default, tetapi Anda dapat menyediakan dan menerapkan bentuk Anda sendiri ke komponen melalui slot yang tersedia.

Card(shape = MaterialTheme.shapes.medium) { /* card content */ }
FloatingActionButton(shape = MaterialTheme.shapes.large) { /* fab content */}

Nilai bentuk default untuk semua komponen Material 3.Pemetaan komponen Material menggunakan berbagai jenis bentuk.

Anda dapat melihat pemetaan bentuk untuk semua komponen dalam dokumentasi Bentuk.

Ada dua bentuk lain yang tersedia untuk digunakan—RectangleShape dan CircleShape—yang merupakan bagian dari Compose. Bentuk persegi panjang tidak memiliki radius batas, dan bentuk lingkaran menampilkan tepi yang dilingkari sepenuhnya.

Anda juga dapat menerapkan bentuk ke Komponen menggunakanModifiers yang berbentuk, seperti Modifier.clip, Modifier.background, danModifier.border Google.

Bentuk panel aplikasi

Kita ingin panel aplikasi memiliki latar belakang sudut bulat:

f873392abe535494.png

TopAppBar menggunakan Row dengan warna latar belakang. Untuk mencapai latar belakang sudut bulat, tentukan bentuk latar belakang dengan meneruskan CircleShape ke pengubah latar belakang:

ReplyAppBars.kt

@Composable
fun ReplySearchBar(modifier: Modifier = Modifier) {
   Row(
       modifier = modifier
           .fillMaxWidth()
           .padding(16.dp)
           .background(
               MaterialTheme.colorScheme.background,
               CircleShape
           ),
       verticalAlignment = Alignment.CenterVertically
   ) {
       // Search bar content
   }
}

f873392abe535494.png

Bentuk item daftar detail

Di layar utama, Anda menggunakan kartu yang menggunakan Shape.Medium secara default. Namun, untuk halaman detail kita, Anda menggunakan Kolom dengan warna latar belakang. Untuk tampilan daftar yang seragam, terapkan bentuk sedang.

3412771e95a45f36.png 80ee881c41a98c2a.png

Kolom item daftar detail tanpa bentuk pada item daftar (Kiri) dan bentuk media pada daftar (Kanan).

ReplyEmailThreadItem.kt

@Composable
fun ReplyEmailThreadItem(
   email: Email,
   modifier: Modifier = Modifier
) {
   Column(
       modifier = modifier
           .fillMaxWidth()
           .padding(8.dp)
           .background(
               MaterialTheme.colorScheme.background,
               MaterialTheme.shapes.medium
           )
           .padding(16.dp)

   ) {
      // List item content

   }
}

Sekarang, menjalankan aplikasi akan menampilkan item daftar layar mendetail yang dibentuk menjadi medium.

8. Penekanan

Penekanan di UI membantu Anda untuk menandai beberapa konten dari yang lain, seperti ketika Anda ingin membedakan judul dari subtitel. Penekanan di M3 menggunakan variasi warna dan kombinasi warnanya. Ada dua cara untuk menambahkan penekanan:

  1. Menggunakan warna permukaan, varian permukaan, dan latar belakang di samping on-surface dan varian on-surface dari sistem warna M3 yang diperluas.

Misalnya, permukaan dapat digunakan dengan varian on-surface, dan varian permukaan dapat digunakan dengan on-surface untuk memberikan tingkat penekanan yang berbeda.

Varian permukaan juga dapat digunakan dengan warna aksen untuk memberikan lebih sedikit penekanan dibandingkan warna yang tidak sengaja, tetapi masih dapat diakses dan mengikuti rasio kontras.

Peran warna Permukaan, Latar Belakang, dan varian Permukaan.

Peran warna permukaan, latar belakang, dan varian permukaan.

  1. Penggunaan ketebalan font yang berbeda untuk teks. Seperti yang terlihat di bagian tipografi, Anda dapat memberikan bobot khusus untuk skala huruf Anda guna memberikan penekanan yang berbeda.

Selanjutnya, update ReplyEmailListItem.kt untuk memberikan perbedaan penekanan menggunakan varian permukaan. Secara default, konten kartu menggunakan warna konten default bergantung pada latar belakang.

Anda akan memperbarui warna composable teks waktu dan teks isi menjadi onSurfaceVariant. Tindakan ini mengurangi penekanannya dibandingkan dengan onContainerColors, yang diterapkan secara default pada composable subjek dan teks judul.

2c9b7f2bd016edb8.png 6850ff391f21e4ba.png

Waktu dan teks isi dengan penekanan yang sama dibandingkan dengan subjek dan judul (Kiri).

Waktu dan isi dengan pengurangan penekanan dibandingkan dengan subjek dan judul (Kanan).

ReplyEmailListItem.kt

Text(
   text = email.createdAt,
   style = MaterialTheme.typography.labelMedium,
   color = MaterialTheme.colorScheme.onSurfaceVariant
)

Text(
   text = email.body,
   maxLines = 2,
   style = MaterialTheme.typography.bodyLarge,
   color = MaterialTheme.colorScheme.onSurfaceVariant,
   overflow = TextOverflow.Ellipsis
)

Untuk kartu email penting dengan latar belakang secondaryContainer, semua warna teks secara default adalah warna onSecondaryContainer. Untuk email lainnya, latar belakang adalah surfaceVariant, sehingga semua teks ditetapkan secara default ke warna onSurfaceVariant.

9. Selamat

Selamat! Anda berhasil menyelesaikan codelab ini. Anda telah menerapkan tema Material dengan Compose menggunakan warna, tipografi, dan bentuk serta warna dinamis untuk tema aplikasi Anda dan memberikan pengalaman yang dipersonalisasi.

2d8fcabf15ac5202.png 5a4d31db0185dca6.png ce009e4ce560834d.png

Akhir dari hasil penerapan tema dengan warna dan tema warna dinamis.

Langkah berikutnya

Lihat codelab kami yang lain di jalur Compose:

Bacaan lebih lanjut

Aplikasi contoh

Dokumen referensi