Mettre un dégradé dans du texte en Flutter
Dans les nombreuses idées que j’ai pour mon blog, voici une série que j’avais envie de pousser depuis un moment : “Dessine-moi un widget”. L’idée est très simple : je trouve une maquette sur internet avec un widget potentiellement intéressant, puis je l’implémente et explique la technique. Quand je dis intéressant, c’est un widget sur lequel, en voyant la maquette, tu te dis : “tiens, mais comment ferais-je ça ?”.
Pour ce premier article de la série, je vais reprendre la question qu’un ami m’avait posée lorsqu’il débutait en Flutter : “Comment faire un dégradé dans un texte ?”. Cette question permet d’effleurer des thématiques un peu avancées dans Flutter ce qui en fait, je trouve, un bon premier sujet. Dans cet article nous allons refaire l’effet du texte “ama.” de cette maquette trouvée sur dribbble.
Pour commencer, pour mettre un dégradé sur un Container
, il existe l’attribut gradient
sur l’objet BoxDecoration
qui vous permet de faire cela facilement.
Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topRight,
end: Alignment.bottomLeft,
colors: [
Colors.blue,
Colors.red,
],
)
),
child: // [...]
)
Mais malheureusement, il n’y a pas d’équivalent sur le widget Text
en Flutter. Il va donc falloir trouver une solution.
Introduction au ShaderMask
Pour pouvoir faire ce que nous souhaitons, je dois vous introduire un widget trop peu connu en Flutter, mais qui permet d’obtenir toutes sortes de résultats assez impressionnants: ShaderMask
.
ShaderMask
est un widget qui applique un masque généré par un shader à son enfant. — documentation officielle
Vous ne comprennez pas cette définition ? Ce n’est pas grave, nous allons tenter de l’expliquer.
Un Shader
est un code exécuté par votre GPU. Dans l’immense majorité des cas, les shaders sont utilisés pour calculer le rendu des pixels affichés à votre écran. C’est quelque chose qui est très utilisé dans les jeux-vidéos mais aussi par Flutter, sans que vous ne vous en rendiez compte.
Un Masque est un calque qui permet de définir quelle partie de l’image sera affichée et quelle partie sera masquée, comme sur l’exemple ci-dessous.
Pour résumer, un ShaderMask
va permet d’appliquer le résultat d’un shader sur ses enfants dans l’arbre.
Il est important de comprendre le rôle des différents paramètres du ShaderMask
pour pouvoir s’en servir pleinement :
blendMode
: Permet de choisir comment nous allons peindre sur la surface. Il existe beaucoup deblendMode
possibles. Celui qui nous interesse estBlendMode.srcin
qui permet d’afficher l’image source (ici le shader) et de se servir de l’image destination (ici les enfants) comme masque. Pour voir tous les modes disponibles, jetez un coup d’oeil à la documentation.shaderCallback
: LeshaderCallback
est l’endroit où l’on va construire notre shader pour pouvoir s’en servir. Si j’avais design cette api, je l’aurais d’ailleurs probablement nomméeshaderBuilder
.child
: Si vous êtes fluent en Flutter, vous connaissez le paramètre, mais pour les autres, c’est ici que l’on va mettre le widget enfant dans l’arbre de widget. C’est la destination de notre shader.
Les Gradients
Il nous reste maintenant à trouver un moyen de faire un Shader de dégradé, d’assembler le tout, et le tour sera joué. En Flutter, il existe trois dégradés : LinearGradient
, RadialGradient
et SweepGradient
.
Ces trois dégradés héritent de la classe abstraite Gradient
qui dispose d’une méthode Gradient.createShader()
. La méthode prend en paramètre un rectangle correspondant à la zone d’affichage du shader. Attention de bien le mettre à la taille du widget cible et pas à la taille de l’écran. Cela donnerait un résultat décalé par rapport au résultat attendu.
ShaderMask + Gradient + Text = GradientText
Maintenant que nous avons trouvé les ingrédients pour notre recette de cuisine, il est temps de les assembler !
import 'package:flutter/material.dart';
class GradientText extends StatelessWidget {
final String data;
final TextStyle? style;
final Gradient gradient;
final TextAlign? textAlign;
const GradientText(this.data, {
super.key,
this.style,
this.textAlign,
required this.gradient
});
@override
Widget build(BuildContext context) {
return ShaderMask(
blendMode: BlendMode.srcIn,
shaderCallback: (bounds) => gradient.createShader(
Rect.fromLTWH(0, 0, bounds.width, bounds.height),
),
child: Text(data, style: style, textAlign: textAlign),
);
}
}
Et maintenant, il ne nous reste plus qu’à l’utiliser avec un joli dégradé multicolore ! 🌈
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: GradientText("ama.",
textAlign: TextAlign.center,
style: GoogleFonts.rubik(
fontWeight: FontWeight.w700,
fontSize: 70
),
gradient: const SweepGradient(colors: [
Colors.red,
Colors.pink,
Colors.purple,
Colors.deepPurple,
Colors.deepPurple,
Colors.indigo,
Colors.blue,
Colors.lightBlue,
Colors.cyan,
Colors.teal,
Colors.green,
Colors.lightGreen,
Colors.lime,
Colors.yellow,
Colors.amber,
Colors.orange,
Colors.deepOrange,
])),
),
);
}
}
Et voilà, vous savez maintenant appliquer un dégradé dans du texte. Mais si vous avez bien compris la logique, il est possible d’appliquer des dégradés sur tous les éléments graphiques existants en Flutter ou même de créer d’autres types de Shaders. Maintenant que vous êtes arrivés au bout, je peux vous le dire, cet article était surtout une manière d’introduire l’utilisation des Shaders en Flutter et d’éveiller votre curiosité quant aux posibilités offertes par cela. Prenez le temps de jouer avec le ShaderMask et les Shaders, vous verrez que l’on peut faire des choses folles !