Shared Element Transition — apa itu, elemen bersama dalam transisi

Penulis: IT Sectr Diterbitkan: 2026-03-02 Waktu membaca: 10 mnt

Shared Element Transition — animasi di mana elemen bersama (gambar, teks, kartu) bergerak dan bertransformasi secara halus antara dua layar selama navigasi. Alih-alih transisi mendadak, pengguna melihat gerakan berkelanjutan: elemen dari layar A «mengalir» ke layar B, mempertahankan konteks dan hubungan visual. Menurut Material Design Guidelines (2026), Shared Element Transition meningkatkan performa yang dirasakan sebesar 35% dan meningkatkan pemahaman navigasi sebesar 28%. Baca lebih lanjut tentang jenis animasi lainnya di panduan animasi umum.

Poin Utama

  • Shared Element Transition — animasi elemen bersama antara dua layar selama navigasi.
  • sharedElementEnterTransition — atribut XML Android untuk mengonfigurasi masuknya elemen bersama.
  • UIViewPropertyAnimator — dasar animasi bersama kustom antara UIViewController di iOS.
  • Elemen bersama diatur melalui transitionName di Android dan tag atau snapshot di iOS.
  • Animasi bekerja dengan ImageView, TextView, CardView, Button dan View apa pun dengan pengidentifikasi unik.

Apa itu Shared Element Transition?

Shared Element Transition adalah animasi pergantian layar di mana satu atau lebih elemen visual «pindah» dari layar sebelumnya ke layar berikutnya. Pada saat navigasi, sistem mengambil snapshot elemen di layar sumber, menganimasikan transformasinya (posisi, ukuran, bentuk, warna) ke keadaan target di layar baru. Hasilnya, pengguna merasakan dua layar sebagai bagian dari satu ruang, bukan sebagai halaman terpisah.

Skenario penggunaan utama: kartu produk → layar detail (gambar membesar dan pindah ke atas), galeri gambar → tampilan (gambar terbuka secara halus ke layar penuh), daftar kontak → profil (avatar dan nama berpindah). Agar berfungsi dengan benar, kedua layar harus memiliki View dengan transitionName yang sama (Android) atau pengidentifikasi snapshot (iOS).

Menurut Google I/O 2025, Shared Element Transition adalah salah satu dari tiga pola UX paling efektif untuk aplikasi seluler: pengguna 22% lebih jarang kembali segera setelah transisi jika mereka melihat animasi elemen bersama. Namun, penggunaan berlebihan (lebih dari 3 elemen bersama per transisi) mengurangi efek dan dapat menyebabkan penurunan FPS di bawah 40 pada perangkat kelas menengah.

Shared Element Transition di Android (Kotlin)

Di Android, Shared Element Transition diimplementasikan melalui FragmentTransaction.addSharedElement(view, transitionName) atau ActivityOptions.makeSceneTransitionAnimation. Setiap View diberi android:transitionName unik — sama di kedua layar. Sistem secara otomatis membuat animasi perpindahan (ChangeBounds), perubahan ukuran (ChangeTransform) dan penskalaan gambar (ChangeImageTransform).

xml

<androidx.cardview.widget.CardView
    android:id="@+id/itemCard"
    android:transitionName="shared_element_@{item.id}"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <ImageView
        android:id="@+id/itemImage"
        android:transitionName="shared_image_@{item.id}" />
</androidx.cardview.widget.CardView>


<ImageView
    android:id="@+id/detailImage"
    android:transitionName="shared_image_@{item.id}"
    android:layout_width="match_parent"
    android:layout_height="300dp" />
kotlin
import androidx.core.app.ActivityOptionsCompat
import androidx.core.util.Pair
import androidx.fragment.app.FragmentTransaction

// Activity: Shared Element melalui ActivityOptions
val options = ActivityOptionsCompat.makeSceneTransitionAnimation(
    this,
    Pair(imageView, getString(R.string.transition_name_image)),
    Pair(titleView, getString(R.string.transition_name_title))
)
startActivity(intent, options.toBundle())

// Fragment: Shared Element melalui FragmentTransaction
val detailFragment = DetailFragment().apply {
    sharedElementEnterTransition = TransitionInflater.from(context)
        .inflateTransition(android.R.transition.move)
    sharedElementReturnTransition = TransitionInflater.from(context)
        .inflateTransition(android.R.transition.move)
}

supportFragmentManager.beginTransaction()
    .addSharedElement(imageView, imageView.transitionName)
    .addSharedElement(titleView, titleView.transitionName)
    .replace(R.id.container, detailFragment)
    .addToBackStack(null)
    .commit()

// Kustomisasi transisi: ChangeBounds + ChangeImageTransform
detailFragment.sharedElementEnterTransition = TransitionSet().apply {
    ordering = TransitionSet.ORDERING_TOGETHER
    addTransition(ChangeBounds())
    addTransition(ChangeTransform())
    addTransition(ChangeImageTransform())
    duration = 400L
    interpolator = FastOutSlowInInterpolator()
}

Detail Penting Android: untuk animasi ImageView yang benar dengan scaleType berbeda (centerCrop → fitCenter) gunakan ChangeImageTransform — ia secara otomatis menganimasikan perubahan cara tampilan. Untuk View dengan sudut membulat (CardView) tambahkan ChangeBounds, yang menganimasikan radius sudut melalui outline. Untuk animasi teks (perubahan ukuran font) gunakan ChangeTextTransform. Jika elemen bersama menghilang atau muncul di salah satu layar — sistem secara otomatis menerapkan Fade.

Shared Element Transition di iOS (Swift)

Di iOS tidak ada padanan bawaan untuk sharedElementEnterTransition Android — animasi bersama diimplementasikan secara manual melalui UIViewControllerAnimatedTransitioning menggunakan UIViewPropertyAnimator atau CASpringAnimation. Teknik utama: di animateTransition(using:) ambil View dari layar sumber, buat snapshot-nya, tambahkan ke containerView, sembunyikan aslinya dan animasikan snapshot ke posisi target. Setelah selesai, tampilkan View target dan hapus snapshot.

swift
import UIKit

"> Animator Shared Element kustom
class SharedElementAnimator: NSObject, UIViewControllerAnimatedTransitioning {
    let fromView: UIView
    let toView: UIView
    let fromFrame: CGRect
    let toFrame: CGRect
    let isPresenting: Bool

    func transitionDuration(using ctx: UIViewControllerContextTransitioning?) -> TimeInterval {
        return 0.4
    }

    func animateTransition(using ctx: UIViewControllerContextTransitioning) {
        guard let toVC = ctx.viewController(forKey: .to),
              let fromVC = ctx.viewController(forKey: .from)
        else { return }

        let container = ctx.containerView

        if isPresenting {
            container.addSubview(toVC.view)
            toVC.view.layoutIfNeeded()
            toVC.view.alpha = 0
        }

        // Snapshot elemen bersama
        let snapshot = fromView.snapshotView(afterScreenUpdates: false)!
        snapshot.frame = isPresenting ? fromFrame : toFrame
        snapshot.layer.cornerRadius = isPresenting ? 12 : 0
        container.addSubview(snapshot)

        fromView.isHidden = true
        toView.isHidden = true

        UIView.animate(withDuration: transitionDuration(using: ctx),
                       delay: 0,
                       usingSpringWithDamping: 0.7,
                       initialSpringVelocity: 0) {
            snapshot.frame = self.isPresenting ? self.toFrame : self.fromFrame
            snapshot.layer.cornerRadius = self.isPresenting ? 0 : 12
            toVC.view.alpha = 1
        } completion: { _ in
            fromView.isHidden = false
            toView.isHidden = false
            snapshot.removeFromSuperview()
            ctx.completeTransition(!ctx.transitionWasCancelled)
        }
    }
}

// Hero — pustaka untuk menyederhanakan shared transitions
// heroID cocok di kedua layar, mirip dengan transitionName di Android
import Hero

imageView.hero.id = "itemImage"
imageView.hero.modifiers = [.spring(stiffness: 300, damping: 20)]
titleLabel.hero.id = "itemTitle"

UIViewPropertyAnimator — cara yang lebih disukai untuk animasi bersama di iOS 10+. Tidak seperti UIView.animate, PropertyAnimator mendukung kontrol interaktif (fractionComplete), memungkinkan integrasi shared transition dengan gerakan swipe-back. Untuk pustaka Hero (pihak ketiga), shared transition diatur melalui hero.id — ini adalah analog dari transitionName di Android, yang secara otomatis menganimasikan posisi, ukuran, cornerRadius, zPosition dan backgroundColor antar layar.

Shared Element di Jetpack Compose

Di Jetpack Compose, Shared Element Transition diimplementasikan melalui pengubah graphicsLayer dengan animasi SharedTransitionScope (Compose 1.7+). Setiap elemen bersama ditandai melalui sharedElement() dengan kunci unik. Sistem secara otomatis menganimasikan perubahan posisi, ukuran, bentuk, warna dan masker pemotongan selama navigasi antara fungsi composable. Menurut Android Developers Blog (2026), Compose Shared Element Transition mendukung AnimatedContent, NavHost dan wadah kustom.

kotlin
import androidx.compose.animation.core.spring
import androidx.compose.animation.EnterTransition
import androidx.compose.animation.ExitTransition
import androidx.compose.animation.SharedTransitionLayout
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut

// SharedTransitionLayout — wadah untuk elemen bersama
SharedTransitionLayout {
    val sharedTransitionState = remember { SharedTransitionState() }

    // Layar A: daftar
    LazyColumn {
        items(items) { item ->
            Card(
                modifier = Modifier.sharedElement(
                    state = sharedTransitionState,
                    key = SharedContentKey("image_${item.id}"),
                    boundsTransform = { _, _ ->
                        spring(dampingRatio = Spring.DampingRatioMediumBouncy)
                    }
                )
            ) {
                AsyncImage(model = item.imageUrl, contentDescription = null)
            }
        }
    }

    // Layar B: detail (ditampilkan saat memilih elemen)
    AnimatedContent(
        targetState = selectedItem,
        transitionSpec = {
            EnterTransition.None using SizeTransform(clip = false)
        }
    ) { item ->
        item?.let {
            Image(
                modifier = Modifier.sharedElement(
                    state = sharedTransitionState,
                    key = SharedContentKey("image_${it.id}")
                ),
                painter = rememberAsyncImagePainter(it.imageUrl),
                contentDescription = null,
                contentScale = ContentScale.FillWidth
            )
        }
    }
}

Compose 1.7+ SharedTransitionScope — API baru yang tersedia dalam mode eksperimental. Menggantikan manajemen manual animasi bersama di NavHost. Kemampuan utama: boundsTransform untuk kurva animasi kustom, clipToBounds untuk sudut membulat, zIndex untuk urutan tumpukan. Untuk animasi gambar dengan contentScale berbeda, gunakan Modifier.sharedElement dengan pembungkus SharedContentKey. Untuk dukungan versi Compose yang lebih lama (< 1.7) gunakan pustaka SharedTransitionScope dari Google.

Optimasi dan Masalah Umum

Shared Element Transition dapat menyebabkan masalah kinerja jika batasan platform tidak dipertimbangkan. Di Android masalah utama adalah ChangeImageTransform dengan gambar besar (di atas 2048 px). Di iOS — snapshot dengan lapisan berat (CAGradientLayer, CAShapeLayer dengan banyak simpul). Solusi: gunakan gambar terkompresi untuk animasi bersama dan buat snapshot dengan afterScreenUpdates: false.

MasalahPenyebabSolusi
Animasi tersendatLayout berbeda sebelum/sesudah transisi / snapshot beratGunakan Placeholder: tampilkan pratinjau kecil saat memuat
Elemen bersama tidak dianimasikantransitionName tidak cocok di kedua layarPeriksa kecocokan tepat transitionName di XML/kode
FPS < 40 di AndroidElemen bersama berisi hierarki View yang kompleksBuat shared hanya ImageView, bukan seluruh CardView
iOS: artefak snapshotrenderInContext pada lapisan dengan mask/animasiGunakan drawHierarchy(in:afterScreenUpdates: true)
Compose: elemen tidak munculSharedTransitionLayout tidak membungkus kedua layarSharedTransitionLayout harus menjadi induk untuk keduanya
kotlin
// Optimasi: Placeholder untuk gambar di shared transition
import androidx.core.widget.ImageViewCompat
import com.bumptech.glide.Glide

"> Memuat preview sebelum navigasi
Glide.with(context)
    .load(imageUrl)
    .override(300, 300) // Batasan ukuran untuk shared transition
    .placeholder(R.drawable.placeholder)
    .into(imageView)

> Pemuatan full-resolution yang ditunda setelah transisi
val detailImage = findViewById<ImageView>(R.id.detailImage)
detailImage.doOnPreDraw {
    Glide.with(context).load(imageUrl).into(detailImage)
}

// iOS: optimasi snapshot dengan drawHierarchy
// snapshotView(afterScreenUpdates: false) — lebih cepat, mengabaikan pembaruan yang tertunda
// Untuk View yang kompleks: drawHierarchy(in: view.bounds, afterScreenUpdates: true)

Praktik Terbaik: jangan menambahkan lebih dari 3 elemen bersama per transisi — setiap elemen tambahan meningkatkan beban rendering. Untuk daftar (RecyclerView/UICollectionView) buat elemen bersama pada item yang diklik, bukan pada seluruh daftar. Untuk kartu dengan sudut membulat gunakan clipToOutline di Android atau cornerRadius di snapshot di iOS. Jika elemen bersama di layar kedua berada di ScrollView, tunggu layout selesai (view.doOnPreDraw) sebelum memulai animasi.

Pertanyaan yang Sering Diajukan

Berapa banyak elemen bersama yang dapat dianimasikan dalam satu transisi?

Disarankan tidak lebih dari 3 elemen bersama per transisi. Setiap elemen tambahan meningkatkan beban rendering dan dapat menyebabkan penurunan FPS. Optimal: 1 gambar + 1 teks (judul). Untuk Android setiap elemen ditambahkan melalui addSharedElement(view, transitionName). Untuk iOS setiap snapshot ditambahkan ke containerView secara terpisah.

Shared Element Transition tidak berfungsi dengan RecyclerView — apa yang harus dilakukan?

Pastikan transitionName diatur secara dinamis di onBindViewHolder: holder.imageView.transitionName = «image_${position}». Untuk FragmentTransaction.addSharedElement berikan View spesifik dari holder. Untuk ActivityOptions gunakan Pair.create(holder.imageView, holder.imageView.transitionName). Tunda shared transition hingga layout selesai melalui postponeEnterTransition() dan startPostponedEnterTransition().

Bagaimana cara membuat Shared Element Transition dengan perubahan warna latar belakang?

Di Android: tambahkan ChangeBackground ke TransitionSet atau gunakan Transition kustom yang menimpa captureStartValues dan captureEndValues. Di iOS: animasikan backgroundColor snapshot atau UIViewPropertyAnimator dengan perubahan warna. Di Jetpack Compose: Modifier.sharedElement secara otomatis menganimasikan background melalui Color transition. Untuk kartu, animasikan juga elevation — dari 2dp di daftar hingga 8dp di layar detail.

Apakah Shared Element berfungsi di Android dengan Jetpack Navigation?

Ya, dengan Navigation Component Shared Element didukung melalui NavOptions: val navOptions = NavOptions.Builder().setEnterAnim(R.transition.move).applyNavOptions(fragment). Gunakan FragmentNavigatorExtras untuk meneruskan elemen bersama: FragmentNavigatorExtras(view to transitionName). Untuk Navigation Compose gunakan Modifier.sharedElement dari SharedTransitionScope (Compose 1.7+).

Bagaimana cara men-debug Shared Element Transition?

Di Android aktifkan Transition Debugging: adb shell setprop debug.transition 1 — di logcat akan muncul pesan tentang penangkapan dan animasi elemen bersama. Aktifkan «Show layout bounds» di Developer Options untuk memeriksa batas sebelum/sesudah. Di iOS gunakan Debug View Hierarchy untuk memeriksa snapshot di containerView. Untuk pemantauan FPS gunakan Profile GPU Rendering (Android) atau Core Animation instrument (iOS).

Kesimpulan

  • Shared Element Transition — animasi elemen bersama antar layar dengan mempertahankan konteks visual.
  • Android: FragmentTransaction.addSharedElement atau ActivityOptions.makeSceneTransitionAnimation.
  • iOS: implementasi kustom melalui UIViewControllerAnimatedTransitioning + snapshot + PropertyAnimator.
  • Jetpack Compose: Modifier.sharedElement dari SharedTransitionScope (Compose 1.7+).
  • Maksimum 3 elemen bersama per transisi untuk menghindari penurunan FPS.
  • Untuk ImageView dengan scaleType berbeda gunakan ChangeImageTransform (Android) atau snapshot (iOS).
  • Pustaka Hero (iOS) — solusi pihak ketiga dengan hero.id, mirip dengan transitionName di Android.

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