Es gibt mehrere Möglichkeiten, Ihre Apps mit Designs zu erstellen. Die Wahl hängt davon ab, wie Ihre App im Hinblick auf die Einführung von Material Design positioniert ist:
- Vollständig benutzerdefiniertes Designsystem ohne Material Design
- Empfehlung: Definieren Sie Komponentenstile, die Werte aus dem Design verwenden, und stellen Sie Stilparameter für Komponenten des Designsystems bereit.
- Material Design verwenden
- Empfehlung: Warten Sie auf die Einführung von Material, um es in Designs zu integrieren. Verwenden Sie nach Möglichkeit Designs für Ihre eigenen Komponenten.
Die Designebene
Im herkömmlichen Compose-Modell basiert die Anpassung oft stark auf dem Überschreiben globaler Tokens (Farben und Typografie), die von MaterialTheme bereitgestellt werden, oder auf dem Umschließen und Überschreiben von Eigenschaften einer zusammensetzbaren Komponente des Designsystems, sofern möglich.
Manchmal gibt es Eigenschaften in der Material-Ebene, die nicht über die Subsysteme oder Parameter verfügbar gemacht werden, sondern als hartcodierte Standardwerte in der Komponente selbst enthalten sind.
Mit der Styles API gibt es eine neue Abstraktionsebene, die eine Brücke zwischen Subsystemen und Komponenten bildet: Designs.
| Layer | Verantwortung | Beispiel |
|---|---|---|
| Werte für das Subsystem | Benannte Werte | val Primary = Color(0xFF34A85E) |
| Atomare Designs | Design, das genau eine Eigenschaft ändert | val buttonStyle = paddingAtomic then roundedCornerShapeAtomic then primaryBackgroundAtomic then largeSize then interactiveShadowAtomic |
| Komponentendesigns | Komponentenspezifische Konfigurationen | Eine Schaltfläche mit primärem Hintergrund und 16 dp-Abstand. val buttonStyle = Style { contentPadding(16.dp) shape(RoundedCornerShape(8.dp)) background(Color.Blue) } |
| Komponenten | Das funktionale UI-Element, das ein Design verwendet. | Button(style = buttonStyle) { ... } |
Atomare Designs im Vergleich zu monolithischen Designs
Mit der Styles API können Sie ein Design in separate atomare Designs aufteilen.
Anstatt komplexe, komponentenspezifische Designs wie baseButtonStyle zu definieren, können Sie auch kleine, zweckgebundene Hilfsdesigns erstellen. Diese fungieren als Ihre „Atome“.
// Define single-purpose "atomic" styles val paddingAtomic = Style { contentPadding(16.dp) } val roundedCornerShapeAtomic = Style { shape(RoundedCornerShape(8.dp)) } val primaryBackgroundAtomic = Style { background(Color.Blue) } val largeSizeAtomic = Style { size(100.dp, 40.dp) } val interactiveShadowAtomic = Style { hovered { animate { dropShadow( Shadow( offset = DpOffset( 0.dp, 0.dp ), radius = 2.dp, spread = 0.dp, color = Color.Blue, ) ) } } }
Zusammensetzung mit „then“
Eine der leistungsstarken Funktionen der neuen Styles API ist der Operator then, mit dem Sie mehrere Style-Objekte zusammenführen können. So können Sie eine Komponente mit atomaren Hilfsklassen erstellen.
Traditionell (nicht atomar):
// One large monolithic style val buttonStyle = Style { contentPadding(16.dp) shape(RoundedCornerShape(8.dp)) background(Color.Blue) }
Atomare Umgestaltung:
// Combine atoms to create the final appearance val buttonStyle = paddingAtomic then roundedCornerShapeAtomic then primaryBackgroundAtomic then interactiveShadowAtomic
Designs in Ihrem Designsystem übernehmen
Je nachdem, wo sich Ihr Designsystem im Spektrum befindet, haben Sie folgende Möglichkeiten, Designs in Ihr Designsystem zu übernehmen.
Benutzerdefiniertes Designsystem mit Designs
In folgenden Fällen zu empfehlen: Sie haben einen umfassenden Markenleitfaden erhalten, der nicht auf Material Design basiert, und Sie planen nicht, Material Design zu verwenden.
Strategie: Implementieren Sie ein vollständig benutzerdefiniertes Designsystem und stellen Sie Designs als Teil des Designs bereit.
Diese Option ist der benutzerdefinierte Pfad, wenn Sie Material nicht als Hauptsprache für Ihr Designsystem verwenden. Sie umgehen MaterialTheme vollständig für visuelle Definitionen und
haben bereits ein eigenes benutzerdefiniertes Design erstellt. Sie erstellen ein CompanyTheme, das als Container für Ihre Designs dient.
- Funktionsweise: Erstellen Sie ein
CompanyTheme-Objekt, dasStyle-Objekte für jede Komponente in Ihrem System enthält. Ihre Komponenten (entweder Wrapper um die Material-Logik oder benutzerdefinierteBox- oderLayout-Implementierungen) verwenden diese Stile direkt und stellen einenStyle-Parameter für Nutzer Ihres Designsystems bereit. - Die Designebene: Designs sind die primäre Definition Ihres Design systems. Tokens sind benannte Variablen, die in diese Designs eingefügt werden. Dies ermöglicht eine umfassende Anpassung, z. B. das Definieren eindeutiger Animationen für Statusänderungen (z. B. Animieren von Skalierung und Farbe beim Drücken).
Wenn Sie ein eigenes benutzerdefiniertes Design ohne Material erstellen und Designs übernehmen möchten, fügen Sie Ihrem Design eine Liste von Designs hinzu. So können Sie von überall in Ihrem Projekt auf Ihre Basisdesigns zugreifen.
Erstellen Sie eine
Styles-Klasse, in der die verschiedenen Designs in Ihrer Anwendung gespeichert werden, und erstellen Sie die Standardwerte. In der Jetsnack-App heißt die Klasse beispielsweiseJetsnackStyles:object JetsnackStyles{ val buttonStyle: Style = Style { shape(shapes.medium) background(colors.brand) contentColor(colors.textPrimary) contentPaddingVertical(8.dp) contentPaddingHorizontal(24.dp) textStyle(typography.labelLarge) disabled { animate { background(colors.brandSecondary) } } } val cardStyle: Style = Style { shape(shapes.medium) background(colors.uiBackground) contentColor(colors.textPrimary) } }
Stellen Sie
Stylesals Teil Ihres Gesamtdesigns bereit und stellen Sie Hilfs-Erweiterungsfunktionen fürStyleScopebereit, um auf die Subsysteme zuzugreifen:@Immutable class JetsnackTheme( val colors: JetsnackColors = LightJetsnackColors, val typography: androidx.compose.material3.Typography = androidx.compose.material3.Typography(), val shapes: Shapes = Shapes() ) { companion object { val colors: JetsnackColors @Composable @ReadOnlyComposable get() = LocalJetsnackTheme.current.colors val typography: androidx.compose.material3.Typography @Composable @ReadOnlyComposable get() = LocalJetsnackTheme.current.typography val shapes: Shapes @Composable @ReadOnlyComposable get() = LocalJetsnackTheme.current.shapes val styles: JetsnackStyles = JetsnackStyles val LocalJetsnackTheme: ProvidableCompositionLocal<JetsnackTheme> get() = LocalJetsnackThemeInstance } } val StyleScope.colors: JetsnackColors get() = LocalJetsnackTheme.currentValue.colors val StyleScope.typography: androidx.compose.material3.Typography get() = LocalJetsnackTheme.currentValue.typography val StyleScope.shapes: Shapes get() = LocalJetsnackTheme.currentValue.shapes internal val LocalJetsnackThemeInstance = staticCompositionLocalOf { JetsnackTheme() } @Composable fun JetsnackTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) { val colors = if (darkTheme) DarkJetsnackColors else LightJetsnackColors val theme = JetsnackTheme(colors = colors) CompositionLocalProvider( LocalJetsnackTheme provides theme, ) { MaterialTheme( typography = LocalJetsnackTheme.current.typography, shapes = LocalJetsnackTheme.current.shapes, content = content, ) } }
Greifen Sie in Ihrer zusammensetzbaren Komponente auf
JetsnackStyleszu:@Composable fun CustomButton(modifier: Modifier, style: Style = Style, text: String) { val interactionSource = remember { MutableInteractionSource() } val styleState = remember(interactionSource) { MutableStyleState(interactionSource) } // Apply style to top level container in combination with incoming style from parameter. Box(modifier = modifier .clickable( interactionSource = interactionSource, indication = null, enabled = true, role = Role.Button, onClick = { }, ) .styleable(styleState, JetsnackTheme.styles.buttonStyle, style)) { Text(text) } }
Neben der globalen Designübernahme gibt es alternative Strategien, um Styles in Ihre Apps einzubinden. Sie können Styles inline für bestimmte Aufrufstellen verwenden oder statische Definitionen verwenden, wenn vollständige Designfunktionen nicht erforderlich sind.
Styles sollten nur bedingt ausgetauscht werden, wenn sich das gesamte Design grundlegend unterscheidet. Sie sollten es vorziehen, auf dynamische Tokens innerhalb einer visuellen Definition zuzugreifen, anstatt zwischen verschiedenen Designobjekten zu wechseln.