Shadow: esencia, creación de sombras en aplicaciones móviles

Autor: IT Sectr Publicado: 2026-02-28 Tiempo de lectura: 9 min

Shadow (sombra) es un efecto visual en interfaces móviles que crea la ilusión de profundidad y jerarquía entre elementos. Las sombras muestran qué elemento está más arriba en el plano z y, por lo tanto, es más importante o interactivo. Según las Material Design Guidelines (Google, 2026), las sombras del sistema utilizan cinco niveles clave de elevation: 0 dp (plano), 1–4 dp (botones), 6–12 dp (tarjetas), 16–24 dp (diálogos), 24+ dp (ventanas modales). Más información sobre la altura de elementos en el artículo sobre Elevation.

Lo más importante

  • Sombra (shadow) — efecto visual que crea profundidad oscureciendo el área debajo de un elemento con desenfoque en los bordes.
  • En iOS, las sombras se configuran mediante propiedades de CALayer: shadowColor, shadowOffset, shadowRadius, shadowOpacity.
  • En Android, las sombras se gestionan a través de elevation (Material Design) o renderizado personalizado mediante OutlineProvider.
  • Jetpack Compose admite sombras mediante Modifier.shadow() y elevation en Card/Surface.
  • En Flutter, las sombras se implementan mediante BoxShadow en BoxDecoration o a través de PhysicalModel.

¿Qué es la sombra en la IU?

La sombra (shadow) en las interfaces de usuario es un efecto visual que simula iluminación y profundidad. La sombra se crea dibujando una copia oscura y borrosa de un elemento, desplazada respecto al original. En el diseño móvil, las sombras cumplen tres funciones: muestran jerarquía (cuanto más alto el elemento, más larga la sombra), señalan interactividad (los botones con sombra parecen pulsables), separan el contenido del fondo (las tarjetas con sombra se leen como bloques independientes).

En Material Design de Google, las sombras están inextricablemente vinculadas al concepto de elevation. Cada elemento ocupa una altura determinada en la coordenada z, y la sombra es una proyección de la iluminación superior. Las Human Interface Guidelines de Apple no prescriben un sistema estricto de alturas, pero recomiendan usar sombras para crear jerarquía visual y profundidad en las interfaces.

Según un estudio de Nielsen Norman Group (2024), las sombras colocadas correctamente reducen el tiempo para encontrar un elemento interactivo en un 24%. Las sombras también ayudan a los usuarios con discapacidad visual a distinguir los límites de los elementos. Sin embargo, el uso excesivo de sombras (3+ niveles en una pantalla) aumenta la carga cognitiva y perjudica la percepción.

Sombras en iOS: CALayer y SwiftUI

En iOS, las sombras se implementan mediante propiedades de CALayer: shadowColor define el color de la sombra, shadowOffset — desplazamiento en CGSize (x, y), shadowRadius — radio de desenfoque, shadowOpacity — opacidad de 0 a 1. Para rendimiento, también se recomienda establecer shadowPath — un UIBezierPath que describe la forma de la sombra, para que Core Animation no la calcule automáticamente.

swift
// UIKit: configuración de sombra mediante CALayer
import UIKit

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

// Configuración de sombra
cardView.layer.shadowColor = UIColor.black.cgColor
cardView.layer.shadowOpacity = 0.15
cardView.layer.shadowOffset = CGSize(width: 0, height: 4)
cardView.layer.shadowRadius = 8

"> Optimización: shadowPath explícito
cardView.layer.shadowPath = UIBezierPath(
    roundedRect: cardView.bounds,
    cornerRadius: 12
).cgPath

En SwiftUI, las sombras se definen mediante el modificador .shadow() con parámetros color, radius, x, y. SwiftUI aplica automáticamente shadowPath y gestiona el rendimiento de renderizado. Para sombras avanzadas (sombras interiores, múltiples sombras), use overlay() con LinearGradient.

swift
// SwiftUI: sombra mediante el modificador shadow
import SwiftUI

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

Rendimiento de sombras en iOS: si no se establece shadowPath, Core Animation calcula la sombra basándose en la forma de la capa cada fotograma, lo que puede causar retrasos durante la animación. Para elementos estáticos, establezca siempre shadowPath. Para elementos animados, use shouldRasterize = true en CALayer para almacenar en caché la sombra en una capa separada. En SwiftUI, la rasterización se gestiona mediante .drawingGroup().

Sombras en Android: elevation y sombras personalizadas

En Android, las sombras en Material Design se implementan mediante elevation — una propiedad de View que establece la altura del elemento en la coordenada z. El sistema renderiza automáticamente una sombra basada en elevation y outlineProvider (contorno del elemento). Cuanto mayor es la elevation, más grande y borrosa es la sombra. Para vistas que no heredan de Material Design (por ejemplo, AppCompat), las sombras solo funcionan con aceleración de hardware habilitada.

xml
<!-- XML: elevation mediante atributos -->
<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">
    <!-- Contenido de la tarjeta -->
</androidx.cardview.widget.CardView>

Para sombras personalizadas en el sistema de vistas de Android, use OutlineProvider. ViewOutlineProvider permite establecer una forma arbitraria mediante outline.setRoundRect() o outline.setPath(). Sin OutlineProvider, la sombra se renderiza basándose en la forma rectangular de la vista, lo que no tiene en cuenta el redondeo de esquinas.

kotlin
// Kotlin: OutlineProvider personalizado para sombra
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  // radio de esquina
        )
    }
}
myView.elevation = 8f  // altura de sombra en px
myView.translationZ = 4f  // altura adicional al presionar

En Jetpack Compose, las sombras se gestionan mediante Modifier.shadow() y elevation en los componentes Card y Surface. Modifier.shadow() acepta elevation y shape. A diferencia del sistema de vistas, Compose renderiza las sombras en su propio pipeline de renderizado, proporcionando un comportamiento más predecible en todas las versiones de Android.

kotlin
// Jetpack Compose: sombra mediante 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 con sombra personalizada")
}

Recomendaciones para el uso de sombras

El uso correcto de las sombras mejora la percepción de la interfaz, mientras que el uso incorrecto crea ruido visual y degrada el rendimiento. Material Design define cinco niveles de elevation: 0 dp (sin sombra), 1–4 dp (botones, chips), 6–12 dp (tarjetas, menús), 16–24 dp (diálogos, bottom sheets), 24+ dp (ventanas modales, FAB).

NivelElevationElementosCaracterística de la sombra
00 dpTexto, iconos, fondoSin sombra, capa plana
11–4 dpBotones, campos de entradaSombra ligera, pequeño desplazamiento
26–12 dpTarjetas, SnackbarSombra moderada, desenfoque notable
316–24 dpDiálogos, menúsSombra profunda, gran desenfoque
424+ dpVentanas modalesSombra máxima, elemento flota

Buenas prácticas: (1) no use sombras para elementos que no deben atraer atención (fondos, divisores), (2) la altura de la sombra debe coincidir con la importancia del elemento — cuanto más importante, más alta, (3) al presionar un botón, disminuya la elevation con animación para retroalimentación táctil, (4) evite sombras en fondos oscuros — use overlay (aclarar/oscurecer) en lugar de sombra. En Material You (Android 12+), las sombras se adaptan al tema: en fondos oscuros, la elevation se reemplaza por una capa de color (surface tint).

Sombras en Flutter: BoxShadow y PhysicalModel

En Flutter, las sombras se implementan mediante BoxShadow dentro de BoxDecoration. BoxShadow acepta color, offset, blurRadius y spreadRadius. Flutter admite múltiples sombras: pasar una lista de BoxShadow permite crear efectos complejos — por ejemplo, una sombra exterior + resalte interior. PhysicalModel es una alternativa que simula una sombra física basada en la forma (BeveledRectangleBorder, CircleBorder).

dart
// Flutter: sombra mediante 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('Contenedor con sombra'),
)

// PhysicalModel para sombra física
PhysicalModel(
    color: Colors.white,
    elevation: 6.0,
    shadowColor: Colors.black.withOpacity(0.2),
    borderRadius: BorderRadius.circular(12.0),
    child: Text('Modelo físico con sombra'),
)

Rendimiento: BoxShadow con múltiples sombras puede causar repintado en cada cambio. Para elementos estáticos, use PhysicalModel — almacena en caché la sombra en una capa separada (RepaintBoundary). En Flutter 3.16+, se admite elevation de Material 3 con sombras adaptativas, que cambian de color y desenfoque según el tema (claro/oscuro).

Preguntas frecuentes

¿En qué se diferencia elevation de shadow?

Elevation es la altura numérica de un elemento en la coordenada z, mientras que shadow (sombra) es el efecto visual creado por esta altura. En Android, elevation es una propiedad de View que genera automáticamente una sombra. En diseño, elevation y shadow a menudo se usan indistintamente, pero técnicamente elevation es la causa y shadow es el efecto.

¿Por qué no se muestra la sombra en Android?

Verifique: (1) la View tiene elevation mayor que 0, (2) se ha establecido outlineProvider (si la View no es de la biblioteca Material), (3) la aceleración de hardware está habilitada (android:hardwareAccelerated="true" en el manifiesto). CardView gestiona automáticamente outlineProvider. Para vistas personalizadas, llame a view.outlineProvider = ViewOutlineProvider.BACKGROUND.

¿Cómo crear una sombra interior (inner shadow) en iOS?

CALayer no admite sombras interiores directamente. Use una combinación: CAGradientLayer con color negro y transparencia dentro de una máscara, o superponga un UIImageView con una sombra interior rasterizada. En SwiftUI, la sombra interior se implementa mediante overlay con LinearGradient y blendMode(.multiply).

¿Cómo animar la sombra al presionar un botón?

En Android, use translationZ para la animación: en reposo elevation = 2 dp, al presionar translationZ = 4 dp (altura total 6 dp). En iOS, anime shadowOffset y shadowRadius simultáneamente con la transformación del botón. En SwiftUI, use withAnimation con cambio de elevation mediante Modifier.shadow(). En Flutter, use AnimatedContainer con cambio de BoxShadow.

¿Cuántas sombras se pueden usar en una misma pantalla?

Material Design recomienda no más de tres niveles de elevation en una misma pantalla. Si hay más elementos con sombra, agrúpelos con la misma altura. La cantidad excesiva de sombras crea ruido visual y reduce el rendimiento de renderizado en dispositivos de gama media y baja.

Resumen

  • Shadow (sombra) es un efecto visual de profundidad basado en la elevation del elemento en la coordenada z.
  • En iOS, las sombras se configuran mediante CALayer: shadowColor, shadowOffset, shadowRadius, shadowOpacity.
  • En Android, las sombras se gestionan mediante elevation y OutlineProvider para forma personalizada.
  • Material Design define cinco niveles de elevation: 0, 1–4, 6–12, 16–24 y 24+ dp.
  • Jetpack Compose usa Modifier.shadow() y elevation en Card/Surface para gestionar sombras.
  • Flutter implementa sombras mediante BoxShadow (BoxDecoration) y PhysicalModel (sombra física).
  • Optimización: establezca shadowPath (iOS), outlineProvider (Android), use PhysicalModel (Flutter).

Desarrollaremos una aplicación móvil llave en mano

IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.

Discutir el proyecto

Lea también