Meskipun Material adalah sistem desain yang kami rekomendasikan dan Jetpack Compose menghadirkan implementasi Material, Anda tidak diharuskan untuk menggunakannya. Material dibuat sepenuhnya di API publik, sehingga Anda dapat membuat sistem desain Anda sendiri dengan cara yang sama.
Ada beberapa pendekatan yang dapat Anda lakukan:
- Memperluas
MaterialTheme
dengan nilai tema tambahan - Mengganti satu atau beberapa sistem Material —
Colors
,Typography
, atauShapes
— dengan implementasi kustom, sambil mempertahankan yang lain - Mengimplementasikan sistem desain yang sepenuhnya kustom untuk
mengganti
MaterialTheme
Anda mungkin juga ingin terus menggunakan komponen Material dengan sistem desain kustom. Hal ini mungkin dilakukan, tetapi ada hal-hal yang perlu diingat agar sesuai dengan pendekatan yang Anda ambil.
Untuk mempelajari konstruksi dan API tingkat lebih rendah yang digunakan oleh MaterialTheme
dan sistem desain kustom, lihat panduan Anatomi tema di Compose.
Memperluas Tema Material
Compose Material dengan cermat mengikuti Tema Material untuk membuatnya mudah dan aman untuk mengikuti panduan Material. Namun, Anda dapat memperluas kumpulan warna, tipografi, dan bentuk dengan nilai tambahan.
Pendekatan yang paling sederhana adalah menambahkan properti ekstensi:
// Use with MaterialTheme.colorScheme.snackbarAction val ColorScheme.snackbarAction: Color @Composable get() = if (isSystemInDarkTheme()) Red300 else Red700 // Use with MaterialTheme.typography.textFieldInput val Typography.textFieldInput: TextStyle get() = TextStyle(/* ... */) // Use with MaterialTheme.shapes.card val Shapes.card: Shape get() = RoundedCornerShape(size = 20.dp)
Hal ini memberikan konsistensi dengan API penggunaan MaterialTheme
. Contohnya,
yang ditetapkan oleh Compose itu sendiri adalah
surfaceColorAtElevation
,
yang menentukan warna platform yang harus digunakan, bergantung pada elevasi.
Pendekatan lainnya adalah menentukan tema tambahan yang "menggabungkan" MaterialTheme
dan
nilainya.
Misalnya Anda ingin menambahkan dua warna tambahan — caution
dan onCaution
, warna kuning yang digunakan untuk tindakan yang semi-berbahaya — dengan tetap mempertahankan
warna Material yang ada:
@Immutable data class ExtendedColors( val caution: Color, val onCaution: Color ) val LocalExtendedColors = staticCompositionLocalOf { ExtendedColors( caution = Color.Unspecified, onCaution = Color.Unspecified ) } @Composable fun ExtendedTheme( /* ... */ content: @Composable () -> Unit ) { val extendedColors = ExtendedColors( caution = Color(0xFFFFCC02), onCaution = Color(0xFF2C2D30) ) CompositionLocalProvider(LocalExtendedColors provides extendedColors) { MaterialTheme( /* colors = ..., typography = ..., shapes = ... */ content = content ) } } // Use with eg. ExtendedTheme.colors.caution object ExtendedTheme { val colors: ExtendedColors @Composable get() = LocalExtendedColors.current }
Ini mirip dengan API penggunaan MaterialTheme
. Ini juga mendukung beberapa tema
karena Anda dapat menyarangkan ExtendedTheme
dengan cara yang sama seperti MaterialTheme
.
Menggunakan komponen Material
Saat memperluas Tema Material, nilai MaterialTheme
yang ada akan dipertahankan
dan komponen Material masih memiliki default yang wajar.
Jika Anda ingin menggunakan nilai yang diperluas dalam komponen, gabungkan ke dalam fungsi composable Anda sendiri, langsung tetapkan nilai yang ingin Anda ubah, dan tampilkan yang lain sebagai parameter ke composable yang berisi:
@Composable fun ExtendedButton( onClick: () -> Unit, modifier: Modifier = Modifier, content: @Composable RowScope.() -> Unit ) { Button( colors = ButtonDefaults.buttonColors( containerColor = ExtendedTheme.colors.caution, contentColor = ExtendedTheme.colors.onCaution /* Other colors use values from MaterialTheme */ ), onClick = onClick, modifier = modifier, content = content ) }
Kemudian, Anda akan mengganti penggunaan Button
dengan ExtendedButton
jika
sesuai.
@Composable fun ExtendedApp() { ExtendedTheme { /*...*/ ExtendedButton(onClick = { /* ... */ }) { /* ... */ } } }
Mengganti subsistem Material
Daripada memperluas Tema Material, Anda dapat mengganti satu atau beberapa
sistem — Colors
, Typography
, atau Shapes
— dengan implementasi kustom,
sambil mempertahankan sistem lainnya.
Misalkan Anda ingin mengganti jenis dan sistem bentuk sambil mempertahankan sistem warna:
@Immutable data class ReplacementTypography( val body: TextStyle, val title: TextStyle ) @Immutable data class ReplacementShapes( val component: Shape, val surface: Shape ) val LocalReplacementTypography = staticCompositionLocalOf { ReplacementTypography( body = TextStyle.Default, title = TextStyle.Default ) } val LocalReplacementShapes = staticCompositionLocalOf { ReplacementShapes( component = RoundedCornerShape(ZeroCornerSize), surface = RoundedCornerShape(ZeroCornerSize) ) } @Composable fun ReplacementTheme( /* ... */ content: @Composable () -> Unit ) { val replacementTypography = ReplacementTypography( body = TextStyle(fontSize = 16.sp), title = TextStyle(fontSize = 32.sp) ) val replacementShapes = ReplacementShapes( component = RoundedCornerShape(percent = 50), surface = RoundedCornerShape(size = 40.dp) ) CompositionLocalProvider( LocalReplacementTypography provides replacementTypography, LocalReplacementShapes provides replacementShapes ) { MaterialTheme( /* colors = ... */ content = content ) } } // Use with eg. ReplacementTheme.typography.body object ReplacementTheme { val typography: ReplacementTypography @Composable get() = LocalReplacementTypography.current val shapes: ReplacementShapes @Composable get() = LocalReplacementShapes.current }
Menggunakan komponen Material
Saat satu atau beberapa sistem MaterialTheme
telah diganti, penggunaan komponen Material sebagaimana adanya dapat mengakibatkan warna Material, jenis, atau nilai bentuk yang tidak diinginkan.
Jika ingin menggunakan nilai pengganti dalam komponen, gabungkan ke dalam fungsi composable, langsung tetapkan nilai untuk sistem yang relevan, dan tampilkan nilai lain sebagai parameter ke composable tersebut.
@Composable fun ReplacementButton( onClick: () -> Unit, modifier: Modifier = Modifier, content: @Composable RowScope.() -> Unit ) { Button( shape = ReplacementTheme.shapes.component, onClick = onClick, modifier = modifier, content = { ProvideTextStyle( value = ReplacementTheme.typography.body ) { content() } } ) }
Kemudian, Anda akan mengganti penggunaan Button
dengan ReplacementButton
jika
sesuai.
@Composable fun ReplacementApp() { ReplacementTheme { /*...*/ ReplacementButton(onClick = { /* ... */ }) { /* ... */ } } }
Menerapkan sistem desain yang sepenuhnya kustom
Anda dapat mengganti Tema Material dengan sistem desain kustom sepenuhnya.
Perhatikan bahwa MaterialTheme
menyediakan sistem berikut:
Colors
,Typography
, danShapes
: Sistem Tema MaterialTextSelectionColors
: Warna yang digunakan untuk pemilihan teks olehText
danTextField
Ripple
danRippleTheme
: Implementasi material dariIndication
Jika ingin terus menggunakan komponen Material, Anda harus mengganti beberapa sistem ini di tema atau tema kustom, atau menangani sistem di komponen Anda untuk menghindari perilaku yang tidak diinginkan.
Namun, sistem desain tidak terbatas pada konsep yang diandalkan Material. Anda dapat mengubah sistem yang ada dan memperkenalkan sistem yang benar-benar baru — dengan class dan jenis baru — untuk membuat konsep lain kompatibel dengan tema.
Dalam kode berikut, kami membuat model sistem warna kustom yang mencakup gradien
(List<Color>
), menyertakan sistem jenis, memperkenalkan sistem ketinggian baru,
dan mengecualikan sistem lain yang disediakan oleh MaterialTheme
:
@Immutable data class CustomColors( val content: Color, val component: Color, val background: List<Color> ) @Immutable data class CustomTypography( val body: TextStyle, val title: TextStyle ) @Immutable data class CustomElevation( val default: Dp, val pressed: Dp ) val LocalCustomColors = staticCompositionLocalOf { CustomColors( content = Color.Unspecified, component = Color.Unspecified, background = emptyList() ) } val LocalCustomTypography = staticCompositionLocalOf { CustomTypography( body = TextStyle.Default, title = TextStyle.Default ) } val LocalCustomElevation = staticCompositionLocalOf { CustomElevation( default = Dp.Unspecified, pressed = Dp.Unspecified ) } @Composable fun CustomTheme( /* ... */ content: @Composable () -> Unit ) { val customColors = CustomColors( content = Color(0xFFDD0D3C), component = Color(0xFFC20029), background = listOf(Color.White, Color(0xFFF8BBD0)) ) val customTypography = CustomTypography( body = TextStyle(fontSize = 16.sp), title = TextStyle(fontSize = 32.sp) ) val customElevation = CustomElevation( default = 4.dp, pressed = 8.dp ) CompositionLocalProvider( LocalCustomColors provides customColors, LocalCustomTypography provides customTypography, LocalCustomElevation provides customElevation, content = content ) } // Use with eg. CustomTheme.elevation.small object CustomTheme { val colors: CustomColors @Composable get() = LocalCustomColors.current val typography: CustomTypography @Composable get() = LocalCustomTypography.current val elevation: CustomElevation @Composable get() = LocalCustomElevation.current }
Menggunakan komponen Material
Jika tidak ada MaterialTheme
, penggunaan komponen Material apa adanya akan menghasilkan
warn, jenis, nilai bentuk, serta perilaku indikasi Material yang tidak diinginkan.
Jika ingin menggunakan nilai kustom dalam komponen, gabungkan ke dalam fungsi composable, langsung tetapkan nilai untuk sistem yang relevan, dan ekspos nilai lain sebagai parameter ke composable tersebut.
Sebaiknya akses nilai yang Anda tetapkan dari tema kustom Anda.
Atau, jika tema Anda tidak menyediakan Color
, TextStyle
, Shape
, atau
sistem lain, Anda dapat meng-hardcode-nya.
@Composable fun CustomButton( onClick: () -> Unit, modifier: Modifier = Modifier, content: @Composable RowScope.() -> Unit ) { Button( colors = ButtonDefaults.buttonColors( containerColor = CustomTheme.colors.component, contentColor = CustomTheme.colors.content, disabledContainerColor = CustomTheme.colors.content .copy(alpha = 0.12f) .compositeOver(CustomTheme.colors.component), disabledContentColor = CustomTheme.colors.content .copy(alpha = 0.38f) ), shape = ButtonShape, elevation = ButtonDefaults.elevatedButtonElevation( defaultElevation = CustomTheme.elevation.default, pressedElevation = CustomTheme.elevation.pressed /* disabledElevation = 0.dp */ ), onClick = onClick, modifier = modifier, content = { ProvideTextStyle( value = CustomTheme.typography.body ) { content() } } ) } val ButtonShape = RoundedCornerShape(percent = 50)
Jika Anda telah memperkenalkan jenis class baru — seperti List<Color>
untuk mewakili
gradien — mungkin lebih baik menerapkan komponen dari awal daripada
menggabungkannya. Misalnya, lihat
JetsnackButton
dari contoh Jetsnack.
Direkomendasikan untuk Anda
- Catatan: teks link ditampilkan saat JavaScript nonaktif
- Desain Material 3 di Compose
- Bermigrasi dari Material 2 ke Material 3 di Compose
- Anatomi tema di Compose