L'indicateur de rafraîchissement "Liquid Pull To Refresh" apporte une dimension organique et fluide aux interfaces Flutter. Contrairement aux indicateurs standards, il utilise des simulations physiques de fluides et des animations de chemins personnalisés pour transformer un geste de balayage banal en une expérience utilisateur interactive.
Architecture et gestion des états
Le fonctionnement d'un tel composant repose sur une machine à états rigoureuse. Le cycle de vie du rafraîchissement est segmenté en plusieurs phases distinctes afin de coordonner les animations visuelles avec les données de défilement.
enum FluidRefreshStatus {
idle, // Repos
dragging, // Traction en cours
armed, // Seuil de déclenchement atteint
snapping, // Transition vers la position de chargement
loading, // Traitement asynchrone (onRefresh)
finished, // Animation de fin
aborted // Annulation par l'utilisateur
}
Au sein de la classe d'état, plusieurs instances de AnimationController sont nécessaires pour piloter indépendamment l'oscillation de la vague, la rotation de l'icône de progression et l'opacité du conteneur.
Modélisation de l'effet liquide via CustomClipper
L'aspect visuel de "liquide" est généré par un découpage dynamique de la surface. On utilise la classe CustomClipper<Path> pour dessiner une courbe de Bézier dont la tension et la hauteur varient en fonction de la profondeur du glissement (offset).
class WaveShapeClipper extends CustomClipper<Path> {
final double fluidHeight;
final double curveIntensity;
WaveShapeClipper({required this.fluidHeight, required this.curveIntensity});
@override
Path getClip(Size size) {
final path = Path();
path.lineTo(0.0, fluidHeight);
// Création de la courbure parabolique
final controlPoint = Offset(size.width / 2, fluidHeight + curveIntensity);
final endPoint = Offset(size.width, fluidHeight);
path.quadraticBezierTo(controlPoint.dx, controlPoint.dy, endPoint.dx, endPoint.dy);
path.lineTo(size.width, 0.0);
path.close();
return path;
}
@override
bool shouldReclip(WaveShapeClipper oldClipper) =>
oldClipper.fluidHeight != fluidHeight || oldClipper.curveIntensity != curveIntensity;
}
Intégration pratique dans un projet
Pour implémenter ce composant, il suffit d'envelopper une vue défilable (comme ListView ou GridView). Le paramètre onRefresh doit retourner un Future pour signaler la fin du chargement.
LiquidPullToRefresh(
color: Colors.blueAccent,
backgroundColor: Colors.white,
height: 100,
onRefresh: () async {
await Future.delayed(Duration(seconds: 3));
// Logique de mise à jour des données
},
child: ListView.separated(
itemCount: 15,
separatorBuilder: (context, index) => Divider(),
itemBuilder: (context, index) => ListTile(
title: Text("Entrée de donnée n°$index"),
),
),
)
Guide de réécriture : Créer son propre système
Pour recréer ce comportement de zéro, il est essentiel de capturer les événements de défilement sans interférer avec le comportement natif de la liste.
1. Capture des notifications de défilement
Utilisez un NotificationListener de type ScrollNotification pour calculer le ratio d'étirement.
NotificationListener<ScrollNotification>(
onNotification: (notification) {
if (notification is ScrollUpdateNotification) {
// Calculer l'extension au-delà des limites (overscroll)
double dragOffset = notification.metrics.pixels;
if (dragOffset < 0) {
_updateAnimationValue(dragOffset.abs());
}
}
return false;
},
child: myListView,
)
2. Synchronisation de l'animation de rebond
Une fois que l'utilisateur relâche la pression, si le seuil est atteint, le composant doit passer en mode snapping. On utilise souvent CurvedAnimation avec Curves.elasticOut pour simuler la tension de la surface du liquide qui reprend sa place.
Paramètres de personnalisation avancés
La flexibilité du composant permet d'ajuster l'esthétique selon la charte graphique de l'application :
springAnimationDurationInMilliseconds: Contrôle la nervosité du retour élastique.borderWidth: Définit l'épaisseur de l'anneau de progression central.showChildOpacityTransition: Active ou désactive le fondu de la liste pendant l'étirement.
En combinant des Path dynamiques et des contrôleurs de timing précis, Flutter permet de transformer des cmoposants UI utilitaires en éléments de design sophistiqués.