Shadow: esensi, membuat bayangan dalam aplikasi mobile

Penulis: IT Sectr Diterbitkan: 2026-02-28 Waktu membaca: 9 mnt

Shadow (bayangan) — efek visual dalam antarmuka mobile yang menciptakan ilusi kedalaman dan hierarki antar elemen. Bayangan menunjukkan elemen mana yang berada lebih tinggi di bidang-Z dan oleh karena itu lebih penting atau interaktif. Menurut Material Design Guidelines (Google, 2026), bayangan sistem menggunakan lima ketinggian kunci (elevation): 0 dp (datar), 1–4 dp (tombol), 6–12 dp (kartu), 16–24 dp (dialog), 24+ dp (jendela modal). Lebih lanjut tentang ketinggian elemen baca di artikel tentang Elevation.

Poin Utama

  • Bayangan (shadow) — efek visual yang menciptakan kedalaman dengan menggelapkan area di bawah elemen dengan keburaman di tepinya.
  • Di iOS bayangan diatur melalui properti CALayer: shadowColor, shadowOffset, shadowRadius, shadowOpacity.
  • Di Android bayangan dikelola melalui elevation (Material Design) atau rendering khusus melalui OutlineProvider.
  • Jetpack Compose mendukung bayangan melalui Modifier.shadow() dan elevation di Card/Surface.
  • Di Flutter bayangan diimplementasikan melalui BoxShadow di BoxDecoration atau melalui PhysicalModel.

Apa itu bayangan dalam UI?

Bayangan (shadow) dalam antarmuka pengguna — efek visual yang meniru pencahayaan dan kedalaman. Bayangan dibuat dengan menggambar salinan gelap buram dari elemen yang digeser relatif terhadap aslinya. Dalam desain mobile, bayangan memiliki tiga fungsi: menunjukkan hierarki (elemen lebih tinggi — bayangan lebih panjang), menandakan interaktivitas (tombol dengan bayangan terlihat dapat diklik), memisahkan konten dari latar belakang (kartu dengan bayangan dibaca sebagai blok terpisah).

Dalam Material Design Google, bayangan terkait erat dengan konsep elevation (ketinggian). Setiap elemen menempati ketinggian tertentu dalam koordinat-Z, dan bayangan adalah proyeksi pencahayaan dari atas. Human Interface Guidelines Apple tidak mewajibkan sistem ketinggian yang ketat, tetapi merekomendasikan penggunaan bayangan untuk menciptakan hierarki visual dan kedalaman dalam antarmuka.

Menurut penelitian Nielsen Norman Group (2024), bayangan yang ditempatkan dengan benar mengurangi waktu pencarian elemen interaktif sebesar 24%. Bayangan juga membantu pengguna dengan gangguan penglihatan membedakan batas elemen. Namun, penggunaan bayangan yang berlebihan (3+ level di layar) meningkatkan beban kognitif dan memperburuk persepsi.

Bayangan di iOS: CALayer dan SwiftUI

Di iOS bayangan diimplementasikan melalui properti CALayer: shadowColor mengatur warna bayangan, shadowOffset — pergeseran dalam CGSize (x, y), shadowRadius — radius keburaman, shadowOpacity — opasitas dari 0 hingga 1. Untuk kinerja, disarankan juga mengatur shadowPath — UIBezierPath yang menggambarkan bentuk bayangan, agar Core Animation tidak menghitungnya secara otomatis.

swift
// UIKit: konfigurasi bayangan melalui CALayer
import UIKit

let cardView = UIView()
cardView.backgroundColor = .systemBackground
cardView.layer.cornerRadius = 12

// Konfigurasi bayangan
cardView.layer.shadowColor = UIColor.black.cgColor
cardView.layer.shadowOpacity = 0.15
cardView.layer.shadowOffset = CGSize(width: 0, height: 4)
cardView.layer.shadowRadius = 8

"> Optimasi: shadowPath eksplisit
cardView.layer.shadowPath = UIBezierPath(
    roundedRect: cardView.bounds,
    cornerRadius: 12
).cgPath

Dalam SwiftUI bayangan diatur melalui pengubah .shadow() dengan parameter color, radius, x, y. SwiftUI secara otomatis menerapkan shadowPath dan mengelola kinerja rendering. Untuk bayangan lanjutan (bayangan dalam, bayangan ganda) gunakan overlay() dengan LinearGradient.

swift
// SwiftUI: bayangan melalui pengubah shadow
import SwiftUI

VStack {
    Text("Kartu dengan bayangan")
        .padding()
        .background(Color.white)
        .cornerRadius(16)
        .shadow(
            color: .black.opacity(0.15),
            radius: 10,
            x: 0,
            y: 4
        )
}

Kinerja bayangan di iOS: jika shadowPath tidak diatur, Core Animation menghitung bayangan berdasarkan bentuk layer setiap frame, yang dapat menyebabkan perlambatan selama animasi. Untuk elemen statis selalu atur shadowPath. Untuk elemen yang dianimasikan gunakan shouldRasterize = true pada CALayer untuk menyimpan bayangan di layer terpisah. Di SwiftUI, rasterisasi dikelola melalui .drawingGroup().

Bayangan di Android: elevation dan bayangan kustom

Di Android bayangan dalam Material Design diimplementasikan melalui elevation — properti View yang mengatur ketinggian elemen dalam koordinat-Z. Sistem secara otomatis menggambar bayangan berdasarkan elevation dan outlineProvider (kontur elemen). Semakin tinggi elevation, semakin besar dan buram bayangannya. Untuk View yang tidak mewarisi Material Design (misalnya AppCompat), bayangan hanya berfungsi dengan akselerasi perangkat keras yang diaktifkan.

xml
<!-- XML: elevation melalui atribut -->
<androidx.cardview.widget.CardView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:cardCornerRadius="12dp"
    app:cardElevation="6dp">
    <!-- Konten kartu -->
</androidx.cardview.widget.CardView>

Untuk bayangan kustom di Android View System digunakan OutlineProvider. ViewOutlineProvider memungkinkan pengaturan bentuk arbitrer melalui outline.setRoundRect() atau outline.setPath(). Tanpa OutlineProvider, bayangan digambar berdasarkan bentuk persegi panjang View, yang tidak memperhitungkan pembulatan sudut.

kotlin
// Kotlin: OutlineProvider kustom untuk bayangan
import android.graphics.Outline
import android.view.View
import android.view.ViewOutlineProvider

val myView: View = findViewById(R.id.myView)
myView.outlineProvider = object : ViewOutlineProvider() {
    override fun getOutline(view: View, outline: Outline) {
        outline.setRoundRect(
            0, 0, view.width, view.height,
            12f  // radius sudut
        )
    }
}
myView.elevation = 8f  // tinggi bayangan dalam px
myView.translationZ = 4f  // tinggi tambahan saat ditekan

Dalam Jetpack Compose bayangan dikelola melalui Modifier.shadow() dan elevation di komponen Card dan Surface. Modifier.shadow() menerima elevation dan shape. Tidak seperti View System, Compose menggambar bayangan di pipeline rendering sendiri, yang memberikan perilaku lebih dapat diprediksi di semua versi Android.

kotlin
// Jetpack Compose: bayangan melalui Modifier.shadow
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp

Card(
    modifier = Modifier.shadow(
        elevation = 6.dp,
        shape = RoundedCornerShape(12.dp),
        clip = false
    ),
    colors = CardDefaults.cardColors(containerColor = Color.White)
) {
    Text("Card dengan bayangan kustom")
}

Rekomendasi penggunaan bayangan

Penggunaan bayangan yang benar meningkatkan persepsi antarmuka, penggunaan yang salah — menciptakan kebisingan visual dan menurunkan kinerja. Material Design mendefinisikan lima level elevation: 0 dp (tanpa bayangan), 1–4 dp (tombol, chip), 6–12 dp (kartu, menu), 16–24 dp (dialog, panel bawah), 24+ dp (jendela modal, FAB).

LevelElevationElemenKarakteristik bayangan
00 dpTeks, ikon, latar belakangTanpa bayangan, lapisan datar
11–4 dpTombol, bidang inputBayangan ringan, pergeseran kecil
26–12 dpKartu, SnackbarBayangan sedang, keburaman terlihat
316–24 dpDialog, menuBayangan dalam, keburaman besar
424+ dpJendela modalBayangan maksimal, elemen melayang

Aturan praktik yang baik: (1) jangan gunakan bayangan untuk elemen yang tidak boleh menarik perhatian (latar belakang, pemisah), (2) ketinggian bayangan harus sesuai dengan kepentingan elemen — semakin penting, semakin tinggi, (3) saat menekan tombol, kurangi elevation secara animasi untuk umpan balik taktil, (4) hindari bayangan pada latar belakang gelap — gunakan overlay (pencerahan/penggelapan) sebagai ganti bayangan. Di Material You (Android 12+) bayangan beradaptasi dengan tema: pada latar belakang gelap, elevation digantikan dengan lapisan warna (surface tint).

Bayangan di Flutter: BoxShadow dan PhysicalModel

Di Flutter bayangan diimplementasikan melalui BoxShadow di dalam BoxDecoration. BoxShadow menerima color, offset, blurRadius dan spreadRadius. Flutter mendukung bayangan ganda: mengirimkan daftar BoxShadow memungkinkan pembuatan efek kompleks — misalnya, bayangan eksternal + pencahayaan internal. PhysicalModel — alternatif yang mensimulasikan bayangan fisik berdasarkan bentuk (BeveledRectangleBorder, CircleBorder).

dart
// Flutter: bayangan melalui BoxDecoration + BoxShadow
import 'package:flutter/material.dart';

Container(
    decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(12.0),
        boxShadow: [
            BoxShadow(
                color: Colors.black.withOpacity(0.15),
                offset: Offset(0, 4),
                blurRadius: 8.0,
                spreadRadius: 0,
            ),
        ],
    ),
    child: Text('Kontainer dengan bayangan'),
)

// PhysicalModel untuk bayangan fisik
PhysicalModel(
    color: Colors.white,
    elevation: 6.0,
    shadowColor: Colors.black.withOpacity(0.2),
    borderRadius: BorderRadius.circular(12.0),
    child: Text('Model fisik dengan bayangan'),
)

Kinerja: BoxShadow dengan bayangan ganda dapat menyebabkan penggambaran ulang (repaint) setiap kali ada perubahan. Untuk elemen statis gunakan PhysicalModel — ia menyimpan bayangan di layer terpisah (RepaintBoundary). Di Flutter 3.16+ ditambahkan dukungan untuk Material 3 elevation dengan bayangan adaptif yang mengubah warna dan keburaman tergantung pada tema (terang/gelap).

Pertanyaan Umum

Apa perbedaan elevation dengan bayangan?

Elevation — adalah ketinggian numerik elemen dalam koordinat-Z, sedangkan shadow (bayangan) — efek visual yang diciptakan oleh ketinggian ini. Di Android, elevation — properti View yang secara otomatis menghasilkan bayangan. Dalam desain, elevation dan shadow sering digunakan sebagai istilah yang dapat dipertukarkan, tetapi secara teknis elevation adalah penyebab dan shadow adalah akibat.

Mengapa bayangan tidak muncul di Android?

Periksa: (1) apakah View memiliki elevation lebih dari 0, (2) apakah outlineProvider diatur (jika View bukan dari pustaka Material), (3) apakah akselerasi perangkat keras diaktifkan (android:hardwareAccelerated="true" di manifes). CardView secara otomatis mengelola outlineProvider. Untuk View kustom, panggil view.outlineProvider = ViewOutlineProvider.BACKGROUND.

Bagaimana cara membuat bayangan dalam (inner shadow) di iOS?

CALayer tidak mendukung bayangan dalam secara langsung. Gunakan kombinasi: CAGradientLayer dengan warna hitam dan transparansi di dalam mask (mask), atau letakkan UIImageView dengan bayangan dalam raster. Di SwiftUI, bayangan dalam diimplementasikan melalui overlay dengan LinearGradient dan blendMode(.multiply).

Bagaimana cara menganimasikan bayangan saat menekan tombol?

Di Android gunakan translationZ untuk animasi: dalam keadaan diam elevation = 2 dp, saat ditekan translationZ = 4 dp (tinggi total 6 dp). Di iOS, animasikan shadowOffset dan shadowRadius bersamaan dengan transformasi tombol. Di SwiftUI gunakan withAnimation dengan perubahan elevation melalui Modifier.shadow(). Di Flutter — AnimatedContainer dengan perubahan BoxShadow.

Berapa banyak bayangan yang dapat digunakan pada satu layar?

Material Design merekomendasikan tidak lebih dari tiga level elevation pada satu layar. Jika ada lebih banyak elemen dengan bayangan — gabungkan dalam grup dengan ketinggian yang sama. Jumlah bayangan yang berlebihan menciptakan kebisingan visual dan menurunkan kinerja rendering pada perangkat segmen menengah dan anggaran.

Kesimpulan

  • Bayangan (shadow) — efek visual kedalaman berdasarkan elevation elemen dalam koordinat-Z.
  • Di iOS bayangan dikonfigurasi melalui CALayer: shadowColor, shadowOffset, shadowRadius, shadowOpacity.
  • Di Android bayangan dikelola melalui elevation dan OutlineProvider untuk bentuk kustom.
  • Material Design mendefinisikan lima level elevation: 0, 1–4, 6–12, 16–24 dan 24+ dp.
  • Jetpack Compose menggunakan Modifier.shadow() dan elevation di Card/Surface untuk mengelola bayangan.
  • Flutter mengimplementasikan bayangan melalui BoxShadow (BoxDecoration) dan PhysicalModel (bayangan fisik).
  • Optimasi: atur shadowPath (iOS), outlineProvider (Android), gunakan PhysicalModel (Flutter).

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