Material Design 3 est la nouvelle évolution de Material Design. Elle inclut des thèmes et des composants mis à jour ainsi que des fonctionnalités de personnalisation Material You telles que les couleurs dynamiques. Cette mise à jour de Material Design 2 est compatible avec le nouveau style visuel et l'UI du système Android 12 et versions ultérieures.
Ce guide traite de la migration de la bibliothèque Jetpack Compose Material (androidx.compose.material) vers la bibliothèque Jetpack Compose Material 3 (androidx.compose.material3).
Approches
En général, vous ne devez pas utiliser à la fois M2 et M3 dans une même application sur une longue période. En effet, les deux systèmes de conception et leur bibliothèque respective diffèrent de manière significative en termes de conception de l'expérience/interface utilisateur et d'implémentation de Compose.
Il se peut que votre application utilise un système de conception, tel que celui créé à l'aide de Figma. Dans ce cas, nous vous recommandons vivement que vous ou votre équipe de conception effectuiez la migration de M2 à M3 avant de commencer la migration vers Compose. Il n'est pas logique de migrer une application vers M3 si sa conception de l'expérience utilisateur/interface utilisateur est basée sur M2.
De plus, votre approche de la migration doit varier en fonction de la taille, de la complexité et de l'expérience/interface utilisateur de votre application. En effet, cela vous permet de minimiser l'impact sur votre codebase. Vous devriez effectuer une migration par étapes.
Quand migrer ?
Vous devez commencer la migration dès que possible. Cependant, il est important de déterminer si votre application est effectivement prête pour une migration totale de M2 vers M3. Voici quelques scénarios bloquants à examiner avant de commencer:
Scénario | Approche recommandée |
---|---|
Aucun bloqueur | Commencez la migration par étapes |
Un composant de M2 n'est pas encore disponible dans M3. Consultez la section Composants et mises en page ci-dessous. | Commencez la migration par étapes |
Vous ou votre équipe de conception n'avez pas migré le système de conception de l'application de M2 vers M3. | Effectuez la migration du système de conception de M2 vers M3, puis commencez la migration par étapes |
Même si les scénarios ci-dessus s'appliquent à votre cas, vous devez migrer par étapes avant de valider et de publier une mise à jour de votre application. Vous devez alors utiliser M2 et M3 côte à côte, puis abandonner progressivement M2 au fur et à mesure que vous migrez vers M3.
Approche par étapes
Voici les étapes générales d'une migration par étapes :
- Ajoutez la dépendance M3 parallèlement à la dépendance M2.
- Ajoutez les versions M3 des thèmes de votre application en plus des versions M2 des thèmes de votre application.
- Migrez les modules, écrans ou composables individuels vers M3, selon la taille et la complexité de votre application. Pour en savoir plus, consultez les sections ci-dessous.
- Une fois la migration terminée, supprimez la ou les versions M2 des thèmes de votre application.
- Supprimez la dépendance M2.
Dépendances
M3 possède un package et une version distincts de M2 :
M2
implementation "androidx.compose.material:material:$m2-version"
M3
implementation "androidx.compose.material3:material3:$m3-version"
Consultez les dernières versions de M3 sur la page des versions de Compose Material 3.
Les autres dépendances Material n'appartenant pas aux bibliothèques M2 et M3 principales n'ont pas été modifiées. Elles mélangent des packages et des versions M2 et M3, mais cela n'a aucun impact sur la migration. Elles peuvent être utilisées telles quelles avec M3 :
Bibliothèque | Package et version |
---|---|
Icônes Compose Material | androidx.compose.material:material-icons-*:$m2-version |
Ondulation Compose Material | androidx.compose.material:material-ripple:$m2-version |
API expérimentales
Certaines API M3 sont considérées comme expérimentales. Dans ce cas, vous devez les activer au niveau de la fonction ou du fichier à l'aide de l'annotation ExperimentalMaterial3Api
:
import androidx.compose.material3.ExperimentalMaterial3Api
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AppComposable() {
// M3 composables
}
Personnalisation des thèmes
Dans M2 et M3, le composable Thème est appelé MaterialTheme
, mais les packages et paramètres d'importation diffèrent :
M2
import androidx.compose.material.MaterialTheme
MaterialTheme(
colors = AppColors,
typography = AppTypography,
shapes = AppShapes
) {
// M2 content
}
M3
import androidx.compose.material3.MaterialTheme
MaterialTheme(
colorScheme = AppColorScheme,
typography = AppTypography,
shapes = AppShapes
) {
// M3 content
}
Couleur
Le système de couleurs de M3 est très différent de celui de M2. Le nombre de paramètres colorimétriques a augmenté, leurs noms sont différents et ils sont mappés différemment aux composants M3. Dans Compose, cela s'applique à la classe M2 Colors
, à la classe M3 ColorScheme
et aux fonctions associées :
M2
import androidx.compose.material.lightColors
import androidx.compose.material.darkColors
val AppLightColors = lightColors(
// M2 light Color parameters
)
val AppDarkColors = darkColors(
// M2 dark Color parameters
)
val AppColors = if (darkTheme) {
AppDarkColors
} else {
AppLightColors
}
M3
import androidx.compose.material3.lightColorScheme
import androidx.compose.material3.darkColorScheme
val AppLightColorScheme = lightColorScheme(
// M3 light Color parameters
)
val AppDarkColorScheme = darkColorScheme(
// M3 dark Color parameters
)
val AppColorScheme = if (darkTheme) {
AppDarkColorScheme
} else {
AppLightColorScheme
}
Compte tenu des différences importantes entre les systèmes de couleurs M2 et M3, il n'existe pas de mappage raisonnable pour les paramètres Color
. Utilisez plutôt l'outil Material Theme Builder pour générer un jeu de couleurs M3. Utilisez les couleurs M2 comme couleurs source principales dans l'outil, que celui-ci développe en palettes tonales utilisées par le jeu de couleurs M3. Les mappages suivants sont recommandés pour commencer:
M2 | Material Theme Builder |
---|---|
primary |
Principal |
primaryVariant |
Version secondaire |
secondary |
Tertiaire |
surface ou background |
Neutre |
Vous pouvez copier les codes hexadécimaux des couleurs pour les thèmes clair et sombre à partir de l'outil et les utiliser pour implémenter une instance ColorScheme M3. Vous pouvez également utiliser Material Theme Builder pour exporter le code Compose.
isLight
Contrairement à la classe M2 Colors
, la classe M3 ColorScheme
n'inclut pas de paramètre isLight
. En général, vous devez essayer de modéliser les éléments qui ont besoin de ces informations au niveau du thème. Par exemple :
M2
import androidx.compose.material.lightColors
import androidx.compose.material.darkColors
import androidx.compose.material.MaterialTheme
@Composable
private fun AppTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit
) {
val colors = if (darkTheme) darkColors(…) else lightColors(…)
MaterialTheme(
colors = colors,
content = content
)
}
@Composable
fun AppComposable() {
AppTheme {
val cardElevation = if (MaterialTheme.colors.isLight) 0.dp else 4.dp
…
}
}
M3
import androidx.compose.material3.lightColorScheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.MaterialTheme
val LocalCardElevation = staticCompositionLocalOf { Dp.Unspecified }
@Composable
private fun AppTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit
) {
val cardElevation = if (darkTheme) 4.dp else 0.dp
CompositionLocalProvider(LocalCardElevation provides cardElevation) {
val colorScheme = if (darkTheme) darkColorScheme(…) else lightColorScheme(…)
MaterialTheme(
colorScheme = colorScheme,
content = content
)
}
}
@Composable
fun AppComposable() {
AppTheme {
val cardElevation = LocalCardElevation.current
…
}
}
Pour en savoir plus, consultez le guide sur la conception de systèmes personnalisés dans Compose.
Couleurs dynamiques
La couleur dynamique est une nouvelle fonctionnalité de M3. Au lieu d'utiliser des couleurs personnalisées, un ColorScheme
M3 peut utiliser les couleurs de fond d'écran de l'appareil sur Android 12 ou version ultérieure avec les fonctions suivantes :
Typographie
Le système typographique de M3 est différent de celui de M2. Le nombre de paramètres typographiques est à peu près le même, mais leurs noms sont différents et ils sont mappés différemment aux composants M3. Dans Compose, cela s'applique à la classe M2 Typography
et à la classe M3 Typography
:
M2
import androidx.compose.material.Typography
val AppTypography = Typography(
// M2 TextStyle parameters
)
M3
import androidx.compose.material3.Typography
val AppTypography = Typography(
// M3 TextStyle parameters
)
Les mappages de paramètres TextStyle
suivants sont recommandés pour commencer :
M2 | M3 |
---|---|
h1 |
displayLarge |
h2 |
displayMedium |
h3 |
displaySmall |
N/A | headlineLarge |
h4 |
headlineMedium |
h5 |
headlineSmall |
h6 |
titleLarge |
subtitle1 |
titleMedium |
subtitle2 |
titleSmall |
body1 |
bodyLarge |
body2 |
bodyMedium |
caption |
bodySmall |
button |
labelLarge |
N/A | labelMedium |
overline |
labelSmall |
Forme
Le système de formes de M3 est différent de celui de M2. Le nombre de paramètres de forme a augmenté, leurs noms sont différents et ils sont mappés différemment aux composants M3. Dans Compose, cela s'applique à la classe M2 Shapes
et à la classe M3 Shapes
:
M2
import androidx.compose.material.Shapes
val AppShapes = Shapes(
// M2 Shape parameters
)
M3
import androidx.compose.material3.Shapes
val AppShapes = Shapes(
// M3 Shape parameters
)
Les mappages de paramètres Shape
suivants sont recommandés pour commencer :
M2 | M3 |
---|---|
N/A | extraSmall |
small |
small |
medium |
medium |
large |
large |
N/A | extraLarge |
Composants et mises en page
La plupart des composants et mises en page de M2 sont disponibles dans M3. Toutefois, il en manque certains, et M3 en propose de nouveaux qui n'existent pas dans M2. De plus, certains composants M3 présentent plus de variantes que leurs équivalents dans M2. En général, les surfaces d'API M3 tentent d'être aussi semblables que possible à leurs équivalents les plus proches dans M2.
Compte tenu des nouveaux systèmes de couleurs, de typographie et de forme, les composants M3 ont tendance à être mappés différemment aux nouvelles valeurs de thème. Nous vous recommandons de consulter le répertoire des jetons dans le code source de Compose Material 3 pour obtenir des informations fiables sur ces mappages.
Bien que certains composants nécessitent une attention particulière, les mappages de fonctions suivants sont recommandés pour commencer :
API manquantes :
M2 | M3 |
---|---|
androidx.compose.material.swipeable |
Pas encore disponible |
API remplacées:
M2 | M3 |
---|---|
androidx.compose.material.BackdropScaffold |
Aucun équivalent M3, migrer vers Scaffold ou BottomSheetScaffold à la place |
androidx.compose.material.BottomDrawer |
Aucun équivalent M3, migrer vers ModalBottomSheet à la place |
API renommées :
Toutes les autres API :
Consultez les derniers composants et mises en page de M3 dans la présentation de référence des API Compose Material 3. Consultez également régulièrement la page des versions pour connaître les nouvelles API et les mises à jour.
Échafaudages, snackbars et panneau de navigation
Les échafaudages de M3 sont différents de ceux de M2. Dans M2 et M3, le composable Mise en page principal est appelé Scaffold
, mais les packages et paramètres d'importation diffèrent :
M2
import androidx.compose.material.Scaffold
Scaffold(
// M2 scaffold parameters
)
M3
import androidx.compose.material3.Scaffold
Scaffold(
// M3 scaffold parameters
)
Le Scaffold
M2 contient un paramètre backgroundColor
désormais appelé containerColor
dans le Scaffold
M3 :
M2
import androidx.compose.material.Scaffold
Scaffold(
backgroundColor = …,
content = { … }
)
M3
import androidx.compose.material3.Scaffold
Scaffold(
containerColor = …,
content = { … }
)
La classe M2 ScaffoldState
n'existe plus dans M3, car elle contient un paramètre drawerState
qui n'est plus nécessaire. Pour afficher des snackbars avec le Scaffold
M3, utilisez plutôt SnackbarHostState
:
M2
import androidx.compose.material.Scaffold
import androidx.compose.material.rememberScaffoldState
val scaffoldState = rememberScaffoldState()
val scope = rememberCoroutineScope()
Scaffold(
scaffoldState = scaffoldState,
content = {
…
scope.launch {
scaffoldState.snackbarHostState.showSnackbar(…)
}
}
)
M3
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
val snackbarHostState = remember { SnackbarHostState() }
val scope = rememberCoroutineScope()
Scaffold(
snackbarHost = { SnackbarHost(snackbarHostState) },
content = {
…
scope.launch {
snackbarHostState.showSnackbar(…)
}
}
)
Tous les paramètres drawer*
du Scaffold
M2 ont été supprimés du Scaffold
M3 (par exemple, drawerShape
et drawerContent
). Pour afficher un panneau contenant l'Scaffold
M3, utilisez plutôt un composable Panneau de navigation, par exemple ModalNavigationDrawer
:
M2
import androidx.compose.material.DrawerValue
import
import androidx.compose.material.Scaffold
import androidx.compose.material.rememberDrawerState
import androidx.compose.material.rememberScaffoldState
val scaffoldState = rememberScaffoldState(
drawerState = rememberDrawerState(DrawerValue.Closed)
)
val scope = rememberCoroutineScope()
Scaffold(
scaffoldState = scaffoldState,
drawerContent = { … },
drawerGesturesEnabled = …,
drawerShape = …,
drawerElevation = …,
drawerBackgroundColor = …,
drawerContentColor = …,
drawerScrimColor = …,
content = {
…
scope.launch {
scaffoldState.drawerState.open()
}
}
)
M3
import androidx.compose.material3.DrawerValue
import androidx.compose.material3.ModalDrawerSheet
import androidx.compose.material3.ModalNavigationDrawer
import androidx.compose.material3.Scaffold
import androidx.compose.material3.rememberDrawerState
val drawerState = rememberDrawerState(DrawerValue.Closed)
val scope = rememberCoroutineScope()
ModalNavigationDrawer(
drawerState = drawerState,
drawerContent = {
ModalDrawerSheet(
drawerShape = …,
drawerTonalElevation = …,
drawerContainerColor = …,
drawerContentColor = …,
content = { … }
)
},
gesturesEnabled = …,
scrimColor = …,
content = {
Scaffold(
content = {
…
scope.launch {
drawerState.open()
}
}
)
}
)
Barre d'application supérieure
Les barres d'application supérieures de M3 sont différentes de celles de M2. Dans M2 et M3, le composable Barre d'application supérieure est appelé TopAppBar
, mais les packages et paramètres d'importation diffèrent :
M2
import androidx.compose.material.TopAppBar
TopAppBar(…)
M3
import androidx.compose.material3.TopAppBar
TopAppBar(…)
Envisagez d'utiliser la propriété CenterAlignedTopAppBar
de M3 si vous centriez auparavant le contenu dans l'élément TopAppBar
de M2. Il est également important de connaître MediumTopAppBar
et LargeTopAppBar
.
Les barres d'application supérieures de M3 contiennent un nouveau paramètre scrollBehavior
, qui permet de fournir différentes fonctionnalités lors du défilement via la classe TopAppBarScrollBehavior
, comme la modification de l'altitude. Elles fonctionnent conjointement avec le défilement de contenu via Modifer.nestedScroll
. Ce fonctionnement était possible dans l'élément TopAppBar
de M2 en modifiant manuellement le paramètre elevation
:
M2
import androidx.compose.material.AppBarDefaults
import androidx.compose.material.Scaffold
import androidx.compose.material.TopAppBar
val state = rememberLazyListState()
val isAtTop by remember {
derivedStateOf {
state.firstVisibleItemIndex == 0 && state.firstVisibleItemScrollOffset == 0
}
}
Scaffold(
topBar = {
TopAppBar(
elevation = if (isAtTop) {
0.dp
} else {
AppBarDefaults.TopAppBarElevation
},
…
)
},
content = {
LazyColumn(state = state) { … }
}
)
M3
import androidx.compose.material3.Scaffold
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()
Scaffold(
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
topBar = {
TopAppBar(
scrollBehavior = scrollBehavior,
…
)
},
content = {
LazyColumn { … }
}
)
Barre de navigation inférieure/Barre de navigation
La barre de navigation inférieure dans M2 a été renommée barre de navigation dans M3. M2 contient les composables BottomNavigation
et BottomNavigationItem
, tandis que dans M3 contient les composables NavigationBar
et NavigationBarItem
:
M2
import androidx.compose.material.BottomNavigation
import androidx.compose.material.BottomNavigationItem
BottomNavigation {
BottomNavigationItem(…)
BottomNavigationItem(…)
BottomNavigationItem(…)
}
M3
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
NavigationBar {
NavigationBarItem(…)
NavigationBarItem(…)
NavigationBarItem(…)
}
Boutons, boutons en forme d'icône et boutons d'action flottants
Les boutons, les boutons en forme d'icône et les boutons d'action flottants de M3 sont différents de ceux de M2. M3 inclut tous les composables Bouton de M2 :
M2
import androidx.compose.material.Button
import androidx.compose.material.ExtendedFloatingActionButton
import androidx.compose.material.FloatingActionButton
import androidx.compose.material.IconButton
import androidx.compose.material.IconToggleButton
import androidx.compose.material.OutlinedButton
import androidx.compose.material.TextButton
// M2 buttons
Button(…)
OutlinedButton(…)
TextButton(…)
// M2 icon buttons
IconButton(…)
IconToggleButton(…)
// M2 FABs
FloatingActionButton(…)
ExtendedFloatingActionButton(…)
M3
import androidx.compose.material3.Button
import androidx.compose.material3.ExtendedFloatingActionButton
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.IconButton
import androidx.compose.material3.IconToggleButton
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.TextButton
// M3 buttons
Button(…)
OutlinedButton(…)
TextButton(…)
// M3 icon buttons
IconButton(…)
IconToggleButton(…)
// M3 FABs
FloatingActionButton(…)
ExtendedFloatingActionButton(…)
M3 inclut également de nouvelles variantes des boutons. Vous en trouverez une description dans la présentation de la documentation de référence de l'API Compose Material 3.
Changer
Les boutons bascules de M3 sont différents de ceux de M2. Dans M2 et M3, le composable Bouton bascule est appelé Switch
, mais les packages d'importation diffèrent :
M2
import androidx.compose.material.Switch
Switch(…)
M3
import androidx.compose.material3.Switch
Switch(…)
Surfaces et altitude
Les systèmes de surface et d'altitude dans M3 sont différents de ceux dans M2. Il existe deux types d'altitude dans M3 :
- Altitude de l'ombre (projette une ombre, comme dans M2)
- Altitude tonale (superpose une couleur, nouveauté dans M3)
Dans Compose, cela s'applique à la fonction Surface
M2 et à la fonction Surface
M3 :
M2
import androidx.compose.material.Surface
Surface(
elevation = …
) { … }
M3
import androidx.compose.material3.Surface
Surface(
shadowElevation = …,
tonalElevation = …
) { … }
Vous pouvez utiliser les valeurs elevation
Dp
dans M2 pour shadowElevation
et/ou tonalElevation
dans M3, en fonction des préférences de conception pour l'expérience/Interface utilisateur.
Surface
est le composable de sauvegarde derrière la plupart des composants. Par conséquent, les composables de composants peuvent également révéler des paramètres d'altitude dont vous devez effectuer la migration de la même manière.
L'élévation tonale dans M3 remplace le concept de superpositions d'élévation dans les thèmes sombres de M2. Par conséquent, ElevationOverlay
et LocalElevationOverlay
n'existent pas dans M3, et LocalAbsoluteElevation
dans M2 est passé à LocalAbsoluteTonalElevation
dans M3.
Mise en valeur et valeur alpha du contenu
La mise en valeur dans M3 est très différente de celle dans M2. Dans M2, la mise en valeur impliquait l'utilisation de couleurs activées avec certaines valeurs alpha pour différencier le contenu comme le texte et les icônes. Dans M3, il existe désormais deux approches différentes :
- Utilisation des couleurs on avec leurs couleurs variant on tirées du système de couleurs M3 étendu.
- Utilisez différentes épaisseurs de police pour le texte.
Par conséquent, ContentAlpha
et LocalContentAlpha
n'existent pas dans M3 et doivent être remplacés.
Les mappages suivants sont recommandés pour commencer :
M2 | M3 |
---|---|
onSurface avec ContentAlpha.high |
onSurface en général, FontWeight.Medium – FontWeight.Black pour le texte |
onSurface avec ContentAlpha.medium |
onSurfaceVariant en général, FontWeight.Thin – FontWeight.Normal pour le texte |
onSurface avec ContentAlpha.disabled |
onSurface.copy(alpha = 0.38f) |
Voici un exemple de mise en valeur d'une icône dans M2 et dans M3:
M2
import androidx.compose.material.ContentAlpha
import androidx.compose.material.LocalContentAlpha
// High emphasis
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.high) {
Icon(…)
}
// Medium emphasis
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
Icon(…)
}
// Disabled emphasis
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.disabled) {
Icon(…)
}
M3
import androidx.compose.material3.LocalContentColor
// High emphasis
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onSurface) {
Icon(…)
}
// Medium emphasis
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onSurfaceVariant) {
Icon(…)
}
// Disabled emphasis
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f)) {
Icon(…)
}
Voici des exemples d'accentuation du texte dans les M2 et M3:
M2
import androidx.compose.material.ContentAlpha
import androidx.compose.material.LocalContentAlpha
// High emphasis
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.high) {
Text(…)
}
// Medium emphasis
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
Text(…)
}
// Disabled emphasis
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.disabled) {
Text(…)
}
M3
import androidx.compose.material3.LocalContentColor
// High emphasis
Text(
…,
fontWeight = FontWeight.Bold
)
// Medium emphasis
Text(
…,
fontWeight = FontWeight.Normal
)
// Disabled emphasis
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f)) {
Text(
…,
fontWeight = FontWeight.Normal
)
}
Arrière-plans et conteneurs
Les arrière-plans dans M2 sont appelés conteneurs dans M3. En général, vous pouvez remplacer les paramètres background*
dans M2 par container*
dans M3, en utilisant les mêmes valeurs.
Par exemple :
M2
Badge(
backgroundColor = MaterialTheme.colors.primary
) { … }
M3
Badge(
containerColor = MaterialTheme.colorScheme.primary
) { … }
Liens utiles
Pour en savoir plus sur la migration de M2 vers M3 dans Compose, consultez les ressources supplémentaires suivantes.
Docs
Exemples d'applications
- Exemple d'application de réponse M3
- Exemple de migration de l'application Jetchat de M2 vers M3
- Exemple de migration de l'application Jetnews de M2 vers M3
- Nouveautés dans l'application principale pour Android M3 : module "core-designsystem"
Vidéos
Documentation de référence de l'API et code source
- Documentation de référence de l'API Compose Material 3
- Exemples de Compose Material 3 dans le code source
Recommandations personnalisées
- Remarque : Le texte du lien s'affiche lorsque JavaScript est désactivé
- Material Design 2 dans Compose
- Material Design 3 dans Compose
- Systèmes de conception personnalisés dans Compose