Shadow: esența, crearea umbrelor în aplicațiile mobile

Autor: IT Sectr Publicat: 2026-02-28 Timp de citire: 9 min

Shadow (umbră) — un efect vizual în interfețele mobile care creează iluzia profunzimii și ierarhiei între elemente. Umbrele arată care element se află mai sus în planul Z și, prin urmare, este mai important sau interactiv. Conform Material Design Guidelines (Google, 2026), umbrele sistemice utilizează cinci înălțimi cheie (elevation): 0 dp (plat), 1–4 dp (butoane), 6–12 dp (carduri), 16–24 dp (dialoguri), 24+ dp (ferestre modale). Mai multe despre înălțimea elementelor citiți în articolul despre Elevation.

Puncte principale

  • Umbra (shadow) — un efect vizual care creează profunzime prin întunecarea zonei de sub element cu estompare la margini.
  • Pe iOS umbrele se configurează prin proprietățile CALayer: shadowColor, shadowOffset, shadowRadius, shadowOpacity.
  • În Android umbrele sunt gestionate prin elevation (Material Design) sau randare personalizată prin OutlineProvider.
  • Jetpack Compose suportă umbrele prin Modifier.shadow() și elevation în Card/Surface.
  • În Flutter umbrele sunt implementate prin BoxShadow în BoxDecoration sau prin PhysicalModel.

Ce este umbra în UI?

Umbra (shadow) în interfețele utilizator — este un efect vizual care imită iluminarea și profunzimea. Umbra se creează prin desenarea unei copii întunecate estompate a elementului, deplasată față de original. În designul mobil, umbrele îndeplinesc trei funcții: arată ierarhia (elementul mai sus — umbra mai lungă), semnalizează interactivitatea (butoanele cu umbră par apăsabile), separă conținutul de fundal (cardurile cu umbră sunt percepute ca blocuri separate).

În Material Design Google, umbrele sunt indisolubil legate de conceptul de elevation (înălțime). Fiecare element ocupă o anumită înălțime în coordonata Z, iar umbra este proiecția iluminării de sus. Human Interface Guidelines Apple nu prescriu un sistem strict de înălțimi, dar recomandă utilizarea umbrelor pentru a crea ierarhie vizuală și profunzime în interfețe.

Conform cercetării Nielsen Norman Group (2024), umbrele plasate corect reduc timpul de găsire a unui element interactiv cu 24%. Umbrele ajută, de asemenea, utilizatorii cu deficiențe de vedere să distingă limitele elementelor. Cu toate acestea, utilizarea excesivă a umbrelor (3+ niveluri pe ecran) crește sarcina cognitivă și degradează percepția.

Umbre pe iOS: CALayer și SwiftUI

Pe iOS umbrele sunt implementate prin proprietățile CALayer: shadowColor setează culoarea umbrei, shadowOffset — deplasarea în CGSize (x, y), shadowRadius — raza de estompare, shadowOpacity — opacitatea de la 0 la 1. Pentru performanță se recomandă și setarea shadowPath — UIBezierPath care descrie forma umbrei, pentru ca Core Animation să nu o calculeze automat.

swift
// UIKit: configurarea umbrei prin CALayer
import UIKit

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

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

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

În SwiftUI umbrele se setează prin modificatorul .shadow() cu parametrii color, radius, x, y. SwiftUI aplică automat shadowPath și gestionează performanța randării. Pentru umbre avansate (umbre interioare, umbre multiple) utilizați overlay() cu LinearGradient.

swift
// SwiftUI: umbră prin modificatorul shadow
import SwiftUI

VStack {
    Text("Card cu umbră")
        .padding()
        .background(Color.white)
        .cornerRadius(16)
        .shadow(
            color: .black.opacity(0.15),
            radius: 10,
            x: 0,
            y: 4
        )
}

Performanța umbrelor pe iOS: dacă shadowPath nu este setat, Core Animation calculează umbra pe baza formei stratului în fiecare cadru, ceea ce poate cauza întârzieri în timpul animației. Pentru elemente statice setați întotdeauna shadowPath. Pentru elemente animate utilizați shouldRasterize = true pe CALayer pentru a stoca umbra într-un strat separat. În SwiftUI rasterizarea este gestionată prin .drawingGroup().

Umbre în Android: elevation și umbre personalizate

În Android umbrele în Material Design sunt implementate prin elevation — proprietatea View care setează înălțimea elementului în coordonata Z. Sistemul desenează automat umbra pe baza elevation și outlineProvider (conturul elementului). Cu cât elevation este mai mare, cu atât umbra este mai mare și mai estompată. Pentru View-uri care nu moștenesc Material Design (de exemplu, AppCompat), umbrele funcționează doar cu accelerarea hardware activată.

xml
<!-- XML: elevation prin atribute -->
<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">
    <!-- Conținutul cardului -->
</androidx.cardview.widget.CardView>

Pentru umbre personalizate în Android View System se utilizează OutlineProvider. ViewOutlineProvider permite setarea unei forme arbitrare prin outline.setRoundRect() sau outline.setPath(). Fără OutlineProvider umbra este desenată pe baza formei dreptunghiulare a View, ceea ce nu ține cont de rotunjirea colțurilor.

kotlin
// Kotlin: OutlineProvider personalizat pentru umbră
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  // raza colțului
        )
    }
}
myView.elevation = 8f  // înălțimea umbrei în px
myView.translationZ = 4f  // înălțime suplimentară la apăsare

În Jetpack Compose umbrele sunt gestionate prin Modifier.shadow() și elevation în componentele Card și Surface. Modifier.shadow() acceptă elevation și shape. Spre deosebire de View System, Compose desenează umbrele în propriul pipeline de randare, ceea ce oferă un comportament mai predictibil pe toate versiunile de Android.

kotlin
// Jetpack Compose: umbră prin 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 cu umbră personalizată")
}

Recomandări pentru utilizarea umbrelor

Utilizarea corectă a umbrelor îmbunătățește percepția interfeței, utilizarea incorectă — creează zgomot vizual și degradează performanța. Material Design definește cinci niveluri de elevation: 0 dp (fără umbră), 1–4 dp (butoane, chipsuri), 6–12 dp (carduri, meniuri), 16–24 dp (dialoguri, panouri inferioare), 24+ dp (ferestre modale, FAB).

NivelElevationElementeCaracteristica umbrei
00 dpText, pictograme, fundalFără umbră, strat plat
11–4 dpButoane, câmpuri de intrareUmbră ușoară, deplasare mică
26–12 dpCarduri, SnackbarUmbră moderată, estompare vizibilă
316–24 dpDialoguri, meniuriUmbră profundă, estompare mare
424+ dpFerestre modaleUmbră maximă, elementul plutește

Reguli de bună practică: (1) nu folosiți umbre pentru elemente care nu ar trebui să atragă atenția (fundaluri, separatoare), (2) înălțimea umbrei trebuie să corespundă importanței elementului — cu cât mai important, cu atât mai sus, (3) la apăsarea butonului reduceți elevation animat pentru feedback tactil, (4) evitați umbrele pe fundaluri întunecate — folosiți overlay (luminiere/întunecare) în loc de umbră. În Material You (Android 12+) umbrele se adaptează la temă: pe fundal întunecat elevation este înlocuită cu un strat de culoare (surface tint).

Umbre în Flutter: BoxShadow și PhysicalModel

În Flutter umbrele sunt implementate prin BoxShadow în interiorul BoxDecoration. BoxShadow acceptă color, offset, blurRadius și spreadRadius. Flutter suportă umbre multiple: transmiterea unei liste de BoxShadow permite crearea de efecte complexe — de exemplu, umbră externă + iluminare internă. PhysicalModel — o alternativă care simulează o umbră fizică pe baza formei (BeveledRectangleBorder, CircleBorder).

dart
// Flutter: umbră prin 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('Container cu umbră'),
)

// PhysicalModel pentru umbră fizică
PhysicalModel(
    color: Colors.white,
    elevation: 6.0,
    shadowColor: Colors.black.withOpacity(0.2),
    borderRadius: BorderRadius.circular(12.0),
    child: Text('Model fizic cu umbră'),
)

Performanța: BoxShadow cu umbre multiple poate cauza redesenarea la fiecare modificare (repaint). Pentru elemente statice utilizați PhysicalModel — acesta stochează umbra într-un strat separat (RepaintBoundary). În Flutter 3.16+ a fost adăugat suportul pentru Material 3 elevation cu umbre adaptive care își schimbă culoarea și estomparea în funcție de temă (light/dark).

Întrebări frecvente

Cu ce diferă elevation de umbră?

Elevation — este înălțimea numerică a elementului în coordonata Z, iar shadow (umbră) — efectul vizual creat de această înălțime. În Android elevation — proprietatea View care generează automat umbra. În design, elevation și shadow sunt adesea folosite ca termeni interschimbabili, dar tehnic elevation este cauza, iar shadow — efectul.

De ce nu se afișează umbra pe Android?

Verificați: (1) View are elevation mai mare de 0, (2) este setat outlineProvider (dacă View nu este din biblioteca Material), (3) accelerarea hardware este activată (android:hardwareAccelerated="true" în manifest). CardView gestionează automat outlineProvider. Pentru View-uri personalizate apelați view.outlineProvider = ViewOutlineProvider.BACKGROUND.

Cum se face o umbră interioară (inner shadow) pe iOS?

CALayer nu suportă umbre interioare direct. Utilizați o combinație: CAGradientLayer cu culoare neagră și transparență în interiorul măștii (mask), sau suprapuneți un UIImageView cu umbră interioară raster. În SwiftUI umbra interioară se implementează prin overlay cu LinearGradient și blendMode(.multiply).

Cum se animează umbra la apăsarea butonului?

În Android utilizați translationZ pentru animație: în stare de repaus elevation = 2 dp, la apăsare translationZ = 4 dp (înălțime totală 6 dp). Pe iOS animați shadowOffset și shadowRadius simultan cu transformarea butonului. În SwiftUI utilizați withAnimation cu modificarea elevation prin Modifier.shadow(). În Flutter — AnimatedContainer cu modificarea BoxShadow.

Câte umbre se pot folosi pe un ecran?

Material Design recomandă nu mai mult de trei niveluri de elevation pe un ecran. Dacă există mai multe elemente cu umbră — combinați-le în grupuri cu aceeași înălțime. Numărul excesiv de umbre creează zgomot vizual și reduce performanța randării pe dispozitivele de segment mediu și bugetar.

Rezumat

  • Umbra (shadow) — un efect vizual de profunzime bazat pe elevation a elementului în coordonata Z.
  • Pe iOS umbrele se configurează prin CALayer: shadowColor, shadowOffset, shadowRadius, shadowOpacity.
  • În Android umbrele sunt gestionate prin elevation și OutlineProvider pentru formă personalizată.
  • Material Design definește cinci niveluri de elevation: 0, 1–4, 6–12, 16–24 și 24+ dp.
  • Jetpack Compose utilizează Modifier.shadow() și elevation în Card/Surface pentru gestionarea umbrelor.
  • Flutter implementează umbrele prin BoxShadow (BoxDecoration) și PhysicalModel (umbră fizică).
  • Optimizare: setați shadowPath (iOS), outlineProvider (Android), utilizați PhysicalModel (Flutter).

Vom dezvolta o aplicație mobilă la cheie

IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.

Discutați proiectul

Citiți și