Material ist unser empfohlenes Designsystem. Jetpack Compose enthält eine Implementierung von Material, aber Sie müssen es nicht verwenden. Material basiert vollständig auf öffentlichen APIs. Sie können also auf dieselbe Weise Ihr eigenes Designsystem erstellen.
Es gibt verschiedene Ansätze:
- Erweitern Sie
MaterialThememit zusätzlichen Design werten. - Ein oder mehrere Material-Systeme ersetzen —
Colors,TypographyoderShapes— durch benutzerdefinierte Implementierungen ersetben und die anderen beibehalten. - Ein vollständig benutzerdefiniertes Designsystem implementieren, um
MaterialThemezu ersetzen
Möglicherweise möchten Sie auch Material-Komponenten mit einem benutzerdefinierten Designsystem verwenden. Das ist möglich, aber Sie müssen einige Dinge beachten, um den gewählten Ansatz zu berücksichtigen.
Weitere Informationen zu den Konstrukten und APIs der niedrigeren Ebene, die von MaterialTheme
und benutzerdefinierten Designsystemen verwendet werden, finden Sie im Leitfaden Aufbau eines Designs in Compose.
Material Theming erweitern
Compose Material ist eng an Material Theming angelehnt, damit die Richtlinien für Material einfach und typsicher eingehalten werden können. Sie können die Farb-, Typografie- und Formensets jedoch mit zusätzlichen Werten erweitern. Der einfachste Ansatz 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)
Dadurch wird die Konsistenz mit den APIs für die Verwendung von MaterialTheme gewährleistet. Ein Beispiel dafür, das von Compose selbst definiert wird, ist
surfaceColorAtElevation.
Damit wird die Oberflächenfarbe festgelegt, die je nach
Höhe verwendet werden soll.
Ein anderer Ansatz 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. Das ist eine gelbe Farbe, die für Aktionen verwendet wird, die halb gefährlich sind. Die vorhandenen Material-Farben sollen 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 ähnelt den APIs für die Verwendung von MaterialTheme. Es werden auch mehrere Designs unterstützt, da Sie ExtendedThemes auf dieselbe Weise wie MaterialTheme verschachteln können.
Material-Komponenten verwenden
Wenn Sie Material Theming erweitern, bleiben die vorhandenen MaterialTheme-Werte erhalten und Material-Komponenten haben weiterhin sinnvolle Standardwerte.
Wenn Sie erweiterte Werte in Komponenten verwenden möchten, umschließen Sie sie in Ihren eigenen zusammensetzbaren Funktionen. Legen Sie die Werte, die Sie ändern möchten, direkt fest und machen Sie andere als Parameter für die enthaltende zusammensetzbare Funktion verfügbar:
@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 ) }
Anschließend ersetzen Sie die Verwendungen von Button gegebenenfalls durch ExtendedButton.
@Composable fun ExtendedApp() { ExtendedTheme { /*...*/ ExtendedButton(onClick = { /* ... */ }) { /* ... */ } } }
Material-Subsysteme ersetzen
Anstatt Material Theming zu erweitern, können Sie ein oder mehrere Systeme (Colors, Typography oder Shapes) durch eine benutzerdefinierte Implementierung ersetzen und die anderen beibehalten.
Angenommen, Sie möchten die Typografie- und Formensysteme ersetzen, aber das Farbsystem 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 }
Material-Komponenten verwenden
Wenn ein oder mehrere Systeme von MaterialTheme ersetzt wurden, kann die unveränderte Verwendung von Material-Komponenten zu unerwünschten Material-Farb-, Typografie- oder Formwerten führen.
Wenn Sie Ersatzwerte in Komponenten verwenden möchten, umschließen Sie sie in Ihren eigenen zusammensetzbaren Funktionen. Legen Sie die Werte für das relevante System direkt fest und machen Sie andere als Parameter für die enthaltende zusammensetzbare Funktion verfügbar.
@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() } } ) }
Anschließend ersetzen Sie die Verwendungen von Button gegebenenfalls durch ReplacementButton wo
angebracht.
@Composable fun ReplacementApp() { ReplacementTheme { /*...*/ ReplacementButton(onClick = { /* ... */ }) { /* ... */ } } }
Vollständig benutzerdefiniertes Designsystem implementieren
Möglicherweise möchten Sie Material Theming durch ein vollständig benutzerdefiniertes Designsystem ersetzen.
Beachten Sie, dass MaterialTheme die folgenden Systeme bietet:
Colors,TypographyundShapes: Material Theming-SystemeTextSelectionColors: Farben, die für die Textauswahl durchTextundTextFieldverwendet werdenRippleundRippleTheme: Material-Implementierung vonIndication
Wenn Sie Material-Komponenten weiterhin verwenden möchten, müssen Sie einige dieser Systeme in Ihren benutzerdefinierten Designs ersetzen oder die Systeme in Ihren Komponenten verarbeiten, um unerwünschtes Verhalten zu vermeiden.
Designsysteme sind jedoch nicht auf die Konzepte beschränkt, auf die sich Material stützt. Sie können vorhandene Systeme ändern und völlig neue einführen – mit neuen Klassen und Typen –, um andere Konzepte mit Designs kompatibel zu machen.
Im folgenden Code modellieren wir ein benutzerdefiniertes Farbsystem, das Farbverläufe
(List<Color>) enthält, ein Typografiesystem einführt, ein neues Höhensystem einführt,
und andere Systeme ausschließt, die von MaterialTheme bereitgestellt werden:
@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 unveränderte Verwendung von Material-Komponenten zu unerwünschten Material-Farb-, Typografie- und Formwerten sowie zu unerwünschtem Indikationsverhalten.
Wenn Sie benutzerdefinierte Werte in Komponenten verwenden möchten, umschließen Sie sie in Ihren eigenen zusammensetzbaren Funktionen. Legen Sie die Werte für das relevante System direkt fest und machen Sie andere als Parameter für die enthaltende zusammensetzbare Funktion verfügbar.
Wir empfehlen, auf die von Ihnen festgelegten Werte aus Ihrem benutzerdefinierten Design zuzugreifen.
Alternativ können Sie Color, TextStyle, Shape oder andere Systeme fest codieren, wenn Ihr Design sie nicht bereitstellt.
@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)
Wenn Sie neue Klassentypen eingeführt haben, z. B. List<Color> für Farbverläufe, ist es möglicherweise besser, Komponenten von Grund auf neu zu implementieren, anstatt sie zu umschließen. Ein Beispiel finden Sie unter
JetsnackButton
im Jetsnack-Beispiel.
Empfehlungen für Sie
- Hinweis: Linktext wird angezeigt, wenn JavaScript deaktiviert ist
- Material Design 3 in Compose
- Von Material 2 zu Material 3 in Compose migrieren
- Aufbau eines Designs in Compose