Tema Jetpack Compose

1. Pengantar

Dalam codelab ini, Anda akan mempelajari cara menggunakan API tema Jetpack Compose untuk memberi gaya pada aplikasi Anda. Kita akan melihat cara menyesuaikan warna, bentuk, dan tipografi agar digunakan secara konsisten di seluruh aplikasi Anda, yang mendukung beberapa tema seperti tema terang & gelap.

Yang akan Anda pelajari

Dalam codelab ini, Anda akan mempelajari:

  • Pedoman dasar Desain Material dan cara menyesuaikannya untuk merek Anda
  • Cara Compose menerapkan sistem Desain Material
  • Cara menentukan dan menggunakan warna, tipografi, dan bentuk di seluruh aplikasi
  • Cara mengatur gaya komponen
  • Cara mendukung tema terang dan gelap

Yang akan Anda build

Dalam codelab ini, kita akan mengatur gaya aplikasi pembacaan berita. Kita memulai dengan aplikasi tanpa gaya dan akan menerapkan apa yang kita pelajari untuk memberi tema aplikasi dan mendukung tema gelap.

Gambar yang menampilkan Jetnews, aplikasi untuk membaca berita, sebelum menerapkan gaya.

Gambar yang menampilkan Jetnews, aplikasi untuk membaca berita, setelah menerapkan gaya.

Gambar yang menampilkan Jetnews, aplikasi untuk membaca berita, yang diberi gaya dengan tema gelap.

Sebelum: aplikasi tanpa gaya

Sesudah: aplikasi yang diberi gaya

Sesudah: tema gelap

Prasyarat

2. Mempersiapkan

Pada langkah ini, Anda akan mendownload kode yang terdiri dari aplikasi pembaca berita sederhana yang akan kita beri gaya.

Yang akan Anda butuhkan

Mendownload kode

Jika sudah menginstal git, Anda cukup menjalankan perintah di bawah. Untuk memeriksa apakah git sudah diinstal, ketik git --version di terminal atau command line dan pastikan git dijalankan dengan benar.

git clone https://github.com/googlecodelabs/android-compose-codelabs.git
cd android-compose-codelabs/ThemingCodelabM2

Jika tidak memiliki git, Anda dapat mengklik tombol berikut untuk mendownload semua kode untuk codelab ini:

Buka project di Android Studio, lalu pilih 'File > Import Project' dan cari direktori ThemingCodelabM2.

Project ini berisi tiga paket utama:

  • com.codelab.theming.data Ini berisi class model dan data contoh. Anda tidak perlu mengedit paket ini selama codelab ini.
  • com.codelab.theming.ui.start Ini adalah titik awal codelab, Anda harus membuat semua perubahan yang diminta dalam codelab ini dalam paket ini.
  • com.codelab.theming.ui.finish Ini adalah status akhir codelab, untuk referensi Anda.

Membuat dan menjalankan aplikasi

Aplikasi ini memiliki 2 konfigurasi run yang mencerminkan status awal dan akhir codelab. Memilih salah satu konfigurasi dan menekan tombol run akan men-deploy kode ke perangkat atau emulator Anda.

a43ae3c4fa75836e.png

Aplikasi juga berisi Pratinjau tata letak Compose. Penjelajahan ke Home.kt dalam paket start/finish dan membuka tampilan desain menunjukkan sejumlah pratinjau yang memungkinkan iterasi cepat pada kode UI Anda:

758a285ad8a6cd51.png

3. Penerapan Tema Material

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 terdiri dari atribut warna, tipografi, dan bentuk. Penyesuain atribut tersebut akan secara otomatis tercermin dalam komponen yang Anda gunakan untuk membuat aplikasi.

Pemahaman tentang Penerapan Tema Material berguna untuk memahami cara memberi tema aplikasi Jetpack Compose, jadi berikut adalah deskripsi singkat konsepnya. Jika sudah memahami Penerapan Tema Material, Anda dapat melewatinya.

Warna

Desain Material menentukan sejumlah warna yang dinamai secara semantik yang dapat Anda gunakan di seluruh aplikasi:

62ccfe5761fd9eda.png

Warna primer adalah warna merek utama Anda dan warna sekunder digunakan untuk memberikan aksen. Anda dapat menyediakan varian yang lebih gelap/lebih terang untuk area yang kontras. Warna latar belakang dan permukaan digunakan untuk penampung yang menyimpan komponen yang pada prinsipnya berada di "permukaan" dalam aplikasi Anda. Material juga mendefinisikan warna "aktif"—warna yang akan digunakan untuk konten selain salah satu warna yang diberi nama, misalnya teks, dalam penampung berwarna 'permukaan' harus diberi warna 'di permukaan'. Komponen material dikonfigurasi untuk menggunakan warna tema ini, misalnya secara default Tombol Tindakan Mengambang (FAB) diberi warna secondary, Kartu ditetapkan secara default ke surface, dll.

Dengan menentukan warna yang diberi nama, Anda dapat menyediakan palet warna alternatif seperti tema terang dan gelap:

1a9b78141ddfa87b.png

Fitur ini juga mendorong Anda untuk menentukan palet warna kecil dan menggunakannya secara konsisten di seluruh aplikasi. Alat warna Material dapat membantu memilih warna dan membuat palet warna, bahkan memastikan bahwa kombinasi bisa dilakukan.

Tipografi

Demikian pula, Material menentukan sejumlah gaya tulisan yang dinamai secara semantik:

1d44de3ff2f7fd1c.png

Meskipun Anda tidak dapat memvariasikan gaya tulisan berdasarkan tema, penggunaan skala tulisan akan mendorong konsistensi dalam aplikasi Anda. Menyediakan font Anda sendiri dan penyesuaian jenis lainnya akan tercermin dalam komponen Material yang Anda gunakan dalam aplikasi, mis. Panel Aplikasi menggunakan gaya h6 secara default, Tombol menggunakan gaya, err, button. Alat generator skala tulisan Material dapat membantu Anda membuat skala tulisan.

Bentuk

Material mendukung penggunaan bentuk secara sistematis untuk menyampaikan merek Anda. Terdapat 3 kategori: komponen kecil, sedang, dan besar; yang masing-masing dapat menentukan bentuk yang akan digunakan, menyesuaikan gaya sudut (terpotong atau membulat) dan ukuran.

886b811cc9cad18e.png

Penyesuaian tema bentuk akan tercermin di berbagai komponen, mis. Tombol & Kolom Teks menggunakan tema bentuk kecil, Kartu dan Dialog menggunakan tema bentuk medium, dan Spreadsheet menggunakan tema bentuk besar secara default. Pemetaan lengkap komponen untuk tema bentuk tersedia di sini. Alat penyesuaian bentuk Material dapat membantu Anda membuat tema bentuk.

Tema dasar bawaan

Material ditetapkan secara default ke tema "dasar bawaan", yaitu skema warna ungu, skala tulisan Roboto, dan bentuk yang sedikit membulat yang terlihat pada gambar di atas. Jika Anda tidak menentukan atau menyesuaikan tema, komponen akan menggunakan tema dasar bawaan.

4. Menentukan tema Anda

MaterialTheme

Elemen inti untuk menerapkan tema ke Jetpack Compose adalah composable MaterialTheme. Menempatkan composable ini dalam hierarki compose memungkinkan Anda menentukan penyesuaian warna, jenis, dan bentuk untuk semua komponen di dalamnya. Berikut adalah cara menentukan composable ini di library:

@Composable
fun MaterialTheme(
    colors: Colors,
    typography: Typography,
    shapes: Shapes,
    content: @Composable () -> Unit
) { ...

Kemudian, Anda dapat mengambil parameter yang diteruskan ke composable ini menggunakan MaterialTheme object, yang mengekspos properti colors, typography, dan shapes. Kita akan membahas masing-masing detail tersebut lebih jauh nanti.

Buka Home.kt dan cari fungsi composable Home — ini adalah titik entri utama untuk aplikasi. Perhatikan bahwa saat mendeklarasikan MaterialTheme, kita tidak menentukan parameter apa pun sehingga menerima gaya "dasar bawaan" default:

@Composable
fun Home() {
  ...
  MaterialTheme {
    Scaffold(...

Mari kita buat parameter warna, jenis, dan bentuk untuk menerapkan tema aplikasi.

Membuat Tema

Untuk memusatkan gaya Anda, sebaiknya buat composable Anda sendiri yang menggabungkan dan mengonfigurasi MaterialTheme. Ini memberi Anda satu tempat untuk menentukan penyesuaian tema dan memungkinkan Anda menggunakannya kembali dengan mudah di banyak tempat, misalnya di beberapa layar atau @Preview. Anda dapat membuat beberapa composable tema jika diperlukan, misalnya jika ingin mendukung gaya yang lain untuk bagian yang berbeda dari aplikasi Anda.

Dalam paket com.codelab.theming.ui.start.theme, buat file Kotlin baru bernama Theme.kt. Tambahkan fungsi composable baru yang disebut JetnewsTheme, yang menerima composable lain sebagai konten dan menggabungkan MaterialTheme:

@Composable
fun JetnewsTheme(content: @Composable () -> Unit) {
  MaterialTheme(content = content)
}

Sekarang beralih kembali ke Home.kt dan ganti MaterialTheme dengan JetnewsTheme kita (dan impor):

-  MaterialTheme {
+  JetnewsTheme {
    ...

Anda belum akan melihat perubahan apa pun di @Preview pada layar ini. Perbarui PostItemPreview dan FeaturedPostPreview untuk menggabungkan kontennya dengan composable JetnewsTheme baru sehingga pratinjau menggunakan tema baru:

@Preview("Featured Post")
@Composable
private fun FeaturedPostPreview() {
  val post = remember { PostRepo.getFeaturedPost() }
+ JetnewsTheme {
    FeaturedPost(post = post)
+ }
}

Warna

Berikut palet warna yang ingin kita terapkan di aplikasi (hanya palet terang untuk saat ini, kita akan kembali untuk mendukung tema gelap segera):

b2635ed3ec4bfc8f.png

Warna di Compose ditentukan menggunakan class Color. Ada beberapa konstruktor yang memungkinkan Anda menentukan warna sebagai ULong atau dengan saluran warna terpisah.

Buat file baru Color.kt di paket theme Anda. Tambahkan warna berikut sebagai properti publik tingkat atas dalam file ini:

val Red700 = Color(0xffdd0d3c)
val Red800 = Color(0xffd00036)
val Red900 = Color(0xffc20029)

Setelah menentukan warna aplikasi, mari gabungkan warna tersebut ke dalam objek Colors yang diperlukan MaterialTheme, yang menetapkan warna tertentu ke warna Material yang dinamai. Beralih kembali ke Theme.kt dan tambahkan berikut ini:

private val LightColors = lightColors(
    primary = Red700,
    primaryVariant = Red900,
    onPrimary = Color.White,
    secondary = Red700,
    secondaryVariant = Red900,
    onSecondary = Color.White,
    error = Red800
)

Di sini kita menggunakan fungsi lightColors untuk membuat Colors, hal ini memberikan default yang masuk akal sehingga kita tidak perlu menentukan semua warna yang membentuk palet warna Material. Misalnya, perhatikan bahwa kita belum menentukan warna background atau banyak warna 'aktif', kita akan menggunakan warna default.

Sekarang, mari kita gunakan warna ini dalam aplikasi. Perbarui composable JetnewsTheme untuk menggunakan Colors baru kita:

@Composable
fun JetnewsTheme(content: @Composable () -> Unit) {
  MaterialTheme(
+   colors = LightColors,
    content = content
  )
}

Buka Home.kt dan muat ulang pratinjau. Perhatikan skema warna baru yang ditampilkan dalam komponen seperti TopAppBar.

Tipografi

Berikut adalah skala tulisan yang ingin kita implementasikan di aplikasi:

54c420f78529b77d.png

Di Compose, kita dapat menentukan objek TextStyle untuk menentukan informasi yang diperlukan untuk memberi gaya beberapa teks. Contoh atributnya:

data class TextStyle(
    val color: Color = Color.Unset,
    val fontSize: TextUnit = TextUnit.Inherit,
    val fontWeight: FontWeight? = null,
    val fontStyle: FontStyle? = null,
    val fontFamily: FontFamily? = null,
    val letterSpacing: TextUnit = TextUnit.Inherit,
    val background: Color = Color.Unset,
    val textAlign: TextAlign? = null,
    val textDirection: TextDirection? = null,
    val lineHeight: TextUnit = TextUnit.Inherit,
    ...
)

Skala tulisan yang kita inginkan menggunakan Montserrat untuk judul dan Domine untuk teks isi. File font yang relevan sudah ditambahkan ke folder res/fonts project Anda.

Buat file baru Typography.kt dalam paket theme. Mari kita tentukan terlebih dahulu FontFamily (yang menggabungkan bobot yang berbeda dari setiap Font ):

private val Montserrat = FontFamily(
    Font(R.font.montserrat_regular),
    Font(R.font.montserrat_medium, FontWeight.W500),
    Font(R.font.montserrat_semibold, FontWeight.W600)
)

private val Domine = FontFamily(
    Font(R.font.domine_regular),
    Font(R.font.domine_bold, FontWeight.Bold)
)

Sekarang, buat objek Typography yang diterima MaterialTheme, dengan menentukan TextStyle untuk setiap gaya semantik dalam skala:

val JetnewsTypography = Typography(
    h4 = TextStyle(
        fontFamily = Montserrat,
        fontWeight = FontWeight.W600,
        fontSize = 30.sp
    ),
    h5 = TextStyle(
        fontFamily = Montserrat,
        fontWeight = FontWeight.W600,
        fontSize = 24.sp
    ),
    h6 = TextStyle(
        fontFamily = Montserrat,
        fontWeight = FontWeight.W600,
        fontSize = 20.sp
    ),
    subtitle1 = TextStyle(
        fontFamily = Montserrat,
        fontWeight = FontWeight.W600,
        fontSize = 16.sp
    ),
    subtitle2 = TextStyle(
        fontFamily = Montserrat,
        fontWeight = FontWeight.W500,
        fontSize = 14.sp
    ),
    body1 = TextStyle(
        fontFamily = Domine,
        fontWeight = FontWeight.Normal,
        fontSize = 16.sp
    ),
    body2 = TextStyle(
        fontFamily = Montserrat,
        fontSize = 14.sp
    ),
    button = TextStyle(
        fontFamily = Montserrat,
        fontWeight = FontWeight.W500,
        fontSize = 14.sp
    ),
    caption = TextStyle(
        fontFamily = Montserrat,
        fontWeight = FontWeight.Normal,
        fontSize = 12.sp
    ),
    overline = TextStyle(
        fontFamily = Montserrat,
        fontWeight = FontWeight.W500,
        fontSize = 12.sp
    )
)

Buka Theme.kt dan perbarui composable JetnewsTheme untuk menggunakan Typography baru kita:

@Composable
fun JetnewsTheme(content: @Composable () -> Unit) {
  MaterialTheme(
    colors = LightColors,
+   typography = JetnewsTypography,
    content = content
  )
}

Buka Home.kt dan muat ulang pratinjau untuk melihat penerapan tipografi baru.

Bentuk

Kita ingin menggunakan bentuk untuk mengekspresikan merek di aplikasi. Kita ingin menggunakan bentuk sudut terpotong pada sejumlah elemen:

9b60c78a78c61570.png

Compose menawarkan class RoundedCornerShape dan CutCornerShape yang dapat Anda gunakan untuk menentukan tema bentuk.

Buat file baru Shape.kt di paket theme dan tambahkan hal berikut:

val JetnewsShapes = Shapes(
    small = CutCornerShape(topStart = 8.dp),
    medium = CutCornerShape(topStart = 24.dp),
    large = RoundedCornerShape(8.dp)
)

Buka Theme.kt dan perbarui composable JetnewsTheme untuk menggunakan Shapes berikut:

@Composable
fun JetnewsTheme(content: @Composable () -> Unit) {
  MaterialTheme(
    colors = LightColors,
    typography = JetnewsTypography,
+   shapes = JetnewsShapes,
    content = content
  )
}

Buka Home.kt dan muat ulang pratinjau untuk melihat cara Card yang menampilkan postingan unggulan mencerminkan tema bentuk yang baru diterapkan.

Tema Gelap

Mendukung tema gelap di aplikasi Anda tidak hanya membantu aplikasi terintegrasi lebih baik di perangkat pengguna (yang memiliki tombol tema gelap global mulai dari Android 10 dan seterusnya), tetapi juga dapat mengurangi penggunaan daya dan mendukung kebutuhan aksesibilitas. Material menawarkan panduan desain tentang cara membuat tema gelap. Berikut palet warna alternatif yang ingin kita terapkan untuk tema gelap:

21768b33f0ccda5f.png

Buka Color.kt dan tambahkan warna berikut:

val Red200 = Color(0xfff297a2)
val Red300 = Color(0xffea6d7e)

Sekarang buka Theme.kt dan tambahkan:

private val DarkColors = darkColors(
    primary = Red300,
    primaryVariant = Red700,
    onPrimary = Color.Black,
    secondary = Red300,
    onSecondary = Color.Black,
    error = Red200
)

Sekarang perbarui JetnewsTheme:

@Composable
fun JetnewsTheme(
+ darkTheme: Boolean = isSystemInDarkTheme(),
  content: @Composable () -> Unit
) {
  MaterialTheme(
+   colors = if (darkTheme) DarkColors else LightColors,
    typography = JetnewsTypography,
    shapes = JetnewsShapes,
    content = content
  )
}

Di sini, kita telah menambahkan parameter baru untuk menentukan apakah akan menggunakan tema gelap atau tidak, dan menyetelnya secara default guna mengkueri perangkat untuk setelan global. Hal ini memberi kita default yang baik, tetapi masih mudah untuk diganti jika ingin layar tertentu selalu/tidak pernah gelap atau untuk membuat @Preview bertema gelap.

Buka Home.kt dan buat pratinjau baru untuk composable FeaturedPost yang menampilkannya dalam tema gelap:

@Preview("Featured Post • Dark")
@Composable
private fun FeaturedPostDarkPreview() {
    val post = remember { PostRepo.getFeaturedPost() }
    JetnewsTheme(darkTheme = true) {
        FeaturedPost(post = post)
    }
}

Muat ulang panel pratinjau untuk melihat pratinjau tema gelap.

84f93b209ce4fd46.png

5. Menangani Warna

Di langkah terakhir, kita telah melihat cara membuat tema Anda sendiri untuk menetapkan warna, gaya tulisan, dan bentuk untuk aplikasi Anda. Semua komponen Material menggunakan penyesuaian ini secara langsung. Misalnya, composable FloatingActionButton secara default menggunakan warna secondary dari tema, tetapi Anda dapat menetapkan warna alternatif dengan menentukan nilai yang berbeda untuk parameter ini:

@Composable
fun FloatingActionButton(
  backgroundColor: Color = MaterialTheme.colors.secondary,
  ...
) {

Anda tidak selalu ingin menggunakan setelan default, bagian ini menunjukkan cara menggunakan warna di aplikasi Anda.

Warna Mentah

Seperti yang kita lihat sebelumnya, compose menawarkan class Color. Anda dapat membuat ini secara lokal, menyimpannya di object, dll:

Surface(color = Color.LightGray) {
  Text(
    text = "Hard coded colors don't respond to theme changes :(",
    textColor = Color(0xffff00ff)
  )
}

Color memiliki sejumlah metode yang berguna, seperti copy, yang memungkinkan Anda membuat warna baru dengan nilai alfa/merah/hijau/biru yang berbeda.

Warna Tema

Pendekatan yang lebih fleksibel adalah dengan mengambil warna dari tema Anda:

Surface(color = MaterialTheme.colors.primary)

Di sini, kita menggunakan MaterialTheme object yang properti colors-nya menampilkan Colors yang ditetapkan dalam composable MaterialTheme. Itu berarti kita dapat mendukung tampilan dan nuansa yang berbeda hanya dengan menyediakan kumpulan warna yang berbeda ke tema kita, dan tidak perlu menyentuh kode aplikasi. Misalnya, AppBar menggunakan warna primary dan latar belakang layar adalah surface; mengubah warna tema akan tercermin dalam composable tersebut:

b0b0ca02b52453a7.png

253ab041d7ea904e.png

Karena setiap warna dalam tema kita adalah instance Color, kita juga dapat dengan mudah memperoleh warna menggunakan metode copy:

val derivedColor = MaterialTheme.colors.onSurface.copy(alpha = 0.1f)

Di sini, kita membuat salinan warna onSurface tetapi dengan opasitas 10%. Pendekatan ini memastikan bahwa warna berfungsi di bagian tema yang berbeda, bukan warna statis hard code.

Warna Permukaan & Konten

Banyak komponen menerima sepasang warna dan "warna konten":

Surface(
  color: Color = MaterialTheme.colors.surface,
  contentColor: Color = contentColorFor(color),
  ...

TopAppBar(
  backgroundColor: Color = MaterialTheme.colors.primarySurface,
  contentColor: Color = contentColorFor(backgroundColor),
  ...

Hal ini memungkinkan Anda untuk tidak hanya menetapkan warna composable, tetapi juga memberikan warna default untuk "konten" yaitu composable di dalamnya. Banyak composable menggunakan warna konten ini secara default, misalnya warna Text atau tint Icon. Metode contentColorFor mengambil warna "aktif" yang sesuai untuk setiap warna tema, mis. jika Anda menetapkan latar belakang primary, metode ini akan menampilkan onPrimary sebagai warna konten. Jika Anda menetapkan warna latar belakang non-tema, Anda harus menyediakan warna konten yang masuk akal.

Surface(color = MaterialTheme.colors.primary) {
  Text(...) // default text color is 'onPrimary'
}
Surface(color = MaterialTheme.colors.error) {
  Icon(...) // default tint is 'onError'
}

Anda dapat menggunakan LocalContentColor CompositionLocal untuk mengambil warna yang kontras dengan latar belakang yang sedang digunakan:

BottomNavigationItem(
  unselectedContentColor = LocalContentColor.current ...

Saat menetapkan warna elemen apa pun, sebaiknya gunakan Surface untuk melakukan hal ini karena menetapkan warna konten yang sesuai untuk nilai CompositionLocal, hati-hati terhadap panggilan Modifier.background langsung yang tidak menetapkan warna konten yang sesuai.

-Row(Modifier.background(MaterialTheme.colors.primary)) {
+Surface(color = MaterialTheme.colors.primary) {
+  Row(
...

Saat ini, komponen Header kita selalu memiliki latar belakang Color.LightGray. Ini terlihat bagus dalam tema terang, tetapi akan memiliki kontras tinggi dengan latar belakang dalam tema gelap. Keduanya juga tidak menentukan warna teks tertentu, jadi warisi warna konten saat ini yang mungkin tidak kontras dengan latar belakangnya:

7329ac6ead5097eb.png

Mari kita perbaiki. Pada composable Header di Home.kt, hapus pengubah background yang menentukan warna hard code. Sebagai gantinya, gabungkan Text dalam Surface dengan warna yang berasal dari tema dan tentukan bahwa konten tersebut harus diberi warna primary:

+ Surface(
+   color = MaterialTheme.colors.onSurface.copy(alpha = 0.1f),
+   contentColor = MaterialTheme.colors.primary,
+   modifier = modifier
+ ) {
  Text(
    text = text,
    modifier = Modifier
      .fillMaxWidth()
-     .background(Color.LightGray)
      .padding(horizontal = 16.dp, vertical = 8.dp)
  )
+ }

Alfa Konten

Sering kali kita ingin menekankan atau tidak menekankan konten untuk menyampaikan nilai penting dan memberikan hierarki visual. Desain Material menyarankan penggunaan berbagai level opasitas untuk menyatakan level nilai penting yang berbeda tersebut.

Jetpack Compose menerapkan ini melalui LocalContentAlpha. Anda dapat menentukan alfa konten untuk hierarki dengan memberikan nilai untuk CompositionLocal ini. Komponen turunan dapat menggunakan nilai ini, misalnya Text dan Icon secara default menggunakan kombinasi LocalContentColor yang disesuaikan untuk menggunakan LocalContentAlpha. Material menentukan beberapa nilai alfa standar ( high, medium, disabled) yang dimodelkan berdasarkan objek ContentAlpha. Perhatikan bahwa MaterialTheme menetapkan LocalContentAlpha secara default ke ContentAlpha.high.

// By default, both Icon & Text use the combination of LocalContentColor &
// LocalContentAlpha. De-emphasize content by setting a different content alpha
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
    Text(...)
}
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.disabled) {
    Icon(...)
    Text(...)
}

Hal ini memudahkan dan membuatnya konsisten menyampaikan nilai penting komponen.

Kita akan menggunakan alfa konten untuk memperjelas hierarki informasi dari postingan unggulan. Di Home.kt, di composable PostMetadata, buat penekanan medium metadata:

+ CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
  Text(
    text = text,
    modifier = modifier
  )
+ }

103ff62c71744935.png

Tema Gelap

Seperti yang telah kita lihat, untuk mengimplementasikan tema gelap di Compose, Anda hanya perlu memberikan kumpulan warna dan warna kueri yang berbeda-beda melalui tema. Beberapa pengecualian catatan adalah:

Anda dapat memeriksa apakah Anda menjalankan tema terang:

val isLightTheme = MaterialTheme.colors.isLight

Nilai ini ditetapkan oleh fungsi builder lightColors/ darkColors.

Pada material, dalam tema gelap, permukaan dengan elevasi yang lebih tinggi menerima overlay elevasi (latar belakangnya akan lebih terang). Hal ini diterapkan secara otomatis saat menggunakan palet warna gelap:

Surface(
  elevation = 2.dp,
  color = MaterialTheme.colors.surface, // color will be adjusted for elevation
  ...

Kita dapat melihat perilaku otomatis ini di aplikasi pada komponen TopAppBar dan Card yang digunakan; komponen tersebut memiliki elevasi 4dp dan 1dp secara default, sehingga latar belakangnya otomatis akan lebih terang di tema gelap untuk menampilkan elevasi ini dengan lebih baik:

cb8c617b8c151820.png

Desain material menyarankan untuk menghindari area luas dengan warna cerah pada tema gelap. Pola yang umum adalah memberi warna primary pada penampung dalam tema terang dan warna surface pada tema gelap; banyak komponen menggunakan strategi ini secara default, misalnya Panel Aplikasi dan Navigasi Bawah. Untuk mempermudah implementasi, Colors menawarkan warna primarySurface yang memberikan perilaku ini dengan tepat dan komponen ini digunakan secara default.

Aplikasi kita saat ini menetapkan Panel Aplikasi ke warna primary, kita dapat mengikuti panduan ini dengan mengalihkannya ke primarySurface atau cukup menghapusnya karena parameter ini adalah default. Di composable AppBar, ubah parameter backgroundColor TopAppBar:

@Composable
private fun AppBar() {
  TopAppBar(
    ...
-   backgroundColor = MaterialTheme.colors.primary
+   backgroundColor = MaterialTheme.colors.primarySurface
  )
}

6. Menangani Teks

Saat menggunakan teks, kita menggunakan composable Text untuk menampilkan teks, TextField dan OutlinedTextField untuk input teks, dan TextStyle untuk menerapkan satu gaya pada teks. Kita dapat menggunakan AnnotatedString untuk menerapkan beberapa gaya ke teks.

Seperti yang kita lihat dengan warna, komponen Material yang menampilkan teks akan mengambil penyesuaian tipografi tema kita:

Button(...) {
  Text("This text will use MaterialTheme.typography.button style by default")
}

Mencapai hal ini sedikit lebih rumit daripada menggunakan parameter default seperti yang kita lihat dengan warna. Hal ini karena komponen cenderung tidak menampilkan teks itu sendiri, melainkan menawarkan 'slot API' yang memungkinkan Anda meneruskan composable Text. Jadi, bagaimana komponen menetapkan gaya tipografi tema? Di balik layar, komponen menggunakan composable ProvideTextStyle (yang menggunakan CompositionLocal) untuk menyetel TextStyle "saat ini". Composable Text akan disetel secara default untuk melakukan kueri gaya "saat ini" jika Anda tidak memberikan parameter textStyle konkret.

Misalnya dari class Button & Text Compose:

@Composable
fun Button(
    // many other parameters
    content: @Composable RowScope.() -> Unit
) {
  ...
  ProvideTextStyle(MaterialTheme.typography.button) { //set the "current" text style
    ...
    content()
  }
}

@Composable
fun Text(
    // many, many parameters
    style: TextStyle = LocalTextStyle.current // get the value set by ProvideTextStyle
) { ...

Gaya teks tema

Sama seperti warna, sebaiknya ambil TextStyle dari tema saat ini, yang mendorong Anda untuk menggunakan kumpulan gaya yang kecil dan konsisten serta membuatnya lebih mudah dikelola. MaterialTheme.typography mengambil kumpulan instance Typography di composable MaterialTheme, sehingga Anda dapat menggunakan gaya yang ditentukan:

Text(
  style = MaterialTheme.typography.subtitle2
)

Jika perlu menyesuaikan TextStyle, Anda dapat melakukan copy dan mengganti properti (hanya data class) atau composable Text menerima sejumlah parameter gaya yang akan ditempatkan di atas TextStyle:

Text(
  text = "Hello World",
  style = MaterialTheme.typography.body1.copy(
    background = MaterialTheme.colors.secondary
  )
)
Text(
  text = "Hello World",
  style = MaterialTheme.typography.subtitle2,
  fontSize = 22.sp // explicit size overrides the size in the style
)

Banyak tempat di aplikasi kita yang otomatis menerapkan tema TextStyle, misalnya TopAppBar menata gaya title-nya karena h6 dan ListItem menata gaya teks utama dan sekunder masing-masing ke subtitle1 dan body2.

Mari terapkan gaya tipografi tema ke seluruh aplikasi kita. Setel Header agar menggunakan subtitle2 dan teks di FeaturedPost agar menggunakan h6 sebagai judul dan body2 untuk penulis dan metadata:

@Composable
fun Header(...) {
  ...
  Text(
    text = text,
+   style = MaterialTheme.typography.subtitle2

45dbf11d6c1013a0.png

Beberapa gaya

Jika perlu menerapkan beberapa gaya ke beberapa teks, Anda dapat menggunakan class AnnotatedString untuk menerapkan markup, dengan menambahkan SpanStyle ke berbagai teks. Anda dapat menambahkannya secara dinamis atau menggunakan sintaksis DSL untuk membuat konten:

val text = buildAnnotatedString {
  append("This is some unstyled text\n")
  withStyle(SpanStyle(color = Color.Red)) {
    append("Red text\n")
  }
  withStyle(SpanStyle(fontSize = 24.sp)) {
    append("Large text")
  }
}

Mari kita tentukan gaya tag yang menjelaskan setiap postingan di aplikasi. Saat ini, metadata tersebut menggunakan gaya teks yang sama dengan metadata lainnya; kita akan menggunakan gaya teks overline dan warna latar belakang untuk membedakannya. Dalam composable PostMetadata:

+ val tagStyle = MaterialTheme.typography.overline.toSpanStyle().copy(
+   background = MaterialTheme.colors.primary.copy(alpha = 0.1f)
+ )
post.tags.forEachIndexed { index, tag ->
  ...
+ withStyle(tagStyle) {
    append(" ${tag.toUpperCase()} ")
+ }
}

3f504aaa0a94599a.png

7. Menangani Bentuk

Sama seperti warna dan tipografi, penyetelan tema bentuk Anda akan tercermin dalam komponen Material, misalnya Button akan mengambil kumpulan bentuk untuk komponen kecil:

@Composable
fun Button( ...
  shape: Shape = MaterialTheme.shapes.small
) {

Seperti warna, komponen Material menggunakan parameter default sehingga mudah untuk memeriksa kategori bentuk yang akan digunakan oleh komponen, atau untuk memberikan alternatif. Untuk pemetaan lengkap komponen guna membentuk kategori, lihat dokumentasi.

Perhatikan bahwa beberapa komponen menggunakan bentuk tema yang telah dimodifikasi agar sesuai dengan konteksnya. Misalnya, secara default TextField menggunakan tema bentuk kecil, tetapi menerapkan ukuran sudut nol ke sudut bawah:

@Composable
fun FilledTextField(
  // other parameters
  shape: Shape = MaterialTheme.shapes.small.copy(
    bottomStart = ZeroCornerSize, // overrides small theme style
    bottomEnd = ZeroCornerSize // overrides small theme style
  )
) {

1f5fa6cf1355e7a6.png

Bentuk tema

Anda tentu saja dapat menggunakan bentuk saat membuat komponen sendiri dengan menggunakan composable atau Modifier yang menerima bentuk, misalnya Surface, Modifier.clip, Modifier.background, Modifier.border, dll.

@Composable
fun UserProfile(
  ...
  shape: Shape = MaterialTheme.shapes.medium
) {
  Surface(shape = shape) {
    ...
  }
}

Mari kita tambahkan tema bentuk ke gambar yang ditampilkan di PostItem; kita akan menerapkan bentuk small tema ke gambar tersebut dengan clip Modifier untuk memotong sudut kiri atas:

@Composable
fun PostItem(...) {
  ...
  Image(
    painter = painterResource(post.imageThumbId),
+   modifier = Modifier.clip(shape = MaterialTheme.shapes.small)
  )

2f989c7c1b8d9e63.png

8. "Gaya" Komponen

Compose tidak menawarkan cara yang eksplisit untuk mengekstrak gaya komponen seperti gaya Android View atau gaya css. Karena semua komponen Compose ditulis di Kotlin, ada cara lain untuk mencapai sasaran yang sama. Sebagai gantinya, buat library komponen kustom sendiri dan gunakan ini di seluruh aplikasi Anda.

Kita sudah melakukan ini di aplikasi:

@Composable
fun Header(
  text: String,
  modifier: Modifier = Modifier
) {
  Surface(
    color = MaterialTheme.colors.onSurface.copy(alpha = 0.1f),
    contentColor = MaterialTheme.colors.primary,
    modifier = modifier.semantics { heading() }
  ) {
    Text(
      text = text,
      style = MaterialTheme.typography.subtitle2,
      modifier = Modifier
        .fillMaxWidth()
        .padding(horizontal = 16.dp, vertical = 8.dp)
    )
  }
}

Pada dasarnya, composable Header adalah Text yang diberi gaya dan dapat kita gunakan di seluruh aplikasi.

Kita telah melihat bahwa semua komponen dibuat dari elemen penyusun yang lebih rendah. Anda dapat menggunakan elemen penyusun yang sama ini untuk menyesuaikan komponen material. Misalnya, kita melihat bahwa Button menggunakan composable ProvideTextStyle untuk menetapkan gaya teks default untuk konten yang diteruskan ke sana. Anda dapat menggunakan mekanisme yang sama persis untuk menetapkan gaya teks Anda sendiri:

@Composable
fun LoginButton(
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
    content: @Composable RowScope.() -> Unit
) {
    Button(
        colors = ButtonConstants.defaultButtonColors(
            backgroundColor = MaterialTheme.colors.secondary
        ),
        onClick = onClick,
        modifier = modifier
    ) {
        ProvideTextStyle(...) { // set our own text style
            content()
        }
    }
}

Dalam contoh ini, kita membuat "gaya" LoginButton sendiri dengan menggabungkan class Button standar dan menentukan properti tertentu seperti backgroundColor dan gaya teks yang berbeda.

Selain itu, tidak ada konsep penataan gaya default, yaitu cara untuk menyesuaikan tampilan default jenis komponen. Sekali lagi, Anda bisa melakukannya dengan membuat komponen sendiri yang menggabungkan dan menyesuaikan komponen library. Misalnya, Anda ingin menyesuaikan bentuk semua Button di seluruh aplikasi, tetapi tidak ingin mengubah tema bentuk kecil, yang akan memengaruhi komponen (non-Button) lainnya. Untuk mencapai hal ini, buat komponen Anda sendiri dan gunakan ini pada:

@Composable
fun AcmeButton(
  // expose Button params consumers should be able to change
) {
  val acmeButtonShape: Shape = ...
  Button(
    shape = acmeButtonShape,
    // other params
  )
}

9. Selamat

Selamat, Anda berhasil menyelesaikan codelab ini dan mendesain aplikasi Jetpack Compose.

Anda telah menerapkan tema Material, menyesuaikan warna, tipografi, dan bentuk yang digunakan di seluruh aplikasi untuk mengekspresikan merek Anda dan mendorong konsistensi. Anda telah menambahkan dukungan untuk tema terang dan gelap.

Apa selanjutnya?

Lihat codelab lain di jalur Compose:

Bacaan lebih lanjut

Aplikasi contoh

  • Owl mendemonstrasikan beberapa tema
  • Jetcaster mendemonstrasikan tema dinamis
  • Jetsnack mendemonstrasikan penerapan sistem desain kustom

Dokumen referensi