Hero Animation: wat is het, animatie van gedeelde elementen in Flutter

Auteur: IT Sectr Gepubliceerd: 2026-03-02 Leestijd: 8 min
Hero Animation — een animatiepatroon waarbij een interface-element soepel tussen schermen beweegt en transformeert, waardoor het effect van een „held" ontstaat die van de ene context naar de andere reist. In tegenstelling tot de standaard push/pop-overgang behoudt de heldenanimatie visuele continuïteit: de gebruiker ziet hoe een miniatuur verandert in een volledig schermbeeld of hoe een celkop naar de schermkop beweegt. Volgens Google Material Design (2025) vermindert Hero Animation de cognitieve belasting met 30% bij navigatie tussen lijst en details — de gebruiker verliest geen focus dankzij het onthouden van de elementpositie. In Flutter wordt het patroon geïmplementeerd via de Hero-widget, in SwiftUI via de matchedGeometryEffect modifier, in Jetpack Compose via Modifier.sharedElement.

Belangrijkste punten

  • Hero Animation — soepele overgang van een gedeeld element tussen schermen, met behoud van visuele context voor de gebruiker.
  • Flutter Hero — widget die automatisch een paar vindt op basis van een tag en de FlightShuttle-animatie tussen schermen start.
  • matchedGeometryEffect — SwiftUI-modifier die elementen via een identifier in een namespace verbindt voor een geanimeerde overgang.
  • Shared Element Transition — Android-mechanisme, geïmplementeerd via ActivityOptions of Modifier.sharedElement in Jetpack Compose.
  • FlightShuttle — intern Flutter-mechanisme dat de baan en transformatie van de Hero-widget tijdens de overgang beheert.

Wat is Hero Animation?

Hero Animation — een interface-animatietechniek waarbij een gedeeld element soepel van het bronscherm naar het doelscherm beweegt en zijn identiteit behoudt. De naam weerspiegelt de metafor van de „held" — het sleutelelement dat door een scène-overgang tussen schermen gaat en de aandacht van de gebruiker trekt. In tegenstelling tot standaardovergangen verbreekt Hero Animation de visuele band niet: de gebruiker ziet het continue pad van het element, wat het begrip van de navigatiehiërarchie verbetert.

Het concept van Hero Animation is verankerd in Material Design als aanbeveling voor lijst-detailschermen, galerijen en kaarten. Het animatie-algoritme bestaat uit drie fasen: op het bronscherm wordt het element gemarkeerd en vastgezet op een absolute positie; tijdens de overgang wordt het geschaald, verplaatst en van vorm veranderd volgens de doelcontainer; op het eindscherm wordt het element in de nieuwe lay-out geïntegreerd. Volgens Material Design 3 vermindert een correct geïmplementeerde heldenanimatie het aantal verkeerde „terug"-klikken met 22%.

Hero in Flutter: tags en FlightShuttle

In Flutter wordt Hero Animation geïmplementeerd via de Hero-widget, die de verplichte parameter tag — een unieke identifier — accepteert. Flutter vindt automatisch een paar Hero met dezelfde tag op de bron- en doelroutes en start de animatie. De Hero-widget vereist geen handmatig animatiebeheer — het framework berekent zelf de baan en past de transformatie toe via het interne FlightShuttle-object.

FlightShuttle — een onzichtbare „shuttle" die op het moment van de overgang een overlay-kopie van de Hero-widget maakt, deze in het absolute coördinatensysteem plaatst en de transformatie (scale, translate, rotate) van begin- naar eindpositie animeert. Aan FlightShuttle kunnen aangepaste curven worden doorgegeven via de eigenschap flightShuttleBuilder, waardoor de vliegbaan kan worden gestuurd: vliegen in een boog, stuiteren op grenzen of vertragen voor aankomst. In Flutter 3.16+ ondersteunt Hero animatie met behoud van clipRect en borderRadius, wat het effect geeft van afgeronde kaarten die transformeren naar rechthoekige schermen.

matchedGeometryEffect in SwiftUI

In SwiftUI is het equivalent van Hero Animation de modifier matchedGeometryEffect, geïntroduceerd in iOS 14. Het verbindt twee elementen via een identifier (id) en een naamruimte (namespace). In tegenstelling tot Flutter, waar Hero automatisch de overgang onderschept, vereist SwiftUI een expliciete declaratie van de namespace en toepassing van de modifier op beide delen van de scène — bron en doel — binnen dezelfde hiërarchie of via GeometryReader.

De matchedGeometryEffect-animatie ondersteunt transformatie van positie, grootte, rotatie en hoekradius (cornerRadius). Voor vloeiendheid wordt Animation ingesteld in withAnimation. SwiftUI interpoleert automatisch tussenliggende toestanden op basis van de standaard easeInOut-curve. In iOS 17+ is ondersteuning voor matchedTransitionSource toegevoegd voor TabView en NavigationStack — een systeemanaloog van Hero Animation met een vooraf gedefinieerd pad voor standaardovergangen.

Shared Element Transition in Android

In Android heet Hero Animation Shared Element Transition en wordt geïmplementeerd via ActivityOptions.makeSceneTransitionAnimation of FragmentTransaction.addSharedElement. Elk gedeeld element wordt gemarkeerd met het attribuut transitionName in XML of via code, en Android is verantwoordelijk voor de animatie tussen activiteiten. Sinds Android 5.0 (API 21) maken overgangen deel uit van het framework; daarvoor moesten aangepaste animaties worden geschreven via ValueAnimator.

In Jetpack Compose wordt Shared Element Transition geïmplementeerd via de modifier Modifier.sharedElement en Modifier.sharedBounds in combinatie met AnimatedContent of SharedTransitionLayout (beschikbaar vanaf Compose BOM 2024.01). In tegenstelling tot het View-systeem tekent Compose elementen lui, waardoor het gedeelde element in CompositionLocal wordt gecachet. Compose ondersteunt ook de animatie van lijst naar detail (list→detail) met behoud van scrollOffset — een belangrijke mogelijkheid voor Hero-overgangen. Volgens Google I/O 2024 werkt Compose Shared Element Transition 15% sneller dan de View-versie dankzij direct beheer van RenderNode.

Codevoorbeelden

Flutter: Hero met afbeelding

Basisvoorbeeld van Hero Animation in Flutter: een miniatuur op het lijstscherm en een volledig schermbeeld op het detailscherm. De tag image-hero verbindt de twee widgets en FlightShuttle animeert automatisch de overgang van kaart naar volledig scherm.

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

Het sleutelpunt — dezelfde tag (image-hero) op beide schermen. Flutter matcht Hero-widgets op tag tijdens de route-overgang. Als de tags verschillen, wordt de animatie niet gestart — het element verschijnt gewoon op het nieuwe scherm zonder overgang. Elk type kan voor de tag worden gebruikt, maar strings zijn de meest betrouwbare optie.

SwiftUI: matchedGeometryEffect tussen schermen

Voorbeeld van Hero Animation in SwiftUI met NavigationStack. De modifier matchedGeometryEffect verbindt de afbeelding in de lijst en het detailscherm via namespace, en withAnimation start een soepele overgang.

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 creëert een naamruimte voor de identifier van matchedGeometryEffect. Beide elementen gebruiken dezelfde id (hero) en een gedeelde namespace. Wanneer isDetailShown wordt gewisseld, animeert SwiftUI de transformatie van grootte, positie en cornerRadius tussen de twee weergaven, waardoor het heldeneffect ontstaat. Gebruik voor standaard NavigationLink met iOS 16+ navigationTransition(.hero).

Android: Shared Element Transition met Jetpack Compose

Voorbeeld van Shared Element Transition in Jetpack Compose met SharedTransitionLayout. De modifier sharedElement animeert het gedeelde element tussen schermen tijdens navigatie.

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+) omhult de hiërarchie, en de modifier sharedElement met de sleutel hero verbindt de begin- en eindelementen. AnimatedContent schakelt de toestand en Compose animeert automatisch de grootte, positie en vorm van de afbeelding. Het belangrijkste voordeel is de ondersteuning voor bounds- en clipBounds-animatie, wat het effect geeft van afronding van de kaart tijdens de overgang.

Veelgestelde vragen

Werkt Hero Animation op iOS en Android?

Ja, Hero Animation is beschikbaar op beide platforms. In iOS wordt matchedGeometryEffect in SwiftUI of UINavigationController.transitioningDelegate gebruikt. In Android — de overgang ActivityOptions.makeSceneTransitionAnimation of Shared Element Transition in Jetpack Compose met Modifier.sharedElement. Flutter implementeert Hero Animation via de Hero-widget, die op beide platforms identiek werkt.

Kunnen meerdere elementen tegelijk worden geanimeerd?

Ja, Hero Animation ondersteunt meerdere gedeelde elementen. Specificeer in Flutter meerdere Hero met verschillende tags. Gebruik in SwiftUI matchedGeometryEffect met verschillende identifiers op elk element. Alle animaties worden parallel uitgevoerd — elk element reist over zijn eigen pad.

Wordt Hero Animation traag op grote afbeeldingen?

Prestaties zijn afhankelijk van de implementatie. Flutter FlightShuttle verwerkt afbeeldingen via RenderObject, wat 60fps oplevert. SwiftUI matchedGeometryEffect is gevectoriseerd. Gebruik voor grote afbeeldingen precacheImage in Flutter of prefetch in SwiftUI — dit elimineert vertragingen bij de eerste overgang. Android Compose vereist lui laden via AsyncImage.

Hoe verschilt Hero van de standaard push-overgang?

De standaard push-overgang verplaatst het hele scherm en creëert de illusie van een stapel kaarten. Hero Animation fixeert de visuele aandacht op één element — de gebruiker ziet de continue beweging ervan, wat het begrip van de relatie tussen schermen verbetert. Push is goed voor hiërarchische navigatie, Hero — voor het benadrukken van inhoud.

Zijn er extra bibliotheken nodig voor Hero Animation?

Nee, Hero Animation is geïmplementeerd met ingebouwde middelen. In Flutter is het de Hero-widget uit het pakket flutter/material.dart. In SwiftUI — de modifier matchedGeometryEffect. In Android Jetpack Compose — SharedTransitionLayout en Modifier.sharedElement uit standaard Compose. Extra bibliotheken zijn alleen nodig voor niet-standaard paden of complexe transformaties.

Samenvatting

  • Hero Animation — animatie van een gedeeld element tussen schermen, met behoud van visuele continuïteit en vermindering van cognitieve belasting.
  • Flutter Hero met tag en FlightShuttle — de eenvoudigste manier om heldenanimatie te implementeren zonder handmatig animatiebeheer.
  • SwiftUI matchedGeometryEffect met @Namespace.id animeert positie, grootte en cornerRadius van het element tussen twee toestanden.
  • Android SharedTransitionLayout met Modifier.sharedElement in Jetpack Compose zorgt voor overgang met RenderNode-caching.
  • Alle drie de frameworks ondersteunen meerdere gedeelde elementen met parallelle animatie.
  • FlightShuttle maakt aanpassing van de vliegbaan mogelijk via flightShuttleBuilder in Flutter.
  • Hero Animation wordt aanbevolen door Material Design voor lijst-detailschermen, galerijen en kaarten.

We ontwikkelen een mobiele applicatie turnkey

IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.

Bespreek het project

Lees ook