Exemples avec des styles

La documentation suivante contient des exemples d'utilisation des styles pour créer certains types de composants.

Boutons

Les styles peuvent être utilisés pour créer de nombreux types de boutons différents qui peuvent être différents des composants Material standards.

Bouton de base

Prenons l'exemple du bouton suivant, défini comme base pour différents types de boutons :

@Composable
fun BaseButton(
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
    style: Style = Style,
    enabled: Boolean = true,
    interactionSource: MutableInteractionSource? = null,
    content: @Composable RowScope.() -> Unit
) {
    val effectiveInteractionSource = interactionSource ?: remember {
        MutableInteractionSource()
    }
    val styleState = remember(effectiveInteractionSource) {
        MutableStyleState(effectiveInteractionSource)
    }
    styleState.isEnabled = enabled
    Row(
        modifier = modifier
            .semantics(properties = {
                role = Role.Button
            })
            .clickable(
                enabled = enabled,
                onClick = onClick,
                interactionSource = effectiveInteractionSource,
                indication = null,
            )
            .styleable(styleState, baseButtonStyle, style),
        content = content,
        verticalAlignment = Alignment.CenterVertically
    )
}

Bouton de traduction avec arrière-plan au survol

Pour définir un bouton avec un arrière-plan qui se déplace au passage de la souris, utilisez le code suivant :

Figure 1. Bouton avec un arrière-plan qui se traduit au passage de la souris.

@Preview
@Composable
fun HoverButtonExample() {
    Box(
        modifier = Modifier.padding(32.dp),
        contentAlignment = Alignment.Center
    ) {
        BaseButton(
            onClick = {},
            style = Style {
                background(Color.Transparent)
                shape(RoundedCornerShape(0.dp))
                border(1.dp, Color.Black)
                contentColor(Color.Black)
                fontSize(16.sp)
                fontWeight(FontWeight.Light)
                letterSpacing(1.sp)
                contentPadding(vertical = 13.dp, horizontal = 20.dp)
                dropShadow(
                    Shadow(
                        spread = 0.dp, color = Color(0xFFFFE54C),
                        radius = 0.dp,
                        offset = DpOffset(7.dp, 7.dp)
                    )
                )
                hovered {
                    animate(tween(200)) {
                        dropShadow(
                            Shadow(
                                spread = 0.dp, color = Color(0xFFFFE54C),
                                radius = 0.dp,
                                offset = DpOffset(0.dp, 0.dp)
                            )
                        )
                    }
                }
                pressed {
                    animate(tween(200)) {
                        dropShadow(
                            Shadow(
                                spread = 0.dp, color = Color(0xFFFFE54C),
                                radius = 0.dp,
                                offset = DpOffset(0.dp, 0.dp)
                            )
                        )
                    }
                }
            }
        ) {
            BaseText("Button 52")
        }
    }
}

Bouton de profondeur arrondi avec animation d'ombre

Pour créer un bouton avec un effet de profondeur qui déplace l'ombre vers le haut et vers le bas sur pressed, procédez comme suit :

Figure 2. Bouton avec effet de profondeur, qui déplace la couche d'ombre lorsqu'on appuie dessus.

@Preview
@Composable
fun ShadowAnimationButton() {
    Box(modifier = Modifier.padding(32.dp)) {
        val density = LocalDensity.current
        val buttonStyle = Style {
            background(Color(0xFFFBEED0))
            border(2.dp, Color(0xFF422800))
            shape(RoundedCornerShape(30.dp))
            dropShadow(
                Shadow(
                    color = Color(0xFF422800), offset = DpOffset(4.dp, 4.dp),
                    radius = 0.dp, spread = 0.dp
                )
            )
            contentColor(Color(0xFF422800))
            fontWeight(FontWeight.SemiBold)
            fontSize(18.sp)
            contentPaddingHorizontal(25.dp)
            externalPadding(8.dp)
            height(50.dp)
            textAlign(TextAlign.Center)
            hovered {
                animate {
                    background(Color.White)
                }
            }
            pressed {
                animate {
                    dropShadow(
                        Shadow(
                            color = Color(0xFF422800),
                            offset = DpOffset(2.dp, 2.dp),
                            radius = 0.dp,
                            spread = 0.dp
                        )
                    )
                    translation(with(density) { 2.dp.toPx() }, with(density) { 2.dp.toPx() })
                }
            }
        }
        BaseButton(
            onClick = {},
            style = buttonStyle
        ) {
            BaseText("Button 74")
        }
    }
}

Plusieurs styles superposés avec animation de pression

Le code suivant crée un bouton avec un effet de profondeur enfoncé avec Styles, qui comporte plusieurs couches de styles, qui utilisent toutes le même StyleState :

Figure 3. Bouton avec effet de profondeur et plusieurs calques de style.

@Preview
@Composable
fun MultipleStylesButton() {
    val interactionSource = remember { MutableInteractionSource() }
    val styleState = remember(interactionSource) { MutableStyleState(interactionSource) }
    val density = LocalDensity.current

    Box(
        modifier = Modifier
            .styleable(styleState) {
                size(200.dp, 48.dp)
                externalPadding(32.dp)
            }
            .clickable(interactionSource, indication = null) {},
        contentAlignment = Alignment.Center
    ) {
        val edgeStyle = Style {
            fillSize()
            shape(RoundedCornerShape(16.dp))
            background(Color(0xFF1CB0F6))
        }

        val frontStyle = Style {
            fillSize()
            background(Color(0xFF1899D6))
            shape(RoundedCornerShape(16.dp))
            contentPadding(vertical = 12.dp, horizontal = 16.dp)
            translationY(with(density) { (-4).dp.toPx() })
            pressed {
                animate {
                    translationY(with(density) { (0).dp.toPx() })
                }
            }
        }
        Box(modifier = Modifier.semantics(properties = {
            role = Role.Button
        }).styleable(styleState, edgeStyle)) {
            Box(
                modifier = Modifier
                    .styleable(styleState, frontStyle),
                contentAlignment = Alignment.Center
            ) {
                BaseText(
                    "Button 19".toUpperCase(Locale.current),
                    style = Style {
                        contentColor(Color.White)
                        fontSize(15.sp)
                        fontWeight(FontWeight.Bold)
                        letterSpacing(0.8.sp)
                    }
                )
            }
        }
    }
}

Bouton d'effet de lueur dégradée

Pour obtenir un effet de lueur dégradée qui s'anime à l'infini, vous pouvez utiliser des styles en combinaison avec rememberInfiniteTransition, comme suit :

Figure 4. Bouton d'effet de lueur de dégradé.

@Preview
@Composable
fun GradientGlowButtonExample() {
    val infiniteTransition = rememberInfiniteTransition(label = "glowing_button_85_animation")
    val animatedProgress by infiniteTransition.animateFloat(
        initialValue = 0f,
        targetValue = 1f,
        animationSpec = infiniteRepeatable(
            animation = tween(durationMillis = 20000, easing = LinearEasing),
        ), label = "progress"
    )

    val gradientColors = listOf(
        Color(0xffff0000), Color(0xffff7300), Color(0xfffffb00), Color(0xff48ff00),
        Color(0xff00ffd5), Color(0xff002bff), Color(0xff7a00ff), Color(0xffff00c8),
        Color(0xffff0000)
    )

    val glowingBrush = remember(animatedProgress) {
        object : ShaderBrush() {
            override fun createShader(size: Size): Shader {
                val width = size.width * 4
                val brushSize = width * animatedProgress
                return LinearGradientShader(
                    colors = gradientColors,
                    from = Offset(brushSize, 0f),
                    to = Offset(brushSize + width, 0f),
                    tileMode = TileMode.Repeated
                )
            }
        }
    }


    Box(
        modifier = Modifier
            .padding(32.dp),
        contentAlignment = Alignment.Center
    ) {
        BaseButton(
            onClick = { },
            style = Style {
                dropShadow(
                    Shadow(
                        brush = glowingBrush,
                        radius = 5.dp
                    )
                )
                transformOrigin(TransformOrigin.Center)
                pressed {
                    animate {
                        dropShadow(
                            Shadow(
                                brush = glowingBrush,
                                radius = 10.dp
                            )
                        )
                        scale(0.95f)
                    }

                }
                size(width = 200.dp, height = 50.dp)
                background(Color(0xFF111111))
                shape(RoundedCornerShape(10.dp))
                contentColor(Color.White)
                contentPadding(vertical = (0.6f * 14).dp, horizontal = (2f * 14).dp)
                border(width = 0.dp, color = Color.Transparent)
            }
        ) {
            BaseText(text = "Button 85")
        }
    }
}