Shadow: суштина, креирање сенки у мобилним апликацијама

Аутор: IT Sectr Објављено: 2026-02-28 Време читања: 9 мин

Shadow (сенка) — визуелни ефекат у мобилним интерфејсима који ствара илузију дубине и хијерархије између елемената. Сенке показују који елемент се налази више у Z-равни и, самим тим, важнији је или интерактивнији. Према Material Design Guidelines (Google, 2026), системске сенке користе пет кључних висина (elevation): 0 dp (равно), 1–4 dp (дугмад), 6–12 dp (картице), 16–24 dp (дијалози), 24+ dp (модални прозори). Више о висини елемената прочитајте у чланку о Elevation.

Главно

  • Сенка (shadow) — визуелни ефекат који ствара дубину затамњивањем области испод елемента са замућењем на ивицама.
  • На iOS-у сенке се подешавају кроз својства CALayer: shadowColor, shadowOffset, shadowRadius, shadowOpacity.
  • У Android-у сенке се управљају кроз elevation (Material Design) или прилагођено цртање кроз OutlineProvider.
  • Jetpack Compose подржава сенке кроз Modifier.shadow() и elevation у Card/Surface.
  • У Flutter-у сенке се реализују кроз BoxShadow у BoxDecoration или кроз PhysicalModel.

Шта је сенка у UI?

Сенка (shadow) у корисничким интерфејсима — визуелни ефекат који имитира осветљење и дубину. Сенка се ствара цртањем тамне замућене копије елемента померене у односу на оригинал. У мобилном дизајну сенке обављају три функције: показују хијерархију (виши елемент — дужа сенка), сигнализирају интерактивност (дугмад са сенком изгледају кликабилно), одвајају садржај од позадине (картице са сенком се читају као засебни блокови).

У Google-овом Material Design-у сенке су нераскидиво повезане са концептом elevation (висине). Сваки елемент заузима одређену висину у Z-координати, а сенка је пројекција осветљења одозго. Apple-ове Human Interface Guidelines не прописују строги систем висина, али препоручују коришћење сенки за стварање визуелне хијерархије и дубине у интерфејсима.

Према истраживању Nielsen Norman Group (2024), правилно постављене сенке смањују време проналажења интерактивног елемента за 24%. Сенке такође помажу корисницима са оштећењем вида да разликују границе елемената. Међутим, прекомерна употреба сенки (3+ нивоа на екрану) повећава когнитивно оптерећење и погоршава перцепцију.

Сенке на iOS-у: CALayer и SwiftUI

На iOS-у сенке се реализују кроз својства CALayer-а: shadowColor поставља боју сенке, shadowOffset — померај у CGSize (x, y), shadowRadius — радијус замућења, shadowOpacity — непрозирност од 0 до 1. За перформансе се препоручује и постављање shadowPath — UIBezierPath који описује облик сенке, како Core Animation не би рачунала аутоматски.

swift
// UIKit: подешавање сенке кроз CALayer
import UIKit

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

// Подешавање сенке
cardView.layer.shadowColor = UIColor.black.cgColor
cardView.layer.shadowOpacity = 0.15
cardView.layer.shadowOffset = CGSize(width: 0, height: 4)
cardView.layer.shadowRadius = 8

"> Optimзация: явный shadowPath
cardView.layer.shadowPath = UIBezierPath(
    roundedRect: cardView.bounds,
    cornerRadius: 12
).cgPath

У SwiftUI сенке се постављају кроз модификатор .shadow() са параметрима color, radius, x, y. SwiftUI аутоматски примењује shadowPath и управља перформансама рендеровања. За напредне сенке (унутрашње сенке, вишеструке сенке) користите overlay() са LinearGradient.

swift
// SwiftUI: сенка кроз модификатор shadow
import SwiftUI

VStack {
    Text("Картица са сенком")
        .padding()
        .background(Color.white)
        .cornerRadius(16)
        .shadow(
            color: .black.opacity(0.15),
            radius: 10,
            x: 0,
            y: 4
        )
}

Перформансе сенки на iOS-у: ако shadowPath није постављен, Core Animation рачуна сенку на основу облика слоја сваки кадар, што може изазвати успоравање током анимације. За статичне елементе увек постављајте shadowPath. За анимиране елементе користите shouldRasterize = true на CALayer-у да бисте кеширали сенку у засебан слој. У SwiftUI-ју растеретизација се управља кроз .drawingGroup().

Сенке у Android-у: elevation и прилагођене сенке

У Android-у сенке у Material Design-у се реализују кроз elevation — својство View-а које поставља висину елемента у Z-координати. Систем аутоматски црта сенку на основу elevation и outlineProvider-а (контуре елемента). Што је elevation веће, то је сенка већа и замућенија. За View-ове који не наслеђују Material Design (нпр. AppCompat), сенке раде само уз укључено хардверско убрзање.

xml
<!-- XML: elevation кроз атрибуте -->
<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">
    <!-- Садржај картице -->
</androidx.cardview.widget.CardView>

За прилагођене сенке у Android View систему користи се OutlineProvider. ViewOutlineProvider омогућава постављање произвољног облика кроз outline.setRoundRect() или outline.setPath(). Без OutlineProvider-а сенка се црта по правоугаоном облику View-а, што не узима у обзир заобљење углова.

kotlin
// Kotlin: прилагођени OutlineProvider за сенку
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  // радијус угла
        )
    }
}
myView.elevation = 8f  // висина сенке у px
myView.translationZ = 4f  // додатна висина при притиску

У Jetpack Compose-у сенке се управљају кроз Modifier.shadow() и elevation у компонентама Card и Surface. Modifier.shadow() прима elevation и shape. За разлику од View система, Compose црта сенке у сопственом рендер-пајплајну, што даје предвидљивије понашање на свим верзијама Android-а.

kotlin
// Jetpack Compose: сенка кроз 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 са прилагођеном сенком")
}

Препоруке за коришћење сенки

Правилно коришћење сенки побољшава перцепцију интерфејса, неправилно — ствара визуелни шум и погоршава перформансе. Material Design дефинише пет нивоа elevation: 0 dp (без сенке), 1–4 dp (дугмад, чипови), 6–12 dp (картице, менији), 16–24 dp (дијалози, доњи панели), 24+ dp (модални прозори, FAB).

НивоElevationЕлементиКарактеристика сенке
00 dpТекст, иконе, позадинаБез сенке, равни слој
11–4 dpДугмад, поља за уносЛагана сенка, мали померај
26–12 dpКартице, SnackbarУмерена сенка, приметно замућење
316–24 dpДијалози, менијиДубока сенка, велико замућење
424+ dpМодални прозориМаксимална сенка, елемент лебди

Правила доброг тона: (1) не користите сенке за елементе који не треба да привлаче пажњу (позадине, разделници), (2) висина сенке треба да одговара важности елемента — што важније, то више, (3) при притиску на дугме смањујте elevation анимирано за тактилну повратну информацију, (4) избегавајте сенке на тамним позадинама — користите overlay (осветљење/затамњење) уместо сенке. У Material You (Android 12+) сенке се прилагођавају теми: на тамној позадини elevation се замењује слојем боје (surface tint).

Сенке у Flutter-у: BoxShadow и PhysicalModel

У Flutter-у сенке се реализују кроз BoxShadow унутар BoxDecoration. BoxShadow прима color, offset, blurRadius и spreadRadius. Flutter подржава вишеструке сенке: прослеђивање листе BoxShadow омогућава стварање сложених ефеката — на пример, спољашња сенка + унутрашње осветљење. PhysicalModel — алтернатива која симулира физичку сенку на основу облика (BeveledRectangleBorder, CircleBorder).

dart
// Flutter: сенка кроз 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('Контејнер са сенком'),
)

// PhysicalModel за физичку сенку
PhysicalModel(
    color: Colors.white,
    elevation: 6.0,
    shadowColor: Colors.black.withOpacity(0.2),
    borderRadius: BorderRadius.circular(12.0),
    child: Text('Физички модел са сенком'),
)

Перформансе: BoxShadow са вишеструким сенкама може изазвати прецртавање при свакој промени (repaint). За статичне елементе користите PhysicalModel — он кешира сенку у засебан слој (RepaintBoundary). У Flutter-у 3.16+ додата је подршка за Material 3 elevation са адаптивним сенкама које мењају боју и замућење у зависности од теме (light/dark).

Често постављана питања

Чим се elevation разликује од сенке?

Elevation — нумеричка висина елемента у Z-координати, а shadow (сенка) — визуелни ефекат створен том висином. У Android-у elevation — својство View-а које аутоматски генерише сенку. У дизајну, elevation и shadow се често користе као заменљиви термини, али технички elevation је узрок, а shadow — последица.

Зашто се сенка не приказује на Android-у?

Проверите: (1) да View има elevation веће од 0, (2) да је постављен outlineProvider (ако View није из Material библиотеке), (3) да је укључено хардверско убрзање (android:hardwareAccelerated="true" у манифесту). CardView аутоматски управља outlineProvider-ом. За прилагођене View-ове позовите view.outlineProvider = ViewOutlineProvider.BACKGROUND.

Како направити унутрашњу сенку (inner shadow) на iOS-у?

CALayer директно не подржава унутрашње сенке. Користите комбинацију: CAGradientLayer са црном бојом и провидношћу унутар маске (mask), или поставите UIImageView са растерском унутрашњом сенком. У SwiftUI-ју унутрашња сенка се реализује кроз overlay са LinearGradient и blendMode(.multiply).

Како анимирати сенку при притиску на дугме?

У Android-у користите translationZ за анимацију: у мировању elevation = 2 dp, при притиску translationZ = 4 dp (укупна висина 6 dp). На iOS-у анимирајте shadowOffset и shadowRadius истовремено са трансформацијом дугмета. У SwiftUI-ју користите withAnimation са променом elevation кроз Modifier.shadow(). У Flutter-у — AnimatedContainer са променом BoxShadow.

Колико сенки се може користити на једном екрану?

Material Design препоручује не више од три нивоа elevation на једном екрану. Ако има више елемената са сенком — обједините их у групе са истом висином. Прекомеран број сенки ствара визуелни шум и смањује перформансе рендеровања на уређајима средњег и буџетског сегмента.

Резиме

  • Сенка (shadow) — визуелни ефекат дубине заснован на elevation елемента у Z-координати.
  • На iOS-у сенке се конфигуришу кроз CALayer: shadowColor, shadowOffset, shadowRadius, shadowOpacity.
  • У Android-у сенке се управљају кроз elevation и OutlineProvider за прилагођени облик.
  • Material Design дефинише пет нивоа elevation: 0, 1–4, 6–12, 16–24 и 24+ dp.
  • Jetpack Compose користи Modifier.shadow() и elevation у Card/Surface за управљање сенкама.
  • Flutter реализује сенке кроз BoxShadow (BoxDecoration) и PhysicalModel (физичка сенка).
  • Оптимизација: поставите shadowPath (iOS), outlineProvider (Android), користите PhysicalModel (Flutter).

Развићемо мобилну апликацију под кључ

IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.

Разговарајте о пројекту

Прочитајте такође