Zalecenia i ograniczenia dotyczące stylów

Na tej stronie opisujemy sprawdzone metody pracy ze stylami, które zapewniają spójność w całej bazie kodu, a także zasady, którymi kierowaliśmy się podczas projektowania interfejsów API.

Tak

Postępuj zgodnie z tymi sprawdzonymi metodami:

Rób: używaj stylów w przypadku elementów wizualnych i modyfikatorów w przypadku zachowań

Używaj interfejsu Styles API do konfiguracji wizualnej (tła, dopełnienia, obramowania), a modyfikatorów do zachowań takich jak logika kliknięć, wykrywanie gestów czy ułatwienia dostępu.

Do: udostępniaj parametry stylu w systemach projektowania.

W przypadku własnych komponentów systemu projektowania niestandardowego po parametrze modyfikatora należy udostępnić Style object.

@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
}

Zrób to: zastąp parametry wizualne stylem

Rozważ zastąpienie parametrów w funkcjach kompozycyjnych pojedynczym parametrem Style. Przykład:

// 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) {
}

Rób: priorytetyzuj style w animacjach

Użyj wbudowanego bloku animate do stylizacji opartej na stanie z animacjami, aby uzyskać większą wydajność niż w przypadku modyfikatorów.

Rób: korzystaj z zasady „ostatni zapis wygrywa”

Skorzystaj z tego, że właściwości style zastępują się nawzajem, a nie nakładają na siebie. Użyj tego parametru, aby zastąpić domyślne obramowania lub tła komponentów bez konieczności używania wielu parametrów.

Nie

Nie zalecamy stosowania tych wzorców:

Nie używaj stylów do logiki interakcji.

Nie próbuj obsługiwać onClick ani wykrywania gestów w ramach stylu. Style są ograniczone do konfiguracji wizualnych opartych na stanie, więc nie powinny obsługiwać logiki biznesowej. Zamiast tego powinny mieć tylko inny wygląd w zależności od stanu.

Nie: podawaj domyślnego stylu jako domyślnego parametru

Parametry stylu należy zawsze deklarować za pomocą style: Style = Style:

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

Aby uwzględnić parametr „default”, scal styl parametru przychodzącego z domyślnym stylem zdefiniowanym w ten sposób:

@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
    }
}

Nie: przekazuj parametrów stylu do komponentów kompozycyjnych opartych na układzie

Chociaż możesz zastosować styl do dowolnego komponentu, nie oczekuje się, że komponenty oparte na układzie lub komponenty na poziomie ekranu będą akceptować styl – z punktu widzenia użytkownika nie jest jasne, co styl miałby robić na tym poziomie. Style są przeznaczone dla komponentów, a niekoniecznie dla układów.

Nie twórz stylów w kompozycji

CompositionLocals są odczytywane w miejscu, w którym zdefiniowano styl, a nie w miejscu, w którym jest on używany. Gdy styl jest faktycznie używany, stan CompositionLocal może się zmienić, co spowoduje, że styl będzie niedokładny.

// 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)
}

Zrób to: utwórz 1 styl dla zmian wartości podsystemu

Jeśli na przykład przełączasz się między trybem ciemnym a jasnym, możesz dynamicznie zmieniać Style, wysyłając zapytania o istniejące wartości motywu (za pomocą CompositionLocal):

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

Rób: zamieniaj całe style, gdy komponent zasadniczo różni się w różnych definicjach motywów.

Możesz zamieniać całe obiekty stylu na poziomie motywu, jeśli są to zasadniczo różne motywy.

Jeśli na przykład tworzysz aplikację, która ma różne motywy dla każdego produktu lub strony, a wiele właściwości stylu jest różnych, dopuszczalne jest zamienianie całych zestawów stylów na poziomie motywu.

// 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
}