Hero Animation: co to je, animace sdílených prvků ve Flutter

Autor: IT Sectr Publikováno: 2026-03-02 Doba čtení: 8 min
Hero Animation — vzor animace, při kterém se prvek rozhraní plynule pohybuje a transformuje mezi obrazovkami a vytváří efekt „hrdiny" cestujícího z jednoho kontextu do druhého. Na rozdíl od standardního přechodu push/pop si animace hrdiny zachovává vizuální kontinuitu: uživatel vidí, jak se miniatura mění na obrázek přes celou obrazovku nebo jak se záhlaví buňky přesouvá do záhlaví obrazovky. Podle Google Material Design (2025) snižuje Hero Animation kognitivní zátěž o 30 % při navigaci mezi seznamem a detaily — uživatel neztrácí soustředění díky zapamatování pozice prvku. Ve Flutter je vzor implementován pomocí widgetu Hero, ve SwiftUI — pomocí modifikátoru matchedGeometryEffect, v Jetpack Compose — pomocí Modifier.sharedElement.

Hlavní body

  • Hero Animation — plynulý přechod sdíleného prvku mezi obrazovkami, který uživateli zachovává vizuální kontext.
  • Flutter Hero — widget, který automaticky najde pár podle tagu a spustí animaci FlightShuttle mezi obrazovkami.
  • matchedGeometryEffect — modifikátor SwiftUI, který spojuje prvky pomocí identifikátoru v namespace pro animovaný přechod.
  • Shared Element Transition — mechanismus Androidu, implementovaný pomocí ActivityOptions nebo Modifier.sharedElement v Jetpack Compose.
  • FlightShuttle — vnitřní mechanismus Flutter, který řídí trajektorii a transformaci widgetu Hero během přechodu.

Co je Hero Animation?

Hero Animation — technika animace rozhraní, při které se sdílený prvek plynule přesouvá z zdrojové obrazovky na cílovou, přičemž si zachovává svou identitu. Název odráží metaforu „hrdiny" — klíčového prvku, který prochází scénickým přechodem mezi obrazovkami a přitahuje pozornost uživatele. Na rozdíl od standardních přechodů Hero Animation nepřerušuje vizuální spojení: uživatel vidí spojitou trajektorii prvku, což zlepšuje porozumění navigační hierarchii.

Koncepce Hero Animation je zakotvena v Material Design jako doporučení pro obrazovky seznam-detail (list-detail), galerie a karty. Algoritmus animace se skládá ze tří fází: na zdrojové obrazovce je prvek zvýrazněn a fixován v absolutní poloze; během přechodu je škálován, přesouván a mění tvar podle cílového kontejneru; na konečné obrazovce je prvek integrován do nového rozvržení. Podle Material Design 3 správně implementovaná animace hrdiny snižuje počet chybných kliknutí „zpět" o 22 %.

Hero ve Flutter: tagy a FlightShuttle

Ve Flutter je Hero Animation implementována pomocí widgetu Hero, který přijímá povinný parametr tag — jedinečný identifikátor. Flutter automaticky najde pár Hero se stejným tagem na zdrojové a cílové trase a spustí animaci. Widget Hero nevyžaduje ruční správu animace — framework si sám vypočítá trajektorii a aplikuje transformaci pomocí vnitřního objektu FlightShuttle.

FlightShuttle — neviditelný „raketoplán", který v okamžiku přechodu vytvoří překryvnou kopii widgetu Hero, umístí ji v absolutním souřadnicovém systému a animuje transformaci (scale, translate, rotate) z počáteční do konečné polohy. Do FlightShuttle lze předat vlastní křivky pomocí vlastnosti flightShuttleBuilder, což umožňuje řídit trajektorii letu: letět po oblouku, odrážet se od okrajů nebo zpomalovat před příletem. Ve Flutter 3.16+ Hero podporuje animaci se zachováním clipRect a borderRadius, což dává efekt zaoblených karet transformujících se na obdélníkové obrazovky.

matchedGeometryEffect ve SwiftUI

Ve SwiftUI je ekvivalentem Hero Animation modifikátor matchedGeometryEffect, představený v iOS 14. Ten spojuje dva prvky pomocí identifikátoru (id) a jmenného prostoru (namespace). Na rozdíl od Flutter, kde Hero automaticky zachycuje přechod, SwiftUI vyžaduje explicitní deklaraci namespace a aplikaci modifikátoru na obě části scény — zdrojovou a cílovou — ve stejné hierarchii nebo pomocí GeometryReader.

Animace matchedGeometryEffect podporuje transformaci pozice, velikosti, rotace a poloměru rohů (cornerRadius). Pro dosažení plynulosti se Animation nastavuje v withAnimation. SwiftUI automaticky interpoluje mezistavy na základě výchozí křivky easeInOut. V iOS 17+ byla přidána podpora pro matchedTransitionSource pro TabView a NavigationStack — systémový analog Hero Animation s předdefinovanou trajektorií pro standardní přechody.

Shared Element Transition v Androidu

V Androidu se Hero Animation nazývá Shared Element Transition a je implementována pomocí ActivityOptions.makeSceneTransitionAnimation nebo FragmentTransaction.addSharedElement. Každý sdílený prvek je označen atributem transitionName v XML nebo pomocí kódu a Android je zodpovědný za animaci mezi aktivitami. Od Androidu 5.0 (API 21) se přechody staly součástí frameworku; předtím bylo nutné psát vlastní animace pomocí ValueAnimator.

V Jetpack Compose je Shared Element Transition implementován pomocí modifikátoru Modifier.sharedElement a Modifier.sharedBounds v kombinaci s AnimatedContent nebo SharedTransitionLayout (dostupný od Compose BOM 2024.01). Na rozdíl od systému View Compose líně překresluje prvky, takže sdílený prvek je ukládán do cache v CompositionLocal. Compose také podporuje animaci seznamu do detailu (list→detail) se zachováním scrollOffset — klíčová schopnost pro Hero přechody. Podle Google I/O 2024 funguje Compose Shared Element Transition o 15 % rychleji než verze View díky přímému řízení RenderNode.

Příklady kódu

Flutter: Hero s obrázkem

Základní příklad Hero Animation ve Flutter: miniatura na obrazovce seznamu a obrázek přes celou obrazovku na obrazovce detailů. Tag image-hero spojuje dva widgety a FlightShuttle automaticky animuje přechod z karty na celou obrazovku.

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

Klíčový bod — stejný tag (image-hero) na obou obrazovkách. Flutter přiřazuje widgety Hero podle tagu během přechodu trasy. Pokud se tagy liší, animace se nespustí — prvek se jednoduše objeví na nové obrazovce bez přechodu. Pro tag lze použít jakýkoli typ, ale řetězce jsou nejspolehlivější volbou.

SwiftUI: matchedGeometryEffect mezi obrazovkami

Příklad Hero Animation ve SwiftUI s NavigationStack. Modifikátor matchedGeometryEffect spojuje obrázek v seznamu a detailní obrazovce pomocí namespace a withAnimation spouští plynulý přechod.

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 vytváří jmenný prostor pro identifikátor matchedGeometryEffect. Oba prvky používají stejné id (hero) a sdílený namespace. Při přepnutí isDetailShown SwiftUI animuje transformaci velikosti, pozice a cornerRadius mezi dvěma pohledy a vytváří efekt hrdiny. Pro standardní NavigationLink s iOS 16+ použijte navigationTransition(.hero).

Android: Shared Element Transition s Jetpack Compose

Příklad Shared Element Transition v Jetpack Compose s SharedTransitionLayout. Modifikátor sharedElement animuje sdílený prvek mezi obrazovkami během navigace.

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+) obaluje hierarchii a modifikátor sharedElement s klíčem hero spojuje počáteční a koncové prvky. AnimatedContent přepíná stav a Compose automaticky animuje velikost, pozici a tvar obrázku. Klíčovou výhodou je podpora animace bounds a clipBounds, což dává efekt zaoblení karty během přechodu.

Často kladené otázky

Funguje Hero Animation na iOS a Androidu?

Ano, Hero Animation je k dispozici na obou platformách. V iOS se používá matchedGeometryEffect ve SwiftUI nebo UINavigationController.transitioningDelegate. V Androidu — přechod ActivityOptions.makeSceneTransitionAnimation nebo Shared Element Transition v Jetpack Compose s Modifier.sharedElement. Flutter implementuje Hero Animation pomocí widgetu Hero, který funguje na obou platformách identicky.

Lze animovat více prvků současně?

Ano, Hero Animation podporuje více sdílených prvků. Ve Flutter zadejte několik Hero s různými tagy. Ve SwiftUI použijte matchedGeometryEffect s různými identifikátory na každém prvku. Všechny animace se provádějí paralelně — každý prvek cestuje po své vlastní trajektorii.

Zpomaluje se Hero Animation na velkých obrázcích?

Výkon závisí na implementaci. Flutter FlightShuttle zpracovává obrázky přes RenderObject, což poskytuje 60fps. SwiftUI matchedGeometryEffect je vektorizovaný. Pro velké obrázky použijte precacheImage ve Flutter nebo prefetch ve SwiftUI — to eliminuje zpoždění při prvním přechodu. Android Compose vyžaduje líné načítání přes AsyncImage.

Čím se Hero liší od standardního push přechodu?

Standardní push přechod posouvá celou obrazovku a vytváří iluzi hromádky karet. Hero Animation fixuje vizuální pozornost na jednom prvku — uživatel vidí jeho nepřetržitý pohyb, což zlepšuje porozumění vztahu mezi obrazovkami. Push je vhodný pro hierarchickou navigaci, Hero — pro zvýraznění obsahu.

Jsou potřeba další knihovny pro Hero Animation?

Ne, Hero Animation je implementována vestavěnými prostředky. Ve Flutter je to widget Hero z balíčku flutter/material.dart. Ve SwiftUI — modifikátor matchedGeometryEffect. V Android Jetpack Compose — SharedTransitionLayout a Modifier.sharedElement ze standardního Compose. Další knihovny jsou potřeba pouze pro nestandardní trajektorie nebo složité transformace.

Shrnutí

  • Hero Animation — animace sdíleného prvku mezi obrazovkami, zachovávající vizuální kontinuitu a snižující kognitivní zátěž.
  • Flutter Hero s tagem a FlightShuttle — nejjednodušší způsob implementace animace hrdiny bez ruční správy animace.
  • SwiftUI matchedGeometryEffect s @Namespace.id animuje pozici, velikost a cornerRadius prvku mezi dvěma stavy.
  • Android SharedTransitionLayout s Modifier.sharedElement v Jetpack Compose zajišťuje přechod s cachováním RenderNode.
  • Všechny tři frameworky podporují více sdílených prvků s paralelní animací.
  • FlightShuttle umožňuje přizpůsobení trajektorie letu pomocí flightShuttleBuilder ve Flutter.
  • Hero Animation je doporučena Material Design pro obrazovky typu seznam-detail, galerie a karty.

Vyvineme mobilní aplikaci na klíč

IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.

Prodiskutovat projekt

Přečtěte si také