Тени визуально улучшают ваш пользовательский интерфейс, указывают на интерактивность и обеспечивают мгновенную обратную связь на действия пользователя. Compose предлагает несколько способов добавить тени в ваше приложение:
-
Modifier.shadow()
: создает тень на основе высоты позади компонуемого объекта, соответствующую принципам Material Design. -
Modifier.dropShadow()
: создает настраиваемую тень, которая появляется позади компонуемого объекта, делая его визуально приподнятым. -
Modifier.innerShadow()
: Создает тень внутри границ компонуемого объекта, заставляя его казаться вдавленным в поверхность позади него.
Modifier.shadow()
подходит для создания базовых теней, тогда как модификаторы dropShadow
и innerShadow
обеспечивают более детальный контроль и точность при рендеринге теней.
На этой странице описывается, как реализовать каждый из этих модификаторов, включая анимацию теней при взаимодействии с пользователем и объединение модификаторов innerShadow()
и dropShadow()
для создания градиентных теней , неоморфных теней и многого другого.
Создайте базовые тени
Modifier.shadow()
создаёт базовую тень, следуя принципам Material Design , имитирующую источник света сверху. Глубина тени определяется значением elevation
, а отбрасываемая тень обрезается по форме компонуемого объекта.
@Composable fun ElevationBasedShadow() { Box( modifier = Modifier.aspectRatio(1f).fillMaxSize(), contentAlignment = Alignment.Center ) { Box( Modifier .size(100.dp, 100.dp) .shadow(10.dp, RectangleShape) .background(Color.White) ) } }

Modifier.shadow
.Реализовать падающие тени
Используйте модификатор dropShadow()
, чтобы нарисовать точную тень позади вашего содержимого, что сделает элемент более приподнятым.
С помощью параметра Shadow
можно контролировать следующие ключевые аспекты:
-
radius
: определяет мягкость и рассеивание размытия. -
color
: определяет цвет оттенка. -
offset
: размещает геометрию тени вдоль осей x и y. -
spread
: управляет расширением или сужением геометрии тени.
Кроме того, параметр shape
определяет общую форму тени. Он может использовать любую геометрию из пакета androidx.compose.foundation.shape
, а также формы Material Expressive .
Чтобы реализовать базовую падающую тень, добавьте модификатор dropShadow()
к вашей компонуемой цепочке, задав радиус, цвет и ширину. Обратите внимание, что фон purpleColor
, появляющийся поверх тени, рисуется после модификатора dropShadow()
:
@Composable fun SimpleDropShadowUsage() { Box(Modifier.fillMaxSize()) { Box( Modifier .width(300.dp) .height(300.dp) .dropShadow( shape = RoundedCornerShape(20.dp), shadow = Shadow( radius = 10.dp, spread = 6.dp, color = Color(0x40000000), offset = DpOffset(x = 4.dp, 4.dp) ) ) .align(Alignment.Center) .background( color = Color.White, shape = RoundedCornerShape(20.dp) ) ) { Text( "Drop Shadow", modifier = Modifier.align(Alignment.Center), fontSize = 32.sp ) } } }
Ключевые моменты кода
- К внутреннему
Box
применяется модификаторdropShadow()
. Тень имеет следующие характеристики:- Форма скругленного прямоугольника (
RoundedCornerShape(20.dp)
) - Радиус размытия
10.dp
, делающий края мягкими и размытыми. - Разброс
6.dp
, который увеличивает размер тени и делает ее больше, чем отбрасывающий ее ящик - Альфа
0.5f
, что делает тень полупрозрачной.
- Форма скругленного прямоугольника (
- После определения тени применяется модификатор .
background()
.-
Box
заполнена белым цветом. - Фон обрезается по той же скругленной форме прямоугольника, что и тень.
-
Результат

Реализуйте внутренние тени
Чтобы создать эффект, обратный dropShadow
, используйте Modifier.innerShadow()
, который создает иллюзию того, что элемент утоплен или вдавлен в нижележащую поверхность.
При создании внутренних теней важен порядок. Внутренняя тень рисуется поверх содержимого, поэтому обычно следует делать следующее:
- Нарисуйте фоновый контент.
- Примените модификатор
innerShadow()
, чтобы создать вогнутый вид.
Если innerShadow()
помещен перед фоном, фон рисуется поверх тени, полностью скрывая ее.
В следующем примере показано применение innerShadow()
к RoundedCornerShape
:
@Composable fun SimpleInnerShadowUsage() { Box(Modifier.fillMaxSize()) { Box( Modifier .width(300.dp) .height(200.dp) .align(Alignment.Center) // note that the background needs to be defined before defining the inner shadow .background( color = Color.White, shape = RoundedCornerShape(20.dp) ) .innerShadow( shape = RoundedCornerShape(20.dp), shadow = Shadow( radius = 10.dp, spread = 2.dp, color = Color(0x40000000), offset = DpOffset(x = 6.dp, 7.dp) ) ) ) { Text( "Inner Shadow", modifier = Modifier.align(Alignment.Center), fontSize = 32.sp ) } } }

Modifier.innerShadow()
к прямоугольнику с закругленными углами.Анимация теней при взаимодействии с пользователем
Чтобы тени реагировали на действия пользователя, вы можете интегрировать свойства теней с API анимации Compose . Например, когда пользователь нажимает кнопку, тень может меняться, обеспечивая мгновенную визуальную обратную связь.
Следующий код создает эффект «вдавливания» с тенью (иллюзия того, что поверхность вдавливается в экран):
@Composable fun AnimatedColoredShadows() { SnippetsTheme { Box(Modifier.fillMaxSize()) { val interactionSource = remember { MutableInteractionSource() } val isPressed by interactionSource.collectIsPressedAsState() // Create transition with pressed state val transition = updateTransition( targetState = isPressed, label = "button_press_transition" ) fun <T> buttonPressAnimation() = tween<T>( durationMillis = 400, easing = EaseInOut ) // Animate all properties using the transition val shadowAlpha by transition.animateFloat( label = "shadow_alpha", transitionSpec = { buttonPressAnimation() } ) { pressed -> if (pressed) 0f else 1f } // ... val blueDropShadow by transition.animateColor( label = "shadow_color", transitionSpec = { buttonPressAnimation() } ) { pressed -> if (pressed) Color.Transparent else blueDropShadowColor } // ... Box( Modifier .clickable( interactionSource, indication = null ) { // ** ...... **// } .width(300.dp) .height(200.dp) .align(Alignment.Center) .dropShadow( shape = RoundedCornerShape(70.dp), shadow = Shadow( radius = 10.dp, spread = 0.dp, color = blueDropShadow, offset = DpOffset(x = 0.dp, -(2).dp), alpha = shadowAlpha ) ) .dropShadow( shape = RoundedCornerShape(70.dp), shadow = Shadow( radius = 10.dp, spread = 0.dp, color = darkBlueDropShadow, offset = DpOffset(x = 2.dp, 6.dp), alpha = shadowAlpha ) ) // note that the background needs to be defined before defining the inner shadow .background( color = Color(0xFFFFFFFF), shape = RoundedCornerShape(70.dp) ) .innerShadow( shape = RoundedCornerShape(70.dp), shadow = Shadow( radius = 8.dp, spread = 4.dp, color = innerShadowColor2, offset = DpOffset(x = 4.dp, 0.dp) ) ) .innerShadow( shape = RoundedCornerShape(70.dp), shadow = Shadow( radius = 20.dp, spread = 4.dp, color = innerShadowColor1, offset = DpOffset(x = 4.dp, 0.dp), alpha = innerShadowAlpha ) ) ) { Text( "Animated Shadows", // ... ) } } } }
Ключевые моменты кода
- Объявляет начальное и конечное состояния параметров для анимации при нажатии с помощью
transition.animateColor
иtransition.animateFloat
. - Использует
updateTransition
и передает ему выбранноеtargetState (targetState = isPressed)
для проверки синхронизации всех анимаций. При измененииisPressed
объект перехода автоматически управляет анимацией всех дочерних свойств с их текущих значений до новых целевых значений. - Определяет спецификацию
buttonPressAnimation
, которая управляет временем и плавностью перехода. Она задаётtween
(сокращение от in-between) длительностью 400 миллисекунд и кривуюEaseInOut
, что означает, что анимация начинается медленно, ускоряется в середине и замедляется в конце. - Определяет
Box
с цепочкой функций-модификаторов, которые применяют все анимированные свойства для создания визуального элемента, включая следующие:- .
clickable()
: модификатор, который делаетBox
интерактивным. -
.dropShadow()
: Сначала применяются две внешние тени. Их цвет и альфа-канал связаны с анимированными значениями (blueDropShadow
и т. д.) и создают начальный рельеф. -
.innerShadow()
: две внутренние тени рисуются поверх фона. Их свойства связаны с другим набором анимированных значений (innerShadowColor1
и т. д.) и создают отступ.
- .
Результат
Создание градиентных теней
Тени не ограничиваются сплошными цветами. API теней поддерживает Brush
, позволяющую создавать градиентные тени.
Box( modifier = Modifier .width(240.dp) .height(200.dp) .dropShadow( shape = RoundedCornerShape(70.dp), shadow = Shadow( radius = 10.dp, spread = animatedSpread.dp, brush = Brush.sweepGradient( colors ), offset = DpOffset(x = 0.dp, y = 0.dp), alpha = animatedAlpha ) ) .clip(RoundedCornerShape(70.dp)) .background(Color(0xEDFFFFFF)), contentAlignment = Alignment.Center ) { Text( text = breathingText, color = Color.Black, style = MaterialTheme.typography.bodyLarge ) }
Ключевые моменты кода
-
dropShadow()
добавляет тень позади блока. -
brush = Brush.sweepGradient(colors)
окрашивает тень градиентом, который чередуется с списком предопределенныхcolors
, создавая эффект радуги.
Результат
Вы можете использовать кисть в качестве тени для создания градиента dropShadow()
с «дышащей» анимацией:
Комбинируйте тени
Вы можете комбинировать и накладывать модификаторы dropShadow()
и innerShadow()
для создания различных эффектов. В следующих разделах показано, как создавать неоморфные, необруталистские и реалистичные тени с помощью этой техники.
Создание неоморфных теней
Нейоморфные тени отличаются мягкостью и органичностью, которые выделяются на фоне. Чтобы создать такие тени, выполните следующие действия:
- Используйте элемент, цвет которого совпадает с цветом фона.
- Нанесите две слабые противостоящие друг другу тени: светлую тень в один угол и темную тень в противоположный угол.
Следующий фрагмент кода накладывает два модификатора dropShadow()
для создания неоморфного эффекта:
@Composable fun NeumorphicRaisedButton( shape: RoundedCornerShape = RoundedCornerShape(30.dp) ) { val bgColor = Color(0xFFe0e0e0) val lightShadow = Color(0xFFFFFFFF) val darkShadow = Color(0xFFb1b1b1) val upperOffset = -10.dp val lowerOffset = 10.dp val radius = 15.dp val spread = 0.dp Box( modifier = Modifier .fillMaxSize() .background(bgColor) .wrapContentSize(Alignment.Center) .size(240.dp) .dropShadow( shape, shadow = Shadow( radius = radius, color = lightShadow, spread = spread, offset = DpOffset(upperOffset, upperOffset) ), ) .dropShadow( shape, shadow = Shadow( radius = radius, color = darkShadow, spread = spread, offset = DpOffset(lowerOffset, lowerOffset) ), ) .background(bgColor, shape) ) }

Создайте необруталистские тени
Необруталистский стиль отличается контрастной блочной компоновкой, яркими цветами и толстыми границами. Для создания этого эффекта используйте dropShadow()
с нулевым размытием и чётким смещением, как показано в следующем фрагменте:
@Composable fun NeoBrutalShadows() { SnippetsTheme { val dropShadowColor = Color(0xFF007AFF) val borderColor = Color(0xFFFF2D55) Box(Modifier.fillMaxSize()) { Box( Modifier .width(300.dp) .height(200.dp) .align(Alignment.Center) .dropShadow( shape = RoundedCornerShape(0.dp), shadow = Shadow( radius = 0.dp, spread = 0.dp, color = dropShadowColor, offset = DpOffset(x = 8.dp, 8.dp) ) ) .border( 8.dp, borderColor ) .background( color = Color.White, shape = RoundedCornerShape(0.dp) ) ) { Text( "Neobrutal Shadows", modifier = Modifier.align(Alignment.Center), style = MaterialTheme.typography.bodyMedium ) } } } }

Создавайте реалистичные тени
Реалистичные тени имитируют тени в физическом мире — они кажутся освещёнными первичным источником света, что приводит к появлению как прямой, так и более рассеянной тени. Вы можете накладывать несколько экземпляров dropShadow()
и innerShadow()
с разными свойствами, чтобы воссоздать реалистичные эффекты теней, как показано в следующем фрагменте кода:
@Composable fun RealisticShadows() { Box(Modifier.fillMaxSize()) { val dropShadowColor1 = Color(0xB3000000) val dropShadowColor2 = Color(0x66000000) val innerShadowColor1 = Color(0xCC000000) val innerShadowColor2 = Color(0xFF050505) val innerShadowColor3 = Color(0x40FFFFFF) val innerShadowColor4 = Color(0x1A050505) Box( Modifier .width(300.dp) .height(200.dp) .align(Alignment.Center) .dropShadow( shape = RoundedCornerShape(100.dp), shadow = Shadow( radius = 40.dp, spread = 0.dp, color = dropShadowColor1, offset = DpOffset(x = 2.dp, 8.dp) ) ) .dropShadow( shape = RoundedCornerShape(100.dp), shadow = Shadow( radius = 4.dp, spread = 0.dp, color = dropShadowColor2, offset = DpOffset(x = 0.dp, 4.dp) ) ) // note that the background needs to be defined before defining the inner shadow .background( color = Color.Black, shape = RoundedCornerShape(100.dp) ) // // .innerShadow( shape = RoundedCornerShape(100.dp), shadow = Shadow( radius = 12.dp, spread = 3.dp, color = innerShadowColor1, offset = DpOffset(x = 6.dp, 6.dp) ) ) .innerShadow( shape = RoundedCornerShape(100.dp), shadow = Shadow( radius = 4.dp, spread = 1.dp, color = Color.White, offset = DpOffset(x = 5.dp, 5.dp) ) ) .innerShadow( shape = RoundedCornerShape(100.dp), shadow = Shadow( radius = 12.dp, spread = 5.dp, color = innerShadowColor2, offset = DpOffset(x = (-3).dp, (-12).dp) ) ) .innerShadow( shape = RoundedCornerShape(100.dp), shadow = Shadow( radius = 3.dp, spread = 10.dp, color = innerShadowColor3, offset = DpOffset(x = 0.dp, 0.dp) ) ) .innerShadow( shape = RoundedCornerShape(100.dp), shadow = Shadow( radius = 3.dp, spread = 9.dp, color = innerShadowColor4, offset = DpOffset(x = 1.dp, 1.dp) ) ) ) { Text( "Realistic Shadows", modifier = Modifier.align(Alignment.Center), fontSize = 24.sp, color = Color.White ) } } }
Ключевые моменты кода
- Применяются два связанных модификатора
dropShadow()
с различными свойствами, за которыми следует модификаторbackground
. - Для создания эффекта металлического ободка по краю компонента применяются цепочка модификаторов
innerShadow()
.
Результат
Предыдущий фрагмент кода выводит следующее:
