Animations Avancées avec Flutter
Gad Ntenta

Gad Ntenta

Développeur Full Stack

Animations Avancées avec Flutter

FlutterAnimationsUI/UXMobile Development

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 const pour les widgets statiques
  • Évitez les animations inutiles
  • Utilisez RepaintBoundary pour 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 !