Hero Animation: mi ez, közös elemek animációja Flutter-ben

Szerző: IT Sectr Megjelenés: 2026-03-02 Olvasási idő: 8 perc
Hero Animation — egy animációs minta, amelyben egy felületi elem finoman mozog és transzformálódik a képernyők között, létrehozva egy „hős" hatását, aki egyik kontextusból a másikba utazik. A szokásos push/pop átmenettel ellentétben a hős animáció megőrzi a vizuális folytonosságot: a felhasználó látja, hogyan válik egy miniatűr teljes képernyős képpé, vagy hogyan mozog egy cella fejléce a képernyő fejlécébe. A Google Material Design (2025) szerint a Hero Animation kognitív terhelést 30%-kal csökkenti a lista és részletek közötti navigáció során — a felhasználó nem veszíti el a fókuszt az elem pozíciójának megjegyzésének köszönhetően. Flutter-ben a minta a Hero widgeten keresztül, SwiftUI-ban a matchedGeometryEffect módosítón keresztül, Jetpack Compose-ban a Modifier.sharedElement-en keresztül valósul meg.

Főbb pontok

  • Hero Animation — egy közös elem finom átmenete a képernyők között, megőrizve a vizuális kontextust a felhasználó számára.
  • Flutter Hero — widget, amely automatikusan megtalálja a párt tag alapján és elindítja a FlightShuttle animációt a képernyők között.
  • matchedGeometryEffect — SwiftUI módosító, amely azonosítón keresztül köti össze az elemeket namespace-ben az animált átmenethez.
  • Shared Element Transition — Android mechanizmus, amely ActivityOptions vagy Modifier.sharedElement segítségével valósul meg Jetpack Compose-ban.
  • FlightShuttle — Flutter belső mechanizmusa, amely a Hero widget pályáját és transzformációját kezeli az átmenet során.

Mi a Hero Animation?

Hero Animation — egy felületi animációs technika, amelyben egy közös elem finoman mozog a forrásképernyőről a célképernyőre, megőrizve identitását. A név a „hős" metaforáját tükrözi — a kulcselemet, amely áthalad a képernyők közötti színpadi átmeneten, felkeltve a felhasználó figyelmét. A szokásos átmenetekkel ellentétben a Hero Animation nem szakítja meg a vizuális kapcsolatot: a felhasználó látja az elem folyamatos pályáját, ami javítja a navigációs hierarchia megértését.

A Hero Animation koncepciója a Material Design-ban gyökerezik, mint ajánlás lista-részlet (list-detail) képernyőkhöz, galériákhoz és kártyákhoz. Az animációs algoritmus három fázisból áll: a forrásképernyőn az elem kiemelésre kerül és rögzítésre kerül abszolút pozícióban; az átmenet során skálázódik, mozog és alakot vált a célkonténernek megfelelően; a végső képernyőn az elem beépül az új elrendezésbe. A Material Design 3 szerint a helyesen implementált hős animáció 22%-kal csökkenti a hibás „vissza" kattintások számát.

Hero Flutter-ben: tagek és FlightShuttle

Flutter-ben a Hero Animation a Hero widgeten keresztül valósul meg, amely elfogadja a kötelező tag paramétert — egy egyedi azonosítót. A Flutter automatikusan megtalálja az azonos taggal rendelkező Hero párt a forrás- és célútvonalakon, és elindítja az animációt. A Hero widget nem igényel kézi animációkezelést — a keretrendszer maga számítja ki a pályát és alkalmazza a transzformációt a belső FlightShuttle objektumon keresztül.

FlightShuttle — egy láthatatlan „űrsikló", amely az átmenet pillanatában létrehozza a Hero widget átfedő másolatát, elhelyezi azt az abszolút koordináta-rendszerben, és animálja a transzformációt (scale, translate, rotate) a kezdeti pozíciótól a végső pozícióig. A FlightShuttle-be egyedi görbék adhatók át a flightShuttleBuilder tulajdonságon keresztül, lehetővé téve a repülési pálya vezérlését: ívben repülés, határokról való pattogás vagy lassulás érkezés előtt. Flutter 3.16+-ban a Hero támogatja az animációt a clipRect és borderRadius megőrzésével, ami lekerekített kártyák hatását kelti, amelyek téglalap alakú képernyőkké alakulnak.

matchedGeometryEffect SwiftUI-ban

SwiftUI-ban a Hero Animation megfelelője a matchedGeometryEffect módosító, amelyet iOS 14-ben vezettek be. Ez két elemet köt össze egy azonosító (id) és névtér (namespace) segítségével. Ellentétben Flutter-rel, ahol a Hero automatikusan elfogja az átmenetet, a SwiftUI megköveteli a namespace explicit deklarálását és a módosító alkalmazását a jelenet mindkét részére — forrásra és célra — ugyanazon a hierarchián belül vagy GeometryReader-en keresztül.

A matchedGeometryEffect animáció támogatja a pozíció, méret, forgatás és saroksugár (cornerRadius) transzformációját. A simaság érdekében az Animation beállításra kerül a withAnimation-ban. A SwiftUI automatikusan interpolálja a köztes állapotokat az alapértelmezett easeInOut görbe alapján. iOS 17+-ban hozzáadásra került a matchedTransitionSource támogatása TabView és NavigationStack számára — a Hero Animation rendszerbeli megfelelője előre meghatározott pályával a szabványos átmenetekhez.

Shared Element Transition Android-ban

Android-ban a Hero Animation-t Shared Element Transition-nek hívják, és az ActivityOptions.makeSceneTransitionAnimation vagy FragmentTransaction.addSharedElement segítségével valósul meg. Minden közös elem megjelölésre kerül a transitionName attribútummal XML-ben vagy kódon keresztül, és az Android felel az aktivitások közötti animációért. Android 5.0-tól (API 21) kezdve az átmenetek a keretrendszer részévé váltak; előtte egyedi animációkat kellett írni ValueAnimator segítségével.

Jetpack Compose-ban a Shared Element Transition a Modifier.sharedElement és Modifier.sharedBounds módosítókon keresztül valósul meg AnimatedContent vagy SharedTransitionLayout kombinációjával (elérhető Compose BOM 2024.01-től). Ellentétben a View rendszerrel, a Compose lustán rajzolja újra az elemeket, így a közös elem gyorsítótárazásra kerül a CompositionLocal-ban. A Compose támogatja továbbá a lista→részlet (list→detail) animációt a scrollOffset megőrzésével — kulcsfontosságú képesség a Hero átmenetekhez. A Google I/O 2024 szerint a Compose Shared Element Transition 15%-kal gyorsabban működik, mint a View verzió a RenderNode közvetlen kezelésének köszönhetően.

Kódpéldák

Flutter: Hero képpel

Alapvető Hero Animation példa Flutter-ben: egy miniatűr a lista képernyőn és egy teljes képernyős kép a részlet képernyőn. Az image-hero tag összeköti a két widgetet, és a FlightShuttle automatikusan animálja az átmenetet a kártyáról a teljes képernyőre.

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

A kulcspont — ugyanaz a tag (image-hero) mindkét képernyőn. A Flutter a Hero widgeteket tag alapján párosítja az útvonal átmenet során. Ha a tagek különböznek, az animáció nem indul el — az elem egyszerűen megjelenik az új képernyőn átmenet nélkül. Bármilyen típus használható a taghez, de a karakterláncok a legmegbízhatóbb lehetőség.

SwiftUI: matchedGeometryEffect képernyők között

Hero Animation példa SwiftUI-ban NavigationStack-kel. A matchedGeometryEffect módosító összeköti a képet a listában és a részlet képernyőn a namespace-en keresztül, a withAnimation pedig elindítja a finom átmenetet.

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 létrehoz egy névteret a matchedGeometryEffect azonosító számára. Mindkét elem ugyanazt az id-t (hero) és megosztott namespace-t használja. Amikor az isDetailShown változik, a SwiftUI animálja a méret, pozíció és cornerRadius transzformációját a két nézet között, létrehozva a hős hatást. A szabványos NavigationLink-hez iOS 16+-tól használja a navigationTransition(.hero)-t.

Android: Shared Element Transition Jetpack Compose-zal

Shared Element Transition példa Jetpack Compose-ban SharedTransitionLayout-val. A sharedElement módosító animálja a közös elemet a képernyők között navigáció során.

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()
                    )
                }
            }
        }
    }
}

A Compose SharedTransitionLayout (Compose BOM 2024.01+) beburkolja a hierarchiát, és a sharedElement módosító a hero kulccsal összeköti a kezdeti és végső elemeket. Az AnimatedContent váltja az állapotot, és a Compose automatikusan animálja a kép méretét, pozícióját és alakját. A kulcsfontosságú előny — a bounds és clipBounds animáció támogatása, ami a kártya lekerekítésének hatását adja az átmenet során.

Gyakran Ismételt Kérdések

Működik a Hero Animation iOS-en és Android-on?

Igen, a Hero Animation mindkét platformon elérhető. iOS-ben a matchedGeometryEffect SwiftUI-ban vagy az UINavigationController.transitioningDelegate használatos. Android-ban — az ActivityOptions.makeSceneTransitionAnimation átmenet vagy Shared Element Transition Jetpack Compose-ban Modifier.sharedElement-lel. A Flutter a Hero widgeten keresztül valósítja meg a Hero Animation-t, amely mindkét platformon azonosan működik.

Lehet egyszerre több elemet animálni?

Igen, a Hero Animation támogatja a több közös elemet. Flutter-ben adjon meg több Hero-t különböző tagekkel. SwiftUI-ban használjon matchedGeometryEffect-et különböző azonosítókkal minden elemen. Az összes animáció párhuzamosan fut — minden elem a saját pályáján utazik.

Lassítja-e a Hero Animation a nagy képeket?

A teljesítmény az implementációtól függ. A Flutter FlightShuttle a képeket RenderObject-en keresztül dolgozza fel, ami 60fps-t biztosít. A SwiftUI matchedGeometryEffect vektorizált. Nagy képekhez használja a precacheImage-et Flutter-ben vagy a prefetch-et SwiftUI-ban — ez kiküszöböli a késéseket az első átmenetnél. Az Android Compose lusta betöltést igényel AsyncImage-en keresztül.

Miben különbözik a Hero a szabványos push átmenettől?

A szabványos push átmenet az egész képernyőt mozgatja, kártyaköteg illúzióját keltve. A Hero Animation egy elemre rögzíti a vizuális figyelmet — a felhasználó látja annak folyamatos mozgását, ami javítja a képernyők közötti kapcsolat megértését. A push a hierarchikus navigációhoz jó, a Hero — a tartalom kiemeléséhez.

Szükségesek-e további könyvtárak a Hero Animation-hez?

Nem, a Hero Animation beépített eszközökkel van implementálva. Flutter-ben ez a Hero widget a flutter/material.dart csomagból. SwiftUI-ban — a matchedGeometryEffect módosító. Android Jetpack Compose-ban — SharedTransitionLayout és Modifier.sharedElement a szabványos Compose-ból. További könyvtárak csak nem szabványos pályákhoz vagy összetett transzformációkhoz szükségesek.

Összefoglalás

  • Hero Animation — közös elem animációja képernyők között, megőrizve a vizuális folytonosságot és csökkentve a kognitív terhelést.
  • Flutter Hero taggel és FlightShuttle-lel — a legegyszerűbb mód a hős animáció implementálására kézi animációkezelés nélkül.
  • SwiftUI matchedGeometryEffect @Namespace.id-vel animálja a pozíciót, méretet és cornerRadius-t két állapot között.
  • Android SharedTransitionLayout Modifier.sharedElement-lel Jetpack Compose-ban RenderNode gyorsítótárazással biztosítja az átmenetet.
  • Mindhárom keretrendszer támogatja a több közös elemet párhuzamos animációval.
  • A FlightShuttle lehetővé teszi a repülési pálya testreszabását a flightShuttleBuilder segítségével Flutter-ben.
  • A Hero Animation-t a Material Design ajánlja lista-részlet képernyőkhöz, galériákhoz és kártyákhoz.

Kulcsrakész mobilalkalmazást fejlesztünk

Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.

Projekt megbeszélése

Olvassa el is