Vous pouvez créer vos applications de plusieurs manières à l'aide de styles. Votre choix dépend de la place de votre application par rapport à son adoption de Material Design :
- Système de conception entièrement personnalisé, n'utilisant pas Material Design
- Recommandation : Définissez des styles de composants qui utilisent des valeurs du thème et exposez des paramètres de style sur les composants du système de conception.
- Utilisation de 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 de style
Dans le modèle Compose traditionnel, la personnalisation repose souvent sur la substitution de jetons globaux (couleurs et typographie) fournis par MaterialTheme, ou sur l'encapsulation et la substitution des propriétés d'un composable de système de conception lorsque cela est possible.
Parfois, certaines propriétés de la couche Material ne sont pas exposées via 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.
Avec l'API Styles, il existe une nouvelle couche d'abstraction qui sert de pont entre les sous-systèmes et les composants : les styles.
| couche | Responsabilité | Exemple |
|---|---|---|
| Valeurs du sous-système | Valeurs nommées | val Primary = Color(0xFF34A85E) |
| Styles atomiques | Style qui ne modifie qu'une seule propriété | val largeSizeAtomic = Style { size(100.dp, 40.dp) } |
| 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 ou monolithiques
Avec l'API Styles, vous pouvez 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. Ils agissent comme vos "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 à l'aide de "then"
L'une des fonctionnalités puissantes de la nouvelle API Styles est l'opérateur then, qui vous permet de fusionner plusieurs objets Style. Vous pouvez ainsi 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
À prendre en compte lorsque : 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 dans le cadre du thème.
Cette option est le chemin personnalisé si vous n'utilisez pas Material comme langage principal de votre système de conception. Vous contournez MaterialTheme entièrement 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
CompanyThemequi contient des objetsStylepour chaque composant de votre système. Vos composants (encapsuleurs autour de la logique Material ou implémentationsBoxouLayoutpersonnalisées) utilisent directement ces styles et exposent un paramètreStylepour les consommateurs de votre système de conception. - La couche de style : les styles sont la définition principale de votre système de conception. Les jetons sont des variables nommées qui sont transmises à ces styles. Cela permet une personnalisation approfondie, par exemple en définissant des animations uniques pour les changements d'état (par exemple, en animant l'échelle et la couleur lors d'une pression).
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. Vous pouvez ainsi accéder à vos styles de base depuis n'importe où dans votre projet.
Créez une classe
Stylesqui stocke les différents styles de votre application et créez les valeurs 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 le cadre de votre thème global et exposez des 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 globale du thème, il existe d'autres stratégies pour intégrer Styles dans vos applications. Vous pouvez exploiter Styles en ligne pour des sites d'appel spécifiques ou utiliser des définitions statiques lorsque les fonctionnalités de thème complètes ne sont pas nécessaires.
Styles ne doit pas être échangé de manière conditionnelle, sauf si le style entier est fondamentalement différent. Vous devez préférer accéder aux jetons dynamiques dans une définition visuelle plutôt que de basculer entre des objets de style distincts.