Bonnes pratiques et pratiques à éviter avec les styles

Cette page décrit les bonnes pratiques à suivre pour travailler avec des styles qui assurent la cohérence de votre code, ainsi que les principes que nous avons suivis lors de la conception des API.

À faire

Appliquez les bonnes pratiques suivantes :

À faire : utiliser des styles pour les éléments visuels et des modificateurs pour les comportements

Utilisez l'API Styles pour la configuration visuelle (arrière-plans, marges intérieures, bordures) et réservez les modificateurs pour les comportements tels que la logique de clic, la détection des gestes ou l'accessibilité.

À faire : exposer les paramètres de style dans les systèmes de conception

Pour vos propres composants de système de conception personnalisés, vous devez exposer un objet Style après le paramètre de modificateur.

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

À faire : remplacer les paramètres visuels par un style

Envisagez de remplacer les paramètres de vos composables par un seul paramètre Style. Exemple :

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

À faire : privilégier les styles pour les animations

Utilisez le bloc animate intégré pour appliquer un style basé sur l'état avec des animations afin d'améliorer les performances par rapport aux modificateurs.

À faire : profitez de la règle "La dernière écriture l'emporte"

Profitez du fait que les propriétés style se remplacent plutôt que de s'empiler. Utilisez-le pour remplacer les bordures ou les arrière-plans des composants par défaut sans avoir besoin de plusieurs paramètres.

À éviter

Les formats suivants sont déconseillés :

Ne pas utiliser de styles pour la logique d'interaction

N'essayez pas de gérer la détection de onClick ou de gestes dans un style. Les styles sont limités aux configurations visuelles basées sur l'état. Ils ne doivent donc pas gérer la logique métier, mais uniquement avoir un visuel différent en fonction de l'état.

À ne pas faire : fournir un style par défaut en tant que paramètre par défaut

Les paramètres de style doivent toujours être déclarés à l'aide de 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) }
) {
}

Pour inclure un paramètre "par défaut", fusionnez le style du paramètre entrant avec celui défini par défaut :

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

À éviter : fournir des paramètres de style aux composables basés sur la mise en page

Bien que vous puissiez fournir un style à n'importe quel composable, il n'est pas prévu que les composables basés sur la mise en page ou les composables au niveau de l'écran acceptent un style. Du point de vue du consommateur, l'effet d'un style à ce niveau n'est pas clair. Les styles sont conçus pour les composants, et pas nécessairement pour les mises en page.

À ne pas faire : créer des styles dans Composition

Les CompositionLocals sont lus au moment où le style est défini, et non au moment où il est utilisé. Lorsque le style est réellement utilisé, l'état de CompositionLocal peut avoir changé, ce qui entraîne un style inexact.

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

À faire : créer un style pour les modifications de valeurs de sous-système

Par exemple, si vous basculez entre le mode sombre et le mode clair, interrogez les valeurs thématiques existantes (via CompositionLocal) pour modifier Style de manière dynamique :

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

À faire : remplacer des styles entiers lorsque le composant diffère fondamentalement selon les définitions de thème

Vous pouvez remplacer des objets de style entiers au niveau d'un thème s'il s'agit de thèmes fondamentalement différents.

Par exemple, si vous créez une application qui propose différents thèmes par produit/page ou offre, et que de nombreuses propriétés d'un style sont différentes, il est acceptable de remplacer des ensembles entiers de styles au niveau du thème.

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