Während Material als Designsystem empfohlen wird, liefert Jetpack Compose eine von Material implementiert ist, sind Sie nicht gezwungen, es zu nutzen. Material Design basiert vollständig auf öffentlichen APIs. Daher ist es möglich, auf dieselbe Weise ein eigenes Designsystem zu erstellen.
Dafür gibt es mehrere Ansätze:
MaterialTheme
mit zusätzlichen Themen erweitern Werte- Ein oder mehrere Materialsysteme –
Colors
,Typography
oderShapes
– durch benutzerdefinierte Implementierungen ersetzen, während die anderen beibehalten werden - Ein vollständig angepasstes Designsystem implementieren,
MaterialTheme
ersetzen
Sie können auch weiterhin Material-Komponenten mit einem benutzerdefinierten Designsystem verwenden. Das ist möglich, aber es gibt einige Dinge zu beachten, Ihren Ansatz.
Weitere Informationen zu den Konstrukten und APIs der unteren Ebene, die von MaterialTheme
und benutzerdefinierten Designsystemen verwendet werden, finden Sie im Leitfaden Anatomy of a theme in Compose (Anatomie eines Themas in Compose).
Material-Design erweitern
Stark modelliertes Material zusammenstellen Material-Design damit es einfach und typsicher ist. Es ist jedoch Farben, Typografie und Form mit weiteren Werte.
Die einfachste Methode besteht darin, Erweiterungseigenschaften hinzuzufügen:
// 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)
Dies sorgt für Konsistenz mit den MaterialTheme
-Nutzungs-APIs. Ein Beispiel:
Compose-Datei definiert,
surfaceColorAtElevation
,
wodurch die zu verwendende Oberflächenfarbe in Abhängigkeit von der Höhe bestimmt wird.
Eine weitere Möglichkeit besteht darin, ein erweitertes Design zu definieren, das MaterialTheme
und seine Werte umschließt.
Angenommen, Sie möchten zwei zusätzliche Farben hinzufügen: caution
und onCaution
, eine gelbe Farbe für Aktionen mit mittlerem Gefahrenpotenzial. Die vorhandenen Materialfarben sollen dabei beibehalten werden:
@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 }
Das ist mit den APIs zur MaterialTheme
-Nutzung vergleichbar. Es werden auch mehrere Designs unterstützt.
da du ExtendedTheme
s auf dieselbe Weise verschachteln kannst wie MaterialTheme
.
Material-Komponenten verwenden
Wenn Sie Material-Design-Themen erweitern, bleiben vorhandene MaterialTheme
-Werte erhalten und Material-Komponenten haben weiterhin angemessene Standardwerte.
Wenn Sie erweiterte Werte in Komponenten verwenden möchten, schließen Sie sie in Ihre eigenen ein. zusammensetzbaren Funktionen verwenden, die zu ändernden Werte direkt festlegen und andere als Parameter an die enthaltene zusammensetzbare Funktion übergeben:
@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 ) }
Ersetzen Sie dann gegebenenfalls die Verwendung von Button
durch ExtendedButton
.
@Composable fun ExtendedApp() { ExtendedTheme { /*...*/ ExtendedButton(onClick = { /* ... */ }) { /* ... */ } } }
Material-Subsysteme ersetzen
Anstatt Material Theming zu erweitern, können Sie ein oder mehrere
Colors
, Typography
oder Shapes
mit einer benutzerdefinierten Implementierung,
und die anderen beibehalten.
Angenommen, Sie möchten das Schriftbild- und das Formensystem ersetzen, das Farbsystem jedoch beibehalten:
@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 }
Materialkomponenten verwenden
Wenn eines oder mehrere Systeme von MaterialTheme
mit Material ersetzt wurden
Komponenten in unveränderter Form zu unerwünschten Farb-, Typ- oder Formwerten für Material führen.
Wenn Sie Ersatzwerte in Komponenten verwenden möchten, schließen Sie sie in Ihre eigenen ein. zusammensetzbaren Funktionen verwenden, die Werte direkt für das jeweilige System festlegen und andere als Parameter an die enthaltene zusammensetzbare Funktion übergeben.
@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() } } ) }
Ersetzen Sie dann gegebenenfalls die Verwendung von Button
durch ReplacementButton
.
@Composable fun ReplacementApp() { ReplacementTheme { /*...*/ ReplacementButton(onClick = { /* ... */ }) { /* ... */ } } }
Vollständig benutzerdefiniertes Designsystem implementieren
Sie können Material Theming durch ein vollständig benutzerdefiniertes Designsystem ersetzen.
Angenommen, MaterialTheme
stellt die folgenden Systeme bereit:
Colors
,Typography
undShapes
: Material Theming-SystemeTextSelectionColors
: Farben für die Textauswahl vonText
undTextField
Ripple
undRippleTheme
: Materialisierte Implementierung vonIndication
Wenn Sie weiterhin Material-Komponenten verwenden möchten, benutzerdefinierten Designs erstellen oder die Systeme in Ihrem um unerwünschtes Verhalten zu vermeiden.
Designsysteme sind jedoch nicht auf die Konzepte beschränkt, auf denen das Material basiert. Ich bestehende Systeme modifizieren und ganz neue einführen – mit neuen Klassen und Typen auswählen, um andere Konzepte mit Themen kompatibel zu machen.
Im folgenden Code wird ein benutzerdefiniertes Farbsystem mit Farbverläufen modelliert
(List<Color>
), enthalten ein Schriftsystem, führen ein neues Höhensystem ein,
und andere von MaterialTheme
bereitgestellte Systeme ausschließen:
@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 }
Material-Komponenten verwenden
Wenn kein MaterialTheme
vorhanden ist, führt die Verwendung der unveränderten Material-Komponenten zur Folge.
in unerwünschtem Material-Farb-, Schrift- und Formwerte sowie in Anzeigeverhalten angezeigt.
Wenn Sie benutzerdefinierte Werte in Komponenten verwenden möchten, fassen Sie sie in Ihre eigene zusammensetzbare Funktion zusammen. die Werte für das relevante System direkt festlegen und andere Parameter als Parameter für die zusammensetzbare Funktion verwenden, in der sie enthalten ist.
Wir empfehlen, auf Werte zuzugreifen, die Sie über Ihr benutzerdefiniertes Design festgelegt haben.
Wenn Ihr Design keine Color
-, TextStyle
-, Shape
- oder andere Systeme bietet, können Sie sie auch hartcodieren.
@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)
Sie haben neue Klassentypen eingeführt, z. B. List<Color>
zur Darstellung
Gradienten: Dann ist es möglicherweise besser, Komponenten von Grund auf neu zu implementieren,
sie zu verpacken. Sehen Sie sich zum Beispiel
JetsnackButton
aus dem Jetsnack-Beispiel.
Empfehlungen für dich
- Hinweis: Der Linktext wird angezeigt, wenn JavaScript deaktiviert ist.
- Material Design 3 in Compose
- In Compose von Material 2 zu Material 3 migrieren
- Aufbau eines Themas in Compose