Jetpack Compose propose une implémentation de Material Design, un système de conception complet permettant de créer des interfaces numériques. Les composants Material Design (boutons, cartes, interrupteurs, etc.) sont basés sur la thématisation Material, un moyen systématique de personnaliser Material Design pour mieux refléter la marque de votre produit. Un thème Material inclut les attributs couleur, typographie et forme. Lorsque vous personnalisez ces attributs, vos modifications sont automatiquement répercutées dans les composants que vous utilisez pour créer votre application.
Jetpack Compose implémente ces concepts avec le composable MaterialTheme :
MaterialTheme( colors = // ... typography = // ... shapes = // ... ) { // app content }
Configurez les paramètres que vous transmettez à MaterialTheme pour appliquer un thème à votre application.
Couleur
Les couleurs sont modélisées dans Compose avec la classe Color, qui est une classe de stockage de données.
val Red = Color(0xffff0000) val Blue = Color(red = 0f, green = 0f, blue = 1f)
Bien que vous puissiez les organiser comme vous le souhaitez (en tant que constantes de premier niveau, dans un singleton ou définies de manière intégrée), nous vous recommandons vivement de spécifier des couleurs dans votre thème et de les récupérer à partir de cet endroit. Cela permet de prendre en charge les thèmes sombres et les thèmes imbriqués.
Compose fournit la classe Colors pour modéliser le système de couleurs Material. Colors fournit des fonctions de compilateur pour créer des ensembles de couleurs claires ou sombres :
private val Yellow200 = Color(0xffffeb46) private val Blue200 = Color(0xff91a4fc) // ... private val DarkColors = darkColors( primary = Yellow200, secondary = Blue200, // ... ) private val LightColors = lightColors( primary = Yellow500, primaryVariant = Yellow400, secondary = Blue700, // ... )
Après avoir défini vos Colors, vous pouvez les transmettre à un MaterialTheme :
MaterialTheme( colors = if (darkTheme) DarkColors else LightColors ) { // app content }
Utiliser les couleurs du thème
Vous pouvez récupérer les Colors fournies au composable MaterialTheme à l'aide de MaterialTheme.colors.
Text( text = "Hello theming", color = MaterialTheme.colors.primary )
Couleur de la surface et du contenu
De nombreux composants acceptent une paire de couleurs et des couleurs de contenu :
Surface( color = MaterialTheme.colors.surface, contentColor = contentColorFor(color), // ... ) { /* ... */ } TopAppBar( backgroundColor = MaterialTheme.colors.primarySurface, contentColor = contentColorFor(backgroundColor), // ... ) { /* ... */ }
Cela vous permet non seulement de définir la couleur d'un composable, mais aussi d'utiliser une couleur par défaut pour le contenu,c'est-à-dire les composables qu'il contient. De nombreux composables utilisent cette couleur de contenu par défaut. Par exemple, Text base sa couleur sur celle du contenu de son parent, et Icon utilise cette couleur pour définir sa teinte.
La méthode contentColorFor() récupère la couleur "on" appropriée pour toutes les couleurs du thème. Par exemple, si vous définissez un arrière-plan de couleur primary pour la Surface, cette fonction est utilisée pour définir onPrimary comme couleur de contenu.
Si vous choisissez une couleur d'arrière-plan différente de celle du thème, vous devez également spécifier une couleur de contenu appropriée. Utilisez LocalContentColor pour récupérer la couleur de contenu préférée de l'arrière-plan actuel, à une position donnée dans la hiérarchie.
Valeurs alpha du contenu
Vous souhaitez souvent varier la mise en valeur du contenu pour insister sur l'importance et fournir une hiérarchie visuelle. Les recommandations de lisibilité du texte Material Design conseillent d'employer différents niveaux d'opacité pour indiquer différents niveaux d'importance.
Jetpack Compose implémente cela à l'aide de LocalContentAlpha. Vous pouvez spécifier une valeur alpha du contenu pour une hiérarchie en fournissant une valeur pour ce CompositionLocal. Les composables imbriqués peuvent utiliser cette valeur pour appliquer le traitement alpha à leur contenu. Par exemple, Text et Icon utilisent par défaut la combinaison de LocalContentColor ajustée pour utiliser LocalContentAlpha. Material spécifie des valeurs alpha standards (high, medium, disabled), qui sont modélisées par l'objet ContentAlpha.
// By default, both Icon & Text use the combination of LocalContentColor & // LocalContentAlpha. De-emphasize content by setting content alpha CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) { Text( // ... ) } CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.disabled) { Icon( // ... ) Text( // ... ) }
Pour en savoir plus sur CompositionLocal, consultez Données à champ d'application local avec CompositionLocal.
ContentAlpha.high. La deuxième ligne contient des métadonnées moins importantes et utilise donc ContentAlpha.medium.Thème sombre
Dans Compose, vous implémentez des thèmes clair et sombre en fournissant différents ensembles de Colors pour le composable MaterialTheme :
@Composable fun MyTheme( darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit ) { MaterialTheme( colors = if (darkTheme) DarkColors else LightColors, /*...*/ content = content ) }
Dans cet exemple, MaterialTheme est encapsulé dans sa propre fonction modulable, qui accepte un paramètre spécifiant s'il faut ou non utiliser un thème sombre. Dans ce cas, la fonction obtient la valeur par défaut pour darkTheme en interrogeant le paramètre de thème de l'appareil.
Vous pouvez utiliser ce code pour vérifier si les Colors actuelles sont claires ou sombres :
val isLightTheme = MaterialTheme.colors.isLight Icon( painterResource( id = if (isLightTheme) { R.drawable.ic_sun_24 } else { R.drawable.ic_moon_24 } ), contentDescription = "Theme" )
Superpositions d'élévation
Dans Material, les surfaces des thèmes sombres d'élévation plus élevée reçoivent des superpositions d'élévation, ce qui éclaircit leur arrière-plan. Plus l'élévation d'une surface est élevée (c'est-à-dire plus proche d'une source de lumière implicite), plus cette surface est claire.
Le composable Surface applique automatiquement ces superpositions lors de l'utilisation de couleurs sombres, tout comme tout autre composable Material qui utilise une surface :
Surface( elevation = 2.dp, color = MaterialTheme.colors.surface, // color will be adjusted for elevation /*...*/ ) { /*...*/ }
surface comme arrière-plan. Étant donné que les cartes et la barre de navigation inférieure sont situées à des élévations différentes au-dessus de l'arrière-plan, les couleurs sont légèrement différentes. Les cartes sont plus claires que l'arrière-plan et la barre de navigation inférieure est plus claire que les cartes.Pour les scénarios personnalisés qui n'impliquent pas de Surface, utilisez LocalElevationOverlay, un CompositionLocal contenant l'ElevationOverlay utilisée par les composants Surface :
// Elevation overlays // Implemented in Surface (and any components that use it) val color = MaterialTheme.colors.surface val elevation = 4.dp val overlaidColor = LocalElevationOverlay.current?.apply( color, elevation )
Pour désactiver les superpositions d'élévation, indiquez null au point choisi dans une hiérarchie composable :
MyTheme { CompositionLocalProvider(LocalElevationOverlay provides null) { // Content without elevation overlays } }
Touches de couleurs limitées
Dans la plupart des cas, Material Design recommande d'appliquer des touches de couleurs limitées pour les thèmes sombres en utilisant plutôt la couleur surface que la couleur primary. Les composables Material tels que TopAppBar et BottomNavigation implémentent ce comportement par défaut.
Pour les scénarios personnalisés, utilisez la propriété d'extension primarySurface :
Surface( // Switches between primary in light theme and surface in dark theme color = MaterialTheme.colors.primarySurface, /*...*/ ) { /*...*/ }
Typographie
Material Design définit un système typographique, qui vous encourage à utiliser un petit nombre de styles nommés sémantiquement.
Compose implémente le système de typographie avec les classes Typography, TextStyle et liées à la police. Le constructeur Typography propose des valeurs par défaut pour chaque style afin que vous puissiez omettre ceux que vous ne souhaitez pas personnaliser :
val raleway = FontFamily( Font(R.font.raleway_regular), Font(R.font.raleway_medium, FontWeight.W500), Font(R.font.raleway_semibold, FontWeight.SemiBold) ) val myTypography = Typography( h1 = TextStyle( fontFamily = raleway, fontWeight = FontWeight.W300, fontSize = 96.sp ), body1 = TextStyle( fontFamily = raleway, fontWeight = FontWeight.W600, fontSize = 16.sp ) /*...*/ ) MaterialTheme(typography = myTypography, /*...*/) { /*...*/ }
Si vous souhaitez utiliser la même police de caractères partout, spécifiez le paramètre defaultFontFamily et omettez la fontFamily des éléments TextStyle :
val typography = Typography(defaultFontFamily = raleway) MaterialTheme(typography = typography, /*...*/) { /*...*/ }
Utiliser des styles de texte
Les éléments TextStyle sont accessibles à l'aide de MaterialTheme.typography. Récupérez les éléments TextStyle comme suit :
Text( text = "Subtitle2 styled", style = MaterialTheme.typography.subtitle2 )
Forme
Material définit un système de formes qui vous permet de définir des formes pour les composants volumineux, moyens et petits.
Compose implémente le système de formes avec la classe Shapes, qui vous permet de spécifier une CornerBasedShape pour chaque catégorie de taille :
val shapes = Shapes( small = RoundedCornerShape(percent = 50), medium = RoundedCornerShape(0f), large = CutCornerShape( topStart = 16.dp, topEnd = 0.dp, bottomEnd = 0.dp, bottomStart = 16.dp ) ) MaterialTheme(shapes = shapes, /*...*/) { /*...*/ }
De nombreux composants utilisent ces formes par défaut. Par exemple, Button, TextField et FloatingActionButton sont définis par défaut sur "petit", AlertDialog sur "moyen" et ModalDrawer sur "grand". Consultez la documentation de référence sur les schémas de forme pour le mappage complet.
Utiliser des formes
Les éléments Shape sont accessibles à l'aide de MaterialTheme.shapes. Récupérez les éléments Shape avec le code suivant :
Surface( shape = MaterialTheme.shapes.medium, /*...*/ ) { /*...*/ }
Styles par défaut
Les styles par défaut pour les vues Android n'ont pas d'équivalent dans Compose. Vous pouvez fournir une fonctionnalité similaire en créant vos propres fonctions composables overload qui encapsulent les composants Material. Par exemple, pour créer un style de bouton, encapsulez un bouton dans votre fonction modulable, en définissant directement les paramètres que vous souhaitez ou devez modifier et en exposant les autres en tant que paramètres au composable associé.
@Composable fun MyButton( onClick: () -> Unit, modifier: Modifier = Modifier, content: @Composable RowScope.() -> Unit ) { Button( colors = ButtonDefaults.buttonColors( backgroundColor = MaterialTheme.colors.secondary ), onClick = onClick, modifier = modifier, content = content ) }
Superpositions de thèmes
Vous pouvez obtenir l'équivalent des superpositions de thèmes d'Android Views dans Compose en imbriquant des composables MaterialTheme. Comme MaterialTheme utilise par défaut la valeur actuelle du thème pour les couleurs, la typographie et les formes, tous les autres paramètres conservent leurs valeurs par défaut lorsqu'un thème ne définit qu'un seul de ces paramètres.
De plus, lorsque vous migrez des écrans basés sur les vues vers Compose, faites attention aux utilisations de l'attribut android:theme. Vous aurez probablement besoin d'un nouveau MaterialTheme dans cette partie de l'arborescence de l'UI Compose.
Dans cet exemple, l'écran des détails utilise un thème rose (PinkTheme) pour la majeure partie de l'écran, puis un thème bleu (BlueTheme) pour la section associée. La capture d'écran et le code suivants illustrent ce concept :
@Composable fun DetailsScreen(/* ... */) { PinkTheme { // other content RelatedSection() } } @Composable fun RelatedSection(/* ... */) { BlueTheme { // content } }
États des composants
Les composants Material avec lesquels vous pouvez interagir (cliqué, activé/désactivé, etc.) peuvent se trouver dans différents états visuels. Les états incluent : activé, désactivé, appuyé, etc.
Les composables comportent souvent un paramètre enabled. Définir sa valeur sur false empêche toute interaction et modifie des propriétés telles que la couleur et l'élévation pour transmettre visuellement l'état du composant.
enabled = true (à gauche) et enabled = false (à droite).Dans la plupart des cas, vous pouvez conserver les valeurs par défaut pour les valeurs telles que la couleur et l'élévation. Si vous devez configurer des valeurs utilisées dans différents états, des classes et des fonctions pratiques sont disponibles. Prenons l'exemple de bouton suivant :
Button( onClick = { /* ... */ }, enabled = true, // Custom colors for different states colors = ButtonDefaults.buttonColors( backgroundColor = MaterialTheme.colors.secondary, disabledBackgroundColor = MaterialTheme.colors.onBackground .copy(alpha = 0.2f) .compositeOver(MaterialTheme.colors.background) // Also contentColor and disabledContentColor ), // Custom elevation for different states elevation = ButtonDefaults.elevation( defaultElevation = 8.dp, disabledElevation = 2.dp, // Also pressedElevation ) ) { /* ... */ }
enabled = true (à gauche) et enabled = false (à droite), avec des valeurs de couleur et d'élévation ajustées.Ondulations
Les composants Material utilisent des ondulations pour indiquer qu'ils font l'objet d'une interaction. Si vous utilisez MaterialTheme dans votre hiérarchie, une ondulation (Ripple) est utilisée comme Indication par défaut dans des modificateurs tels que clickable et indication.
Dans la plupart des cas, vous pouvez conserver la Ripple par défaut. Si vous devez configurer leur apparence, vous pouvez utiliser RippleTheme pour modifier des propriétés telles que la couleur et l'alpha.
Vous pouvez étendre RippleTheme et utiliser les fonctions utilitaires defaultRippleColor et defaultRippleAlpha. Vous pouvez ensuite fournir votre thème d'ondulation personnalisé dans votre hiérarchie à l'aide de LocalRippleTheme :
@Composable fun MyApp() { MaterialTheme { CompositionLocalProvider( LocalRippleTheme provides SecondaryRippleTheme ) { // App content } } } @Immutable private object SecondaryRippleTheme : RippleTheme { @Composable override fun defaultColor() = RippleTheme.defaultRippleColor( contentColor = MaterialTheme.colors.secondary, lightTheme = MaterialTheme.colors.isLight ) @Composable override fun rippleAlpha() = RippleTheme.defaultRippleAlpha( contentColor = MaterialTheme.colors.secondary, lightTheme = MaterialTheme.colors.isLight ) }
RippleTheme.En savoir plus
Pour en savoir plus sur la thématisation Material dans Compose, consultez les ressources supplémentaires suivantes.
Ateliers de programmation
Vidéos
- Material You in Jetpack Compose (Material You dans Jetpack Compose)
Recommandations personnalisées
- Remarque : Le texte du lien s'affiche lorsque JavaScript est désactivé
- Systèmes de conception personnalisés dans Compose
- Passer de Material 2 à Material 3 dans Compose
- Accessibilité dans Compose