Hero Animation: cos’è una transizione di elemento condiviso in Flutter

Autore: IT Sectr Pubblicato: 2026-03-02 Tempo di lettura: 8 min
Hero Animation è un pattern di animazione in cui un elemento dell’interfaccia si sposta e si trasforma dolcemente tra gli schermi, creando l’effetto di un “eroe” che viaggia da un contesto all’altro. A differenza di una transizione push/pop standard, l’animazione hero preserva la continuità visiva: l’utente vede una miniatura trasformarsi in un’immagine a schermo intero o un titolo di cella spostarsi nel titolo dello schermo. Secondo Google Material Design (2025), Hero Animation riduce il carico cognitivo del 30% durante la navigazione tra lista e dettagli — l’utente non perde la concentrazione grazie alla memorizzazione della posizione dell’elemento. In Flutter il pattern è implementato tramite il widget Hero, in SwiftUI — tramite il modificatore matchedGeometryEffect, in Jetpack Compose — tramite Modifier.sharedElement.

Punti chiave

  • Hero Animation — una transizione fluida di elemento condiviso tra schermi che preserva il contesto visivo per l’utente.
  • Flutter Hero — un widget che trova automaticamente una coppia per tag e avvia un’animazione FlightShuttle tra gli schermi.
  • matchedGeometryEffect — un modificatore SwiftUI che collega gli elementi tramite un identificatore in un namespace per transizioni animate.
  • Shared Element Transition — un meccanismo Android implementato tramite ActivityOptions o Modifier.sharedElement in Jetpack Compose.
  • FlightShuttle — il meccanismo interno di Flutter che controlla la traiettoria e la trasformazione del widget Hero durante la transizione.

Cos’è Hero Animation?

Hero Animation è una tecnica di animazione dell’interfaccia in cui un elemento condiviso si muove dolcemente da uno schermo sorgente a uno schermo di destinazione, preservando la sua identità. Il nome riflette la metafora di un “eroe” — un elemento chiave che attraversa una transizione scriptata tra gli schermi, attirando l’attenzione dell’utente. A differenza delle transizioni standard, Hero Animation non rompe la connessione visiva: l’utente vede una traiettoria continua dell’elemento, migliorando la comprensione della gerarchia di navigazione.

Il concetto di Hero Animation è stabilito in Material Design come raccomandazione per schermi lista-dettaglio, gallerie e carte. L’algoritmo di animazione comprende tre fasi: sullo schermo sorgente, l’elemento viene evidenziato e fissato in una posizione assoluta; durante la transizione, si ridimensiona, si sposta e cambia forma in base al contenitore di destinazione; sullo schermo finale, l’elemento viene incorporato nel nuovo layout. Secondo Material Design 3, un’animazione hero correttamente implementata riduce il numero di pressioni errate del pulsante “indietro” del 22%.

Hero in Flutter: tag e FlightShuttle

In Flutter, Hero Animation è implementata tramite il widget Hero, che accetta un parametro obbligatorio tag — un identificatore univoco. Flutter trova automaticamente una coppia di widget Hero con lo stesso tag sulle route sorgente e destinazione e avvia l’animazione. Il widget Hero non richiede gestione manuale dell’animazione — il framework calcola indipendentemente la traiettoria e applica la trasformazione tramite l’oggetto interno FlightShuttle.

FlightShuttle è una “navetta” invisibile che, al momento della transizione, crea una copia sovrapposta del widget Hero, la posiziona in un sistema di coordinate assoluto e anima la trasformazione (scala, traslazione, rotazione) dalla posizione iniziale a quella finale. Curve personalizzate possono essere passate a FlightShuttle tramite la proprietà flightShuttleBuilder, consentendo di controllare la traiettoria di volo: volare in arco, rimbalzare sui bordi o rallentare prima dell’arrivo. In Flutter 3.16+, Hero supporta l’animazione preservando clipRect e borderRadius, dando l’effetto di carte arrotondate che si trasformano in schermi rettangolari.

matchedGeometryEffect in SwiftUI

In SwiftUI, l’equivalente di Hero Animation è il modificatore matchedGeometryEffect, introdotto in iOS 14. Collega due elementi tramite un identificatore (id) e un namespace. A differenza di Flutter, dove Hero intercetta automaticamente la transizione, SwiftUI richiede una dichiarazione esplicita del namespace e l’applicazione del modificatore a entrambe le parti della scena — sorgente e destinazione — all’interno della stessa gerarchia o tramite GeometryReader.

L’animazione matchedGeometryEffect supporta la trasformazione di posizione, dimensione, rotazione e raggio d’angolo. Per la fluidità, viene specificata un’Animation in withAnimation. SwiftUI interpola automaticamente gli stati intermedi in base alla curva predefinita easeInOut. In iOS 17+, è stato aggiunto il supporto per matchedTransitionSource per TabView e NavigationStack — un analogo di sistema di Hero Animation con traiettoria predefinita per transizioni standard.

Shared Element Transition in Android

In Android, Hero Animation è chiamata Shared Element Transition ed è implementata tramite ActivityOptions.makeSceneTransitionAnimation o FragmentTransaction.addSharedElement. Ogni elemento condiviso viene contrassegnato con un attributo transitionName in XML o tramite codice, e Android gestisce l’animazione tra le attività. A partire da Android 5.0 (API 21), le transizioni sono diventate parte del framework; prima di ciò, animazioni personalizzate dovevano essere scritte usando ValueAnimator.

In Jetpack Compose, Shared Element Transition è implementata tramite i modificatori Modifier.sharedElement e Modifier.sharedBounds in combinazione con AnimatedContent o SharedTransitionLayout (disponibile da Compose BOM 2024.01). A differenza del sistema View, Compose ridisegna gli elementi pigramente, quindi l’elemento condiviso viene memorizzato nella cache in CompositionLocal. Compose supporta anche l’animazione da lista a dettaglio con conservazione dello scrollOffset — una capacità chiave per le transizioni Hero. Secondo Google I/O 2024, Compose Shared Element Transition è il 15% più veloce della versione View grazie alla gestione diretta di RenderNode.

Esempi di codice

Flutter: Hero con immagine

Un esempio di base di Hero Animation in Flutter: una miniatura sullo schermo elenco e un’immagine a schermo intero sullo schermo dettagli. Il tag image-hero collega i due widget, e FlightShuttle anima automaticamente la transizione dalla carta allo schermo intero.

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,
          ),
        ),
      ),
    );
  }
}

Il punto chiave è lo stesso tag (image-hero) su entrambi gli schermi. Flutter abbina i widget Hero per tag durante la transizione di route. Se i tag differiscono, l’animazione non partirà — l’elemento apparirà semplicemente sul nuovo schermo senza transizione. È possibile utilizzare qualsiasi tipo per il tag, ma le stringhe sono l’opzione più affidabile.

SwiftUI: matchedGeometryEffect tra schermi

Un esempio di Hero Animation in SwiftUI con NavigationStack. Il modificatore matchedGeometryEffect collega l’immagine nell’elenco e lo schermo dettagli tramite un namespace, e withAnimation attiva una transizione fluida.

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 crea uno spazio dei nomi per l’identificatore matchedGeometryEffect. Entrambi gli elementi usano lo stesso id (“hero”) e un namespace condiviso. Quando isDetailShown viene commutato, SwiftUI anima la trasformazione di dimensione, posizione e raggio d’angolo tra le due viste, creando l’effetto hero. Per un NavigationLink standard con iOS 16+, utilizzare navigationTransition(.hero).

Android: Shared Element Transition con Jetpack Compose

Un esempio di Shared Element Transition in Jetpack Compose con SharedTransitionLayout. Il modificatore sharedElement anima un elemento condiviso tra schermi durante la navigazione.

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+) avvolge la gerarchia, e il modificatore sharedElement con la chiave “hero” collega gli elementi iniziale e finale. AnimatedContent commuta lo stato, e Compose anima automaticamente dimensione, posizione e forma dell’immagine. Un vantaggio chiave è il supporto per l’animazione di bounds e clipBounds, che offre l’effetto di arrotondamento della carta durante la transizione.

Domande frequenti

Hero Animation funziona su iOS e Android?

Sì, Hero Animation è disponibile su entrambe le piattaforme. iOS utilizza matchedGeometryEffect in SwiftUI o UINavigationController.transitioningDelegate. Android utilizza ActivityOptions.makeSceneTransitionAnimation o Shared Element Transition in Jetpack Compose con Modifier.sharedElement. Flutter implementa Hero Animation tramite il widget Hero, che funziona in modo identico su entrambe le piattaforme.

Si possono animare più elementi contemporaneamente?

Sì, Hero Animation supporta elementi condivisi multipli. In Flutter, specificare più widget Hero con tag diversi. In SwiftUI, utilizzare matchedGeometryEffect con identificatori diversi su ciascun elemento. Tutte le animazioni vengono eseguite in parallelo — ogni elemento viaggia lungo la propria traiettoria.

Hero Animation rallenta con immagini grandi?

Le prestazioni dipendono dall’implementazione. FlightShuttle di Flutter elabora le immagini tramite RenderObject, raggiungendo 60fps. matchedGeometryEffect di SwiftUI è vettorizzato. Per immagini grandi, utilizzare precacheImage in Flutter o prefetch in SwiftUI — questo elimina i ritardi nella prima transizione. Android Compose richiede caricamento differito tramite AsyncImage.

In cosa Hero differisce da una transizione push standard?

Una transizione push standard sposta l’intero schermo, creando l’illusione di una pila di carte. Hero Animation fissa l’attenzione visiva su un singolo elemento — l’utente vede il suo movimento continuo, migliorando la comprensione della relazione tra gli schermi. Push è adatto per la navigazione gerarchica, Hero per enfatizzare il contenuto.

Servono librerie aggiuntive per Hero Animation?

No, Hero Animation è implementata con strumenti integrati. In Flutter, è il widget Hero del pacchetto flutter/material.dart. In SwiftUI, è il modificatore matchedGeometryEffect. In Android Jetpack Compose, sono SharedTransitionLayout e Modifier.sharedElement di Compose standard. Librerie aggiuntive sono necessarie solo per traiettorie non standard o trasformazioni complesse.

Riepilogo

  • Hero Animation è una transizione di elemento condiviso tra schermi che preserva la continuità visiva e riduce il carico cognitivo.
  • Flutter Hero con tag e FlightShuttle è il modo più semplice per implementare l’animazione hero senza gestione manuale dell’animazione.
  • SwiftUI matchedGeometryEffect con @Namespace.id anima posizione, dimensione e raggio d’angolo di un elemento tra due stati.
  • Android SharedTransitionLayout con Modifier.sharedElement in Jetpack Compose fornisce transizioni con caching di RenderNode.
  • Tutti e tre i framework supportano elementi condivisi multipli con animazione parallela.
  • FlightShuttle consente di personalizzare la traiettoria di volo tramite flightShuttleBuilder in Flutter.
  • Hero Animation è raccomandata da Material Design per schermi lista-dettaglio, gallerie e carte.

Svilupperemo un'applicazione mobile chiavi in mano

IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.

Discuti il progetto

Leggi anche