Tipps für Stile

Auf dieser Seite werden Best Practices für die Arbeit mit Stilen beschrieben, die für Konsistenz in Ihrer Codebasis sorgen. Außerdem werden die Prinzipien erläutert, die wir beim Entwerfen der APIs befolgt haben.

Richtig

Wir empfehlen folgende Best Practices:

Do: Use Styles for visuals and modifiers for behaviors (Do: Use Styles for visuals and modifiers for behaviors)

Verwenden Sie die Styles API für die visuelle Konfiguration (Hintergründe, Abstand, Rahmen) und reservieren Sie Modifikatoren für Verhaltensweisen wie Klicklogik, Gestenerkennung oder Barrierefreiheit.

Do: Expose Style parameters in design systems

Für Ihre eigenen benutzerdefinierten Designsystemkomponenten sollten Sie nach dem Modifier-Parameter ein Style-Objekt bereitstellen.

@Composable
fun GradientButton(
    modifier: Modifier = Modifier,
    // ✅ DO: for design system components, expose a style modifier to consumers to be able to customize the components
    style: Style = Style
) {
    // Consume the style
}

Do: Replace visual-based parameters with a Style (Empfehlung: Visuelle Parameter durch einen Stil ersetzen)

Erwägen Sie, Parameter in Ihren Composables durch einen einzelnen Style-Parameter zu ersetzen. Beispiel:

// Before
@Composable
fun OldButton(background: Color, fontColor: Color) {
}

// After
// ✅ DO: Replace visual-based parameters with a style that includes same properties
@Composable
fun NewButton(style: Style = Style) {
}

Do: Prioritize Styles for animations

Verwenden Sie den integrierten animate-Block für zustandsbasiertes Styling mit Animationen, um im Vergleich zu Modifizierern eine bessere Leistung zu erzielen.

Do: „Last-write-wins“-Strategie nutzen

Nutzen Sie den Umstand, dass style-Properties überschrieben und nicht gestapelt werden. Damit können Sie Standardkomponentenränder oder -hintergründe überschreiben, ohne mehrere Parameter zu benötigen.

Zu vermeidende Vorgehensweise

Die folgenden Muster sind nicht empfehlenswert:

Nicht: Styles für Interaktionslogik verwenden

Versuchen Sie nicht, onClick oder die Erkennung von Gesten in einem Stil zu verarbeiten. Stile sind auf visuelle Konfigurationen basierend auf dem Status beschränkt. Sie sollten daher keine Geschäftslogik enthalten, sondern nur eine unterschiedliche visuelle Darstellung basierend auf dem Status.

Nicht: Standardstil als Standardparameter angeben

Stilparameter sollten immer mit style: Style = Style deklariert werden:

@Composable
fun BadButton(
    modifier: Modifier = Modifier,
    // ❌ DON'T set a default style here as a parameter
    style: Style = Style { background(Color.Red) }
) {
}

Wenn Sie einen „default“-Parameter einfügen möchten, führen Sie den eingehenden Parameterstil mit dem Standardstil zusammen:

@Composable
fun GoodButton(
    modifier: Modifier = Modifier,
    // ✅ Do: always pass it as a Style, do not pass other defaults
    style: Style = Style
) {
    // ...
    val defaultStyle = Style { background(Color.Red) }
    // ✅ Do Combine defaults inside with incoming parameter
    Box(modifier = modifier.styleable(styleState, defaultStyle, style)) {
      // your logic
    }
}

Nicht: Stilparameter für layoutbasierte Composables angeben

Sie können zwar jedem Composable einen Stil zuweisen, aber es wird nicht erwartet, dass Layout-basierte oder bildschirmbezogene Composables einen Stil akzeptieren. Aus Nutzersicht ist unklar, was ein Stil auf dieser Ebene bewirken würde. Stile sind für Komponenten und nicht unbedingt für Layouts konzipiert.

Nicht: Stile in „Composition“ erstellen

CompositionLocals werden an dem Punkt gelesen, an dem der Stil definiert wird, nicht an dem Punkt, an dem er verwendet wird. Wenn der Stil tatsächlich verwendet wird, kann sich der Status von CompositionLocal geändert haben, was zu einem ungenauen Stil führt.

// DON'T - Create styles in Composition that access composition locals in this way - this will likely lead to issues when style is used / accessed, as it would not get updated when the value changes.
@Composable
fun containerStyle(): Style {
    val background = MaterialTheme.colorScheme.background
    val onBackground = MaterialTheme.colorScheme.onBackground
    return Style {
        background(background)
        contentColor(onBackground)
    }
}

// Do: Instead, Create StyleScope extension functions for your subsystems to access themed composition Locals
val StyleScope.colors: JetsnackColors
    get() = JetsnackTheme.LocalJetsnackTheme.currentValue.colors

val StyleScope.typography: androidx.compose.material3.Typography
    get() = JetsnackTheme.LocalJetsnackTheme.currentValue.typography
val StyleScope.shapes: Shapes
    get() = JetsnackTheme.LocalJetsnackTheme.currentValue.shapes
// Access CompositionLocals
val button = Style {
    background(colors.brandSecondary)
    shape(shapes.small)
}

Empfehlung: Einen Stil für Änderungen am Subsystemwert erstellen

Wenn Sie beispielsweise zwischen dem dunklen und hellen Modus wechseln, fragen Sie vorhandene thematische Werte (über CompositionLocal) ab, um Style dynamisch zu ändern:

// Do: Use CompositionLocals or themed values to create a single style
val buttonStyle = Style {
    background(colors.brandSecondary)
    shape(shapes.small)
}

Do: Switch out whole Styles when the component fundamentally differs across theme definitions (Do: Switch out whole Styles when the component fundamentally differs across theme definitions)

Sie können ganze Stilobjekte auf Designebene austauschen, wenn es sich um grundlegend unterschiedliche Designs handelt.

Wenn Sie beispielsweise eine App erstellen, die für jedes Produkt/jede Seite oder jedes Angebot unterschiedliche Designs hat und viele Eigenschaften eines Stils unterschiedlich sind, ist es akzeptabel, ganze Stilgruppen auf Designebene auszutauschen.

// DO Switch out whole styles when many properties differ - if Product A and Product B are two white labelled apps that provide different Themes.
val productBThemedButton = Style {
    shape(shapes.small)
    background(colors.brandSecondary)
    // other properties are fundamentally different
}

val productAThemedButton = Style {
    shape(shapes.large)
    background(colors.brand)
    // other properties are fundamentally different
}