Hero Animation: apa itu, animasi elemen bersama di Flutter

Penulis: IT Sectr Diterbitkan: 2026-03-02 Waktu membaca: 8 mnt
Hero Animation — pola animasi di mana elemen antarmuka bergerak mulus dan bertransformasi antar layar, menciptakan efek "pahlawan" yang bepergian dari satu konteks ke konteks lain. Berbeda dengan transisi standar push/pop, animasi pahlawan mempertahankan kontinuitas visual: pengguna melihat bagaimana thumbnail berubah menjadi gambar layar penuh atau bagaimana judul sel berpindah ke judul layar. Menurut Google Material Design (2025), Hero Animation mengurangi beban kognitif sebesar 30% saat navigasi antara daftar dan detail — pengguna tidak kehilangan fokus berkat mengingat posisi elemen. Di Flutter pola ini diimplementasikan melalui widget Hero, di SwiftUI — melalui modifier matchedGeometryEffect, di Jetpack Compose — melalui Modifier.sharedElement.

Poin Utama

  • Hero Animation — transisi mulus elemen bersama antar layar, mempertahankan konteks visual bagi pengguna.
  • Flutter Hero — widget yang secara otomatis menemukan pasangan berdasarkan tag dan menjalankan animasi FlightShuttle antar layar.
  • matchedGeometryEffect — modifier SwiftUI yang menghubungkan elemen melalui pengenal di namespace untuk transisi animasi.
  • Shared Element Transition — mekanisme Android, diimplementasikan melalui ActivityOptions atau Modifier.sharedElement di Jetpack Compose.
  • FlightShuttle — mekanisme internal Flutter yang mengelola lintasan dan transformasi widget Hero selama transisi.

Apa itu Hero Animation?

Hero Animation — teknik animasi antarmuka di mana elemen bersama bergerak mulus dari layar sumber ke layar tujuan, mempertahankan identitasnya. Nama ini mencerminkan metafora "pahlawan" — elemen kunci yang melewati transisi panggung antar layar, menarik perhatian pengguna. Berbeda dengan transisi standar, Hero Animation tidak memutuskan hubungan visual: pengguna melihat lintasan kontinu elemen, yang meningkatkan pemahaman hierarki navigasi.

Konsep Hero Animation tertanam dalam Material Design sebagai rekomendasi untuk layar daftar-detail (list-detail), galeri, dan kartu. Algoritma animasi terdiri dari tiga fase: di layar sumber, elemen disorot dan diperbaiki pada posisi absolut; selama transisi, elemen diskalakan, dipindahkan, dan diubah bentuknya sesuai dengan wadah tujuan; di layar akhir, elemen diintegrasikan ke dalam tata letak baru. Menurut Material Design 3, animasi pahlawan yang diimplementasikan dengan benar mengurangi jumlah klik "kembali" yang salah sebesar 22%.

Hero di Flutter: tag dan FlightShuttle

Di Flutter, Hero Animation diimplementasikan melalui widget Hero, yang menerima parameter wajib tag — pengenal unik. Flutter secara otomatis menemukan pasangan Hero dengan tag yang sama di rute sumber dan tujuan dan menjalankan animasi. Widget Hero tidak memerlukan pengelolaan animasi manual — framework menghitung sendiri lintasan dan menerapkan transformasi melalui objek internal FlightShuttle.

FlightShuttle — "pesawat ulang-alik" tak terlihat yang pada saat transisi membuat salinan overlay dari widget Hero, memposisikannya dalam sistem koordinat absolut dan menganimasikan transformasi (scale, translate, rotate) dari posisi awal ke posisi akhir. Kurva khusus dapat diteruskan ke FlightShuttle melalui properti flightShuttleBuilder, memungkinkan kontrol lintasan terbang: terbang dalam busur, memantul dari batas, atau melambat sebelum tiba. Di Flutter 3.16+, Hero mendukung animasi dengan mempertahankan clipRect dan borderRadius, memberikan efek kartu membulat yang berubah menjadi layar persegi panjang.

matchedGeometryEffect di SwiftUI

Di SwiftUI, padanan Hero Animation adalah modifier matchedGeometryEffect, diperkenalkan di iOS 14. Ini menghubungkan dua elemen melalui pengenal (id) dan ruang nama (namespace). Berbeda dengan Flutter di mana Hero secara otomatis mencegat transisi, SwiftUI memerlukan deklarasi eksplisit namespace dan penerapan modifier ke kedua bagian adegan — sumber dan tujuan — dalam hierarki yang sama atau melalui GeometryReader.

Animasi matchedGeometryEffect mendukung transformasi posisi, ukuran, rotasi, dan radius sudut (cornerRadius). Untuk mencapai kehalusan, Animation diatur dalam withAnimation. SwiftUI secara otomatis menginterpolasi keadaan antara berdasarkan kurva default easeInOut. Di iOS 17+, dukungan untuk matchedTransitionSource ditambahkan untuk TabView dan NavigationStack — analog sistem Hero Animation dengan lintasan yang telah ditentukan untuk transisi standar.

Shared Element Transition di Android

Di Android, Hero Animation disebut Shared Element Transition dan diimplementasikan melalui ActivityOptions.makeSceneTransitionAnimation atau FragmentTransaction.addSharedElement. Setiap elemen bersama ditandai dengan atribut transitionName di XML atau melalui kode, dan Android bertanggung jawab atas animasi antar aktivitas. Mulai Android 5.0 (API 21), transisi menjadi bagian dari framework; sebelumnya, animasi khusus harus ditulis melalui ValueAnimator.

Di Jetpack Compose, Shared Element Transition diimplementasikan melalui modifier Modifier.sharedElement dan Modifier.sharedBounds dalam kombinasi dengan AnimatedContent atau SharedTransitionLayout (tersedia sejak Compose BOM 2024.01). Berbeda dengan sistem View, Compose menggambar ulang elemen secara malas, sehingga elemen bersama di-cache di CompositionLocal. Compose juga mendukung animasi daftar ke detail (list→detail) dengan mempertahankan scrollOffset — kemampuan kunci untuk transisi Hero. Menurut Google I/O 2024, Compose Shared Element Transition bekerja 15% lebih cepat dari versi View berkat pengelolaan RenderNode secara langsung.

Contoh Kode

Flutter: Hero dengan gambar

Contoh dasar Hero Animation di Flutter: thumbnail di layar daftar dan gambar layar penuh di layar detail. Tag image-hero menghubungkan dua widget, dan FlightShuttle secara otomatis menganimasikan transisi dari kartu ke layar penuh.

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

Poin kunci — tag yang sama (image-hero) di kedua layar. Flutter mencocokkan widget Hero berdasarkan tag selama transisi rute. Jika tag berbeda, animasi tidak akan dimulai — elemen hanya akan muncul di layar baru tanpa transisi. Jenis apa pun dapat digunakan untuk tag, tetapi string adalah opsi yang paling dapat diandalkan.

SwiftUI: matchedGeometryEffect antar layar

Contoh Hero Animation di SwiftUI dengan NavigationStack. Modifier matchedGeometryEffect menghubungkan gambar di daftar dan layar detail melalui namespace, dan withAnimation memulai transisi mulus.

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 membuat ruang nama untuk pengenal matchedGeometryEffect. Kedua elemen menggunakan id yang sama (hero) dan namespace bersama. Saat isDetailShown diubah, SwiftUI menganimasikan transformasi ukuran, posisi, dan cornerRadius antara dua tampilan, menciptakan efek pahlawan. Untuk NavigationLink standar dengan iOS 16+, gunakan navigationTransition(.hero).

Android: Shared Element Transition dengan Jetpack Compose

Contoh Shared Element Transition di Jetpack Compose dengan SharedTransitionLayout. Modifier sharedElement menganimasikan elemen bersama antar layar selama navigasi.

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+) membungkus hierarki, dan modifier sharedElement dengan kunci hero menghubungkan elemen awal dan akhir. AnimatedContent mengubah keadaan, dan Compose secara otomatis menganimasikan ukuran, posisi, dan bentuk gambar. Keuntungan utama — dukungan untuk animasi bounds dan clipBounds, yang memberikan efek pembulatan kartu selama transisi.

Pertanyaan yang Sering Diajukan

Apakah Hero Animation berfungsi di iOS dan Android?

Ya, Hero Animation tersedia di kedua platform. Di iOS digunakan matchedGeometryEffect di SwiftUI atau UINavigationController.transitioningDelegate. Di Android — transisi ActivityOptions.makeSceneTransitionAnimation atau Shared Element Transition di Jetpack Compose dengan Modifier.sharedElement. Flutter mengimplementasikan Hero Animation melalui widget Hero yang bekerja identik di kedua platform.

Bisakah beberapa elemen dianimasikan secara bersamaan?

Ya, Hero Animation mendukung banyak elemen bersama. Di Flutter, tentukan beberapa Hero dengan tag berbeda. Di SwiftUI, gunakan matchedGeometryEffect dengan pengenal berbeda pada setiap elemen. Semua animasi dijalankan secara paralel — setiap elemen bepergian di lintasannya sendiri.

Apakah Hero Animation melambat pada gambar besar?

Kinerja tergantung pada implementasi. Flutter FlightShuttle memproses gambar melalui RenderObject, memberikan 60fps. SwiftUI matchedGeometryEffect telah divektorisasi. Untuk gambar besar, gunakan precacheImage di Flutter atau prefetch di SwiftUI — ini menghilangkan penundaan pada transisi pertama. Android Compose memerlukan pemuatan malas melalui AsyncImage.

Apa perbedaan Hero dengan transisi push standar?

Transisi push standar memindahkan seluruh layar, menciptakan ilusi tumpukan kartu. Hero Animation memusatkan perhatian visual pada satu elemen — pengguna melihat gerakan kontinunya, yang meningkatkan pemahaman hubungan antar layar. Push baik untuk navigasi hierarkis, Hero — untuk menonjolkan konten.

Apakah diperlukan pustaka tambahan untuk Hero Animation?

Tidak, Hero Animation diimplementasikan dengan alat bawaan. Di Flutter, ini adalah widget Hero dari paket flutter/material.dart. Di SwiftUI — modifier matchedGeometryEffect. Di Android Jetpack Compose — SharedTransitionLayout dan Modifier.sharedElement dari Compose standar. Pustaka tambahan hanya diperlukan untuk lintasan non-standar atau transformasi kompleks.

Ringkasan

  • Hero Animation — animasi elemen bersama antar layar, mempertahankan kontinuitas visual dan mengurangi beban kognitif.
  • Flutter Hero dengan tag dan FlightShuttle — cara termudah untuk mengimplementasikan animasi pahlawan tanpa pengelolaan animasi manual.
  • SwiftUI matchedGeometryEffect dengan @Namespace.id menganimasikan posisi, ukuran, dan cornerRadius elemen antara dua keadaan.
  • Android SharedTransitionLayout dengan Modifier.sharedElement di Jetpack Compose menyediakan transisi dengan caching RenderNode.
  • Ketiga framework mendukung banyak elemen bersama dengan animasi paralel.
  • FlightShuttle memungkinkan penyesuaian lintasan terbang melalui flightShuttleBuilder di Flutter.
  • Hero Animation direkomendasikan oleh Material Design untuk layar daftar-detail, galeri, dan kartu.

Kami akan mengembangkan aplikasi seluler turnkey

IT Sectr membuat aplikasi iOS dan Android untuk startup dan bisnis sejak 2017. Kami akan memberi saran dan mengusulkan solusi terbaik.

Diskusikan proyek

Baca juga