Gad Ntenta
Développeur Full Stack
Animations Avancées avec Flutter
Maîtrisez les animations complexes dans Flutter pour créer des expériences utilisateur captivantes.
Animations Avancées avec Flutter
Introduction
Les animations sont un élément crucial pour créer des expériences utilisateur engageantes dans les applications Flutter. Dans cet article, nous allons explorer les techniques avancées d'animation et comment les implémenter efficacement.
Types d'Animations
1. Animations Implicites
Les animations implicites sont les plus simples à implémenter :
class MonWidgetAnime extends StatefulWidget {
@override
_MonWidgetAnimeState createState() => _MonWidgetAnimeState();
}
class _MonWidgetAnimeState extends State<MonWidgetAnime> {
bool _isExpanded = false;
@override
Widget build(BuildContext context) {
return AnimatedContainer(
duration: Duration(seconds: 1),
curve: Curves.easeInOut,
width: _isExpanded ? 200 : 100,
height: _isExpanded ? 200 : 100,
color: _isExpanded ? Colors.blue : Colors.red,
child: GestureDetector(
onTap: () {
setState(() {
_isExpanded = !_isExpanded;
});
},
child: Center(
child: Text('Tapez-moi'),
),
),
);
}
}
2. Animations Explicites
Les animations explicites offrent plus de contrôle :
class AnimationExplicite extends StatefulWidget {
@override
_AnimationExpliciteState createState() => _AnimationExpliciteState();
}
class _AnimationExpliciteState extends State<AnimationExplicite>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: Duration(seconds: 2),
vsync: this,
);
_animation = Tween<double>(
begin: 0.0,
end: 1.0,
).animate(CurvedAnimation(
parent: _controller,
curve: Curves.easeInOut,
));
_controller.repeat(reverse: true);
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _animation,
builder: (context, child) {
return Transform.scale(
scale: 1.0 + _animation.value * 0.5,
child: Container(
width: 100,
height: 100,
color: Colors.blue,
),
);
},
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}
Animations Avancées
1. Animations Personnalisées
class AnimationPersonnalisee extends StatefulWidget {
@override
_AnimationPersonnaliseeState createState() => _AnimationPersonnaliseeState();
}
class _AnimationPersonnaliseeState extends State<AnimationPersonnalisee>
with TickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _rotationAnimation;
late Animation<double> _scaleAnimation;
late Animation<Color?> _colorAnimation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: Duration(seconds: 2),
vsync: this,
);
_rotationAnimation = Tween<double>(
begin: 0,
end: 2 * pi,
).animate(CurvedAnimation(
parent: _controller,
curve: Curves.easeInOut,
));
_scaleAnimation = Tween<double>(
begin: 1.0,
end: 1.5,
).animate(CurvedAnimation(
parent: _controller,
curve: Curves.easeInOut,
));
_colorAnimation = ColorTween(
begin: Colors.blue,
end: Colors.red,
).animate(CurvedAnimation(
parent: _controller,
curve: Curves.easeInOut,
));
_controller.repeat(reverse: true);
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _controller,
builder: (context, child) {
return Transform.rotate(
angle: _rotationAnimation.value,
child: Transform.scale(
scale: _scaleAnimation.value,
child: Container(
width: 100,
height: 100,
color: _colorAnimation.value,
),
),
);
},
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}
2. Animations de Page
class PageTransition extends StatelessWidget {
@override
Widget build(BuildContext context) {
return PageRouteBuilder(
pageBuilder: (context, animation, secondaryAnimation) {
return SecondPage();
},
transitionsBuilder: (context, animation, secondaryAnimation, child) {
var begin = Offset(1.0, 0.0);
var end = Offset.zero;
var curve = Curves.easeInOut;
var tween = Tween(begin: begin, end: end).chain(
CurveTween(curve: curve),
);
var offsetAnimation = animation.drive(tween);
return SlideTransition(
position: offsetAnimation,
child: child,
);
},
);
}
}
3. Animations de Liste
class ListeAnimee extends StatelessWidget {
final List<String> items;
ListeAnimee({required this.items});
@override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return TweenAnimationBuilder(
tween: Tween<double>(begin: 0, end: 1),
duration: Duration(milliseconds: 500),
builder: (context, double value, child) {
return Transform.translate(
offset: Offset(0, 50 * (1 - value)),
child: Opacity(
opacity: value,
child: child,
),
);
},
child: ListTile(
title: Text(items[index]),
),
);
},
);
}
}
Meilleures Pratiques
1. Performance
- Utilisez
constpour les widgets statiques - Évitez les animations inutiles
- Utilisez
RepaintBoundarypour isoler les animations - Optimisez les rebuilds
2. Accessibilité
- Respectez les préférences d'animation
- Fournissez des alternatives
- Testez avec les lecteurs d'écran
- Assurez un contraste suffisant
3. UX
- Gardez les animations courtes
- Utilisez des courbes appropriées
- Donnez un retour visuel
- Évitez les animations distrayantes
Exemples Avancés
1. Animation de Carte
class CarteAnimee extends StatefulWidget {
@override
_CarteAnimeeState createState() => _CarteAnimeeState();
}
class _CarteAnimeeState extends State<CarteAnimee>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _elevationAnimation;
late Animation<double> _scaleAnimation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: Duration(milliseconds: 300),
vsync: this,
);
_elevationAnimation = Tween<double>(
begin: 2.0,
end: 8.0,
).animate(CurvedAnimation(
parent: _controller,
curve: Curves.easeInOut,
));
_scaleAnimation = Tween<double>(
begin: 1.0,
end: 1.05,
).animate(CurvedAnimation(
parent: _controller,
curve: Curves.easeInOut,
));
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTapDown: (_) => _controller.forward(),
onTapUp: (_) => _controller.reverse(),
onTapCancel: () => _controller.reverse(),
child: AnimatedBuilder(
animation: _controller,
builder: (context, child) {
return Transform.scale(
scale: _scaleAnimation.value,
child: Card(
elevation: _elevationAnimation.value,
child: Container(
width: 200,
height: 300,
child: Center(
child: Text('Carte Interactive'),
),
),
),
);
},
),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}
2. Animation de Chargement
class ChargementAnime extends StatefulWidget {
@override
_ChargementAnimeState createState() => _ChargementAnimeState();
}
class _ChargementAnimeState extends State<ChargementAnime>
with TickerProviderStateMixin {
late List<AnimationController> _controllers;
late List<Animation<double>> _animations;
@override
void initState() {
super.initState();
_controllers = List.generate(
3,
(index) => AnimationController(
duration: Duration(milliseconds: 600),
vsync: this,
),
);
_animations = _controllers.map((controller) {
return Tween<double>(
begin: 0.0,
end: 1.0,
).animate(CurvedAnimation(
parent: controller,
curve: Curves.easeInOut,
));
}).toList();
for (var i = 0; i < _controllers.length; i++) {
Future.delayed(Duration(milliseconds: i * 200), () {
if (mounted) {
_controllers[i].repeat(reverse: true);
}
});
}
}
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List.generate(3, (index) {
return AnimatedBuilder(
animation: _animations[index],
builder: (context, child) {
return Transform.scale(
scale: 0.5 + _animations[index].value * 0.5,
child: Container(
width: 20,
height: 20,
margin: EdgeInsets.symmetric(horizontal: 5),
decoration: BoxDecoration(
color: Colors.blue,
shape: BoxShape.circle,
),
),
);
},
);
}),
);
}
@override
void dispose() {
for (var controller in _controllers) {
controller.dispose();
}
super.dispose();
}
}
Conclusion
Les animations sont un outil puissant pour améliorer l'expérience utilisateur de vos applications Flutter. En utilisant les bonnes techniques et en suivant les meilleures pratiques, vous pouvez créer des interfaces dynamiques et engageantes.
N'oubliez pas de :
- Garder les animations simples et pertinentes
- Optimiser les performances
- Tester sur différents appareils
- Respecter les préférences utilisateur
Avec ces connaissances, vous êtes prêt à créer des animations impressionnantes dans vos applications Flutter !