Il existe plusieurs façons de créer vos applications à l'aide de Styles. Votre choix dépend de l'état d'adoption de Material Design par votre application :
- Système de conception entièrement personnalisé, n'utilisant pas Material Design
- Recommandation : Définissez des styles de composants qui consomment des valeurs du thème et exposez des paramètres de style sur les composants du système de conception.
- Utiliser Material Design
- Recommandation : Attendez l'adoption de Material pour l'intégrer aux styles. Utilisez des styles sur vos propres composants lorsque cela est possible.
La couche "Style"
Dans le modèle Compose traditionnel, la personnalisation repose souvent fortement sur la substitution des jetons globaux (couleurs et typographie) fournis par MaterialTheme, ou sur l'encapsulation et la substitution des propriétés d'un composable du système de conception lorsque cela est possible.
Parfois, certaines propriétés de la couche Material ne sont pas exposées par les sous-systèmes ou les paramètres, mais sont des valeurs par défaut codées en dur sur le composant lui-même.
L'API Styles introduit une nouvelle couche d'abstraction qui fait le lien entre les sous-systèmes et les composants : Styles.
| intégrée | Responsabilité | Exemple |
|---|---|---|
| Valeurs de sous-système | Valeurs nommées | val Primary = Color(0xFF34A85E) |
| Styles atomiques | Style qui ne modifie qu'une seule propriété | val buttonStyle = paddingAtomic then roundedCornerShapeAtomic then primaryBackgroundAtomic then largeSize then interactiveShadowAtomic |
| Styles de composants | Configurations spécifiques aux composants | Bouton avec un arrière-plan principal et une marge intérieure de 16 dp. val buttonStyle = Style { contentPadding(16.dp) shape(RoundedCornerShape(8.dp)) background(Color.Blue) } |
| Composants | Élément d'interface utilisateur fonctionnel qui utilise un style. | Button(style = buttonStyle) { ... } |
Styles atomiques et monolithiques
L'API Styles vous permet de décomposer un style en styles atomiques distincts.
Au lieu de définir des styles complexes et spécifiques aux composants comme baseButtonStyle, vous pouvez également créer de petits styles utilitaires à usage unique. Elles font office d'atomes.
// 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, ) ) } } }
Composition utilisant "puis"
L'opérateur then est l'une des fonctionnalités puissantes de la nouvelle API Styles. Il vous permet de fusionner plusieurs objets Style. Cela vous permet de créer un composant à l'aide de classes utilitaires atomiques.
Traditionnel (non atomique) :
// One large monolithic style val buttonStyle = Style { contentPadding(16.dp) shape(RoundedCornerShape(8.dp)) background(Color.Blue) }
Refactorisation atomique :
// Combine atoms to create the final appearance val buttonStyle = paddingAtomic then roundedCornerShapeAtomic then primaryBackgroundAtomic then interactiveShadowAtomic
Adopter des styles dans votre système de conception
Tenez compte des options suivantes lorsque vous adoptez des styles dans votre système de conception, en fonction de l'endroit où se situe votre système de conception dans le spectre.
Système de conception personnalisé avec des styles
Quand l'utiliser : vous avez reçu un guide de marque complet qui n'est pas basé sur Material Design et vous ne prévoyez pas d'utiliser Material Design.
Stratégie : implémentez un système de conception entièrement personnalisé et exposez les styles en tant qu'éléments du thème.
Cette option correspond au chemin personnalisé si vous n'utilisez pas Material comme langue principale de votre système de conception. Vous contournez complètement MaterialTheme pour les définitions visuelles et vous avez déjà créé votre propre thème personnalisé. Vous créez un CompanyTheme qui sert de conteneur pour vos styles.
- Fonctionnement : créez un objet
CompanyThemecontenant des objetsStylepour chaque composant de votre système. Vos composants (qu'il s'agisse d'enveloppes autour de la logique Material ou d'implémentationsBoxouLayoutpersonnalisées) utilisent ces styles directement et exposent un paramètreStylepour les consommateurs de votre système de conception. - Calque de style : les styles sont la définition principale de votre système de conception. Les jetons sont des variables nommées qui sont insérées dans ces styles. Cela permet une personnalisation approfondie, comme la définition d'animations uniques pour les changements d'état (par exemple, l'animation de l'échelle et de la couleur lors de l'appui).
Si vous créez votre propre thème personnalisé sans utiliser Material et que vous souhaitez adopter des styles, ajoutez votre liste de styles à votre thème. Cela vous permet d'accéder à vos styles de base depuis n'importe quel endroit de votre projet.
Créez une classe
Stylesqui stocke les différents styles de votre application et créez les styles par défaut. Par exemple, dans l'application Jetsnack, la classe est nomméeJetsnackStyles: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) } }
Fournissez
Stylesdans votre thème global et exposez les fonctions d'extension d'assistance surStyleScopepour accéder aux sous-systèmes :@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, ) } }
Accédez à
JetsnackStylesdans votre composable :@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) } }
Au-delà de l'adoption du thème global, il existe d'autres stratégies pour intégrer Styles dans vos applications. Vous pouvez utiliser Styles en ligne pour des sites d'appel spécifiques ou utiliser des définitions statiques lorsque les fonctionnalités de thème complet ne sont pas nécessaires.
Styles ne doit pas être remplacé de manière conditionnelle, sauf si le style entier est fondamentalement différent. Il est préférable d'accéder aux jetons dynamiques à l'intérieur d'une définition visuelle plutôt que de basculer entre différents objets de style.