Hero Animation : qu’est-ce qu’une transition d’élément partagé dans Flutter

Auteur : IT Sectr Publié le : 2026-03-02 Temps de lecture : 8 min
Hero Animation est un motif d’animation dans lequel un élément d’interface se déplace et se transforme en douceur entre les écrans, créant l’effet d’un «héros» voyageant d’un contexte à un autre. Contrairement à une transition push/pop standard, l’animation héroïque préserve la continuité visuelle : l’utilisateur voit une vignette se transformer en image plein écran ou un titre de cellule se déplacer vers le titre de l’écran. Selon Google Material Design (2025), Hero Animation réduit la charge cognitive de 30% lors de la navigation entre la liste et les détails — l’utilisateur ne perd pas le focus grâce à la mémorisation de la position de l’élément. Dans Flutter, le motif est implémenté via le widget Hero, dans SwiftUI — via le modificateur matchedGeometryEffect, dans Jetpack Compose — via Modifier.sharedElement.

Points clés

  • Hero Animation — une transition en douceur d’élément partagé entre les écrans qui préserve le contexte visuel pour l’utilisateur.
  • Flutter Hero — un widget qui trouve automatiquement une paire par tag et lance une animation FlightShuttle entre les écrans.
  • matchedGeometryEffect — un modificateur SwiftUI qui relie les éléments via un identifiant dans un namespace pour des transitions animées.
  • Shared Element Transition — un mécanisme Android implémenté via ActivityOptions ou Modifier.sharedElement dans Jetpack Compose.
  • FlightShuttle — le mécanisme interne de Flutter qui contrôle la trajectoire et la transformation du widget Hero pendant la transition.

Qu’est-ce que Hero Animation ?

Hero Animation est une technique d’animation d’interface où un élément partagé se déplace en douceur d’un écran source vers un écran de destination, en préservant son identité. Le nom reflète la métaphore d’un «héros» — un élément clé qui traverse une transition scénarisée entre les écrans, attirant l’attention de l’utilisateur. Contrairement aux transitions standard, Hero Animation ne rompt pas la connexion visuelle : l’utilisateur voit une trajectoire continue de l’élément, ce qui améliore la compréhension de la hiérarchie de navigation.

Le concept de Hero Animation est établi dans Material Design comme une recommandation pour les écrans liste-détail, les galeries et les cartes. L’algorithme d’animation comprend trois phases : sur l’écran source, l’élément est mis en évidence et fixé dans une position absolue ; pendant la transition, il se met à l’échelle, se déplace et change de forme selon le conteneur de destination ; sur l’écran final, l’élément est intégré dans la nouvelle disposition. Selon Material Design 3, une animation héroïque correctement implémentée réduit le nombre d’appuis erronés sur le bouton «retour» de 22%.

Hero dans Flutter : tags et FlightShuttle

Dans Flutter, Hero Animation est implémentée via le widget Hero, qui prend un paramètre obligatoire tag — un identifiant unique. Flutter trouve automatiquement une paire de widgets Hero avec le même tag sur les routes source et destination et lance l’animation. Le widget Hero ne nécessite pas de gestion manuelle de l’animation — le framework calcule indépendamment la trajectoire et applique la transformation via l’objet interne FlightShuttle.

FlightShuttle est une «navette» invisible qui, au moment de la transition, crée une copie superposée du widget Hero, la positionne dans un système de coordonnées absolu et anime la transformation (échelle, translation, rotation) de la position initiale à la position finale. Des courbes personnalisées peuvent être transmises à FlightShuttle via la propriété flightShuttleBuilder, permettant de contrôler la trajectoire de vol : voler en arc, rebondir sur les bords ou ralentir avant l’arrivée. Dans Flutter 3.16+, Hero prend en charge l’animation en préservant clipRect et borderRadius, ce qui donne l’effet de cartes arrondies se transformant en écrans rectangulaires.

matchedGeometryEffect dans SwiftUI

Dans SwiftUI, l’équivalent de Hero Animation est le modificateur matchedGeometryEffect, introduit dans iOS 14. Il relie deux éléments via un identifiant (id) et un namespace. Contrairement à Flutter, où Hero intercepte automatiquement la transition, SwiftUI nécessite une déclaration explicite du namespace et l’application du modificateur aux deux parties de la scène — source et destination — dans la même hiérarchie ou via GeometryReader.

L’animation matchedGeometryEffect prend en charge la transformation de la position, de la taille, de la rotation et du rayon des coins. Pour la fluidité, une Animation est spécifiée dans withAnimation. SwiftUI interpole automatiquement les états intermédiaires en fonction de la courbe par défaut easeInOut. Dans iOS 17+, la prise en charge de matchedTransitionSource a été ajoutée pour TabView et NavigationStack — un analogue système de Hero Animation avec une trajectoire prédéfinie pour les transitions standard.

Shared Element Transition dans Android

Dans Android, Hero Animation est appelée Shared Element Transition et est implémentée via ActivityOptions.makeSceneTransitionAnimation ou FragmentTransaction.addSharedElement. Chaque élément partagé est marqué avec un attribut transitionName en XML ou via du code, et Android gère l’animation entre les activités. À partir d’Android 5.0 (API 21), les transitions sont devenues partie intégrante du framework ; avant cela, des animations personnalisées devaient être écrites à l’aide de ValueAnimator.

Dans Jetpack Compose, Shared Element Transition est implémentée via les modificateurs Modifier.sharedElement et Modifier.sharedBounds en conjonction avec AnimatedContent ou SharedTransitionLayout (disponible depuis Compose BOM 2024.01). Contrairement au système View, Compose redessine les éléments paresseusement, donc l’élément partagé est mis en cache dans CompositionLocal. Compose prend également en charge l’animation de liste vers détail avec préservation du scrollOffset — une capacité clé pour les transitions Hero. Selon Google I/O 2024, Compose Shared Element Transition est 15% plus rapide que la version View grâce à la gestion directe de RenderNode.

Exemples de code

Flutter : Hero avec image

Un exemple de base de Hero Animation dans Flutter : une vignette sur l’écran de liste et une image plein écran sur l’écran de détails. Le tag image-hero relie les deux widgets, et FlightShuttle anime automatiquement la transition de la carte au plein écran.

dart
import 'package:flutter/material.dart';

class ListScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: ListView(
        children: [
          Hero(
            tag: 'image-hero',
            child: GestureDetector(
              onTap: () {
                Navigator.push(context,
                  MaterialPageRoute(
                    builder: (_) => DetailScreen(),
                  ),
                );
              },
              child: ClipRRect(
                borderRadius: BorderRadius.circular(12),
                child: Image.network(
                  'https://picsum.photos/200',
                  width: 100, height: 100,
                  fit: BoxFit.cover,
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

class DetailScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Detail')),
      body: Center(
        child: Hero(
          tag: 'image-hero',
          child: Image.network(
            'https://picsum.photos/800',
            width: 300, height: 400,
            fit: BoxFit.cover,
          ),
        ),
      ),
    );
  }
}

Le point clé est le même tag (image-hero) sur les deux écrans. Flutter fait correspondre les widgets Hero par tag pendant la transition de route. Si les tags diffèrent, l’animation ne démarrera pas — l’élément apparaîtra simplement sur le nouvel écran sans transition. N’importe quel type peut être utilisé pour le tag, mais les chaînes sont l’option la plus fiable.

SwiftUI : matchedGeometryEffect entre les écrans

Un exemple de Hero Animation dans SwiftUI avec NavigationStack. Le modificateur matchedGeometryEffect relie l’image dans la liste et l’écran de détails via un namespace, et withAnimation déclenche une transition en douceur.

swift
import SwiftUI

struct HeroExampleView: View {
    @Namespace private var heroNamespace
    @State private var isDetailShown = false

    var body: some View {
        VStack {
            if isDetailShown {
                DetailView(namespace: heroNamespace)
                    .transition(.identity)
            } else {
                ListView(namespace: heroNamespace)
                    .transition(.identity)
            }
        }
        .animation(Animation.easeInOut(duration: 0.4),
                   value: isDetailShown)
    }
}

struct ListView: View {
    let namespace: Namespace.ID

    var body: some View {
        Image("thumbnail")
            .resizable()
            .frame(width: 100, height: 100)
            .cornerRadius(12)
            .matchedGeometryEffect(id: "hero", in: namespace)
    }
}

struct DetailView: View {
    let namespace: Namespace.ID

    var body: some View {
        Image("thumbnail")
            .resizable()
            .frame(width: 300, height: 400)
            .cornerRadius(0)
            .matchedGeometryEffect(id: "hero", in: namespace)
    }
}

@Namespace crée un espace de noms pour l’identifiant matchedGeometryEffect. Les deux éléments utilisent le même id (“hero”) et un namespace partagé. Lorsque isDetailShown bascule, SwiftUI anime la transformation de la taille, de la position et du rayon des coins entre les deux vues, créant l’effet héroïque. Pour un NavigationLink standard avec iOS 16+, utilisez navigationTransition(.hero).

Android : Shared Element Transition avec Jetpack Compose

Un exemple de Shared Element Transition dans Jetpack Compose avec SharedTransitionLayout. Le modificateur sharedElement anime un élément partagé entre les écrans pendant la navigation.

kotlin
@OptIn(ExperimentalSharedTransitionApi::class)
@Composable
fun HeroExample() {
    SharedTransitionLayout {
        var isDetail by remember { mutableStateOf(false) }

        AnimatedContent(
            targetState = isDetail,
            transitionSpec = {
                fadeIn() togetherWith fadeOut()
            }
        ) { detail ->
            if (detail) {
                Box(
                    modifier = Modifier
                        .fillMaxSize()
                        .sharedElement(
                            state = rememberSharedContentState(key = "hero"),
                            animatedVisibilityScope = this
                        )
                ) {
                    AsyncImage(
                        model = "https://picsum.photos/800",
                        contentDescription = null,
                        modifier = Modifier.fillMaxSize()
                    )
                }
            } else {
                Box(
                    modifier = Modifier
                        .size(100.dp)
                        .sharedElement(
                            state = rememberSharedContentState(key = "hero"),
                            animatedVisibilityScope = this
                        )
                ) {
                    AsyncImage(
                        model = "https://picsum.photos/200",
                        contentDescription = null,
                        modifier = Modifier.fillMaxSize()
                    )
                }
            }
        }
    }
}

Compose SharedTransitionLayout (Compose BOM 2024.01+) encapsule la hiérarchie, et le modificateur sharedElement avec la clé “hero” relie les éléments initial et final. AnimatedContent bascule l’état, et Compose anime automatiquement la taille, la position et la forme de l’image. Un avantage clé est la prise en charge de l’animation des bounds et clipBounds, offrant l’effet d’arrondi de carte pendant la transition.

Foire aux questions

Hero Animation fonctionne-t-elle sur iOS et Android ?

Oui, Hero Animation est disponible sur les deux plateformes. iOS utilise matchedGeometryEffect dans SwiftUI ou UINavigationController.transitioningDelegate. Android utilise ActivityOptions.makeSceneTransitionAnimation ou Shared Element Transition dans Jetpack Compose avec Modifier.sharedElement. Flutter implémente Hero Animation via le widget Hero, qui fonctionne de manière identique sur les deux plateformes.

Peut-on animer plusieurs éléments simultanément ?

Oui, Hero Animation prend en charge plusieurs éléments partagés. Dans Flutter, spécifiez plusieurs widgets Hero avec des tags différents. Dans SwiftUI, utilisez matchedGeometryEffect avec des identifiants différents sur chaque élément. Toutes les animations s’exécutent en parallèle — chaque élément voyage sur sa propre trajectoire.

Hero Animation ralentit-elle sur les grandes images ?

Les performances dépendent de l’implémentation. FlightShuttle de Flutter traite les images via RenderObject, atteignant 60fps. matchedGeometryEffect de SwiftUI est vectorisé. Pour les grandes images, utilisez precacheImage dans Flutter ou prefetch dans SwiftUI — cela élimine les retards lors de la première transition. Android Compose nécessite un chargement différé via AsyncImage.

En quoi Hero diffère-t-il d’une transition push standard ?

Une transition push standard fait glisser tout l’écran, créant l’illusion d’une pile de cartes. Hero Animation fixe l’attention visuelle sur un seul élément — l’utilisateur voit son mouvement continu, ce qui améliore la compréhension de la relation entre les écrans. Push est bon pour la navigation hiérarchique, Hero pour mettre l’accent sur le contenu.

Des bibliothèques supplémentaires sont-elles nécessaires pour Hero Animation ?

Non, Hero Animation est implémentée avec des outils intégrés. Dans Flutter, c’est le widget Hero du package flutter/material.dart. Dans SwiftUI, c’est le modificateur matchedGeometryEffect. Dans Android Jetpack Compose, ce sont SharedTransitionLayout et Modifier.sharedElement de Compose standard. Des bibliothèques supplémentaires ne sont nécessaires que pour les trajectoires non standard ou les transformations complexes.

Résumé

  • Hero Animation est une transition d’élément partagé entre les écrans qui préserve la continuité visuelle et réduit la charge cognitive.
  • Flutter Hero avec tag et FlightShuttle est le moyen le plus simple d’implémenter l’animation héroïque sans gestion manuelle de l’animation.
  • SwiftUI matchedGeometryEffect avec @Namespace.id anime la position, la taille et le rayon des coins d’un élément entre deux états.
  • Android SharedTransitionLayout avec Modifier.sharedElement dans Jetpack Compose fournit des transitions avec mise en cache RenderNode.
  • Les trois frameworks prennent en charge plusieurs éléments partagés avec animation parallèle.
  • FlightShuttle permet de personnaliser la trajectoire de vol via flightShuttleBuilder dans Flutter.
  • Hero Animation est recommandée par Material Design pour les écrans liste-détail, les galeries et les cartes.

Nous développerons une application mobile clé en main

IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.

Discuter du projet

Lisez aussi