Разработка
Сетчатые градиенты в Jetpack Compose
Обновите альфа-канал Compose, откройте генератор и нарисуйте что-нибудь красивое.
Долгие годы сетчатые (mesh) градиенты были той прекрасной вещью, которую мы постоянно заимствовали с других платформ. Вы видели их повсюду — в Instagram*, на обоях Apple, на маркетинговых страницах Stripe, в каждом файле Figma, созданном после 2022 года. SwiftUI добавил MeshGradient ещё в iOS 18. На Android мы обходились хитрыми шейдерными хаками и отличными сторонними библиотеками.
Сейчас это изменится. Jetpack Compose теперь имеет собственный API для создания сетчатых градиентов, встроенный прямо в androidx.compose.ui. В этой статье мы рассмотрим, что это такое, нарисуем несколько примеров и поговорим о том, чем он отличается как от SwiftUI, так и от решений сообщества, которые вы, возможно, уже используете.
Прежде всего: это всё ещё альфа-версия.
🚧 Давайте определим ожидания, прежде чем мы начнём радоваться.
MeshGradientPainter находится в альфа-канале Compose UI. Я экспериментировал с ним так:
# gradle/libs.versions.toml composeBom = "2026.05.00" composeUi = "1.12.0-alpha03"
Это значит: API может и будет меняться до стабильной версии. Названия параметров, значения по умолчанию и даже сам класс могут быть изменены до тех пор, пока не будет достигнута стабильная версия. Пока не стоит внедрять это в продакшн. Но он абсолютно готов к использованию, прототипированию и обучению — именно этим мы и займемся.
Приятный сюрприз заключается в том, что он является частью основного артефакта androidx.compose.ui, поэтому нет необходимости добавлять дополнительные зависимости и бороться с экспериментальными аннотациями. Переведите ваш Compose UI в альфа-версию, и класс просто… появится.
Общая идея
Обычный линейный или радиальный градиент смешивает цвета вдоль одной линии или от одной точки. Сетчатый градиент — это сетка цветов. Вы размещаете цветные точки на двумерной сетке, и рендер плавно смешивает их все одновременно. Переместите точку, измените цвет, и вся поверхность будет следовать за ней.
В Compose основными строительными блоками являются:
- Вершины (Vertices) — точки сетки. Каждая имеет положение и цвет.
- Патчи (Patches) — ячейки между вершинами. Именно здесь происходит смешивание.
- Кисть (Painter) —
MeshGradientPainter, это обычная кисть ComposePainter, поэтому вы рисуете её с помощьюModifier.paint(...),Modifier.backgroundили внутри Canvas.
Вот самая маленькая возможная сетка — один патч с четырьмя углами:
val painter = remember {
MeshGradientPainter(rows = 1, columns = 1, hasBicubicColor = true) {
// setVertex(row, column, position, color)
setVertex(0, 0, Offset(0f, 0f), Color(0xFFAF52DE)) // top-left
setVertex(0, 1, Offset(1f, 0f), Color(0xFF5856D6)) // top-right
setVertex(1, 0, Offset(0f, 1f), Color(0xFFFF9500)) // bottom-left
setVertex(1, 1, Offset(1f, 1f), Color(0xFFFFCC00)) // bottom-right
}
}
Box(
modifier = Modifier
.fillMaxSize()
.paint(painter)
)
Несколько важных моментов:
- Позиции нормализованы:
(0f, 0f)— это верхний левый угол области рисования,(1f, 1f)— нижний правый. Вы никогда не работаете с пикселями. - Блок конфигурации — это
AMeshGradientScope, иsetVertex— единственный метод, который вам действительно нужен для начала. - Этот блок работает внутри
DrawScopeи отслеживает чтение состояния, что является секретом анимации подобных вещей — подробнее об этом позже.
Хотите более плотный и насыщенный градиент? Просто сдвиньте строки и столбцы и установите больше вершин. Сетка из точек 4×4 дает вам пышное, органичное сочетание шестнадцати цветов, плавно перетекающих друг в друга.

Подводный камень №1: патчи против точек (ошибка на единицу, которая вас подловит)
Это самая важная вещь во всей статье, поэтому прочтите ее дважды.
В SwiftUI вы пишете MeshGradient(width: 3, height: 3, points: …, colors: …) — и width/height — это количество точек вдоль каждой стороны.
В Compose количество патчей — это строки и столбцы, а не точки. В документации это четко указано:
Для заданного количества
rowsиcolumns(количество патчей) всего имеется(rows + 1) * (columns + 1)вершин.
Таким образом, MeshGradientPainter(rows = 3, columns = 3) ожидает сетку 4×4 из вершин (16 штук), где строка и столбец имеют значения от 0 до 3.
Перепутайте это, и вы зададите только часть сетки. Неустановленные вершины по умолчанию имеют позицию (0, 0) и цвет (Transparent), что приводит к сворачиванию участков в угол и отрисовке ненужных объектов. Вот в чем ловушка:
// ❌ WRONG — treats rows/columns as point counts
MeshGradientPainter(rows = gridSize, columns = gridSize) {
for (row in 0 until rows) { // 0 until N → misses the last vertex row
for (column in 0 until columns) {
setVertex(row, column, points[row * columns + column], colors[...])
}
}
}
// ✅ RIGHT — gridSize points per side = (gridSize - 1) patches
MeshGradientPainter(rows = gridSize - 1, columns = gridSize - 1) {
val pointsPerRow = columns + 1
for (row in 0..rows) { // inclusive: every vertex
for (column in 0..columns) {
val index = row * pointsPerRow + column
setVertex(row, column, points[index], colors[index])
}
}
}
Если ваш градиент отображается, но выглядит немного искаженным — странный прозрачный клин возле угла, отдельные треугольные швы — почти всегда причина в этом.
Подводный камень №2: по умолчанию выглядит не как в iOS
Когда я впервые нарисовал сетку 4х4, я был немного разочарован. Она выглядела… полосатой. Жесткие диагональные полосы вместо мягкого, мечтательного перехода, как в iOS.
Исправление заключается в одном параметре:
MeshGradientPainter(rows = 3, columns = 3, hasBicubicColor = true) { … }
По умолчанию hasBicubicColor имеет значение false, что означает билинейную интерполяцию цвета — быструю, но создающую видимые полосы. Установите значение true, и вы получите интерполяцию Кэтмулла-Рома, которая представляет собой плавное, мягкое смешение, которое вы себе представляли. Прямо из документации:
hasBicubicColor— еслиtrue, использует интерполяцию Кэтмулла-Рома для цветов, что приводит к более плавным переходам по сравнению с более простой и немного более быстрой билинейной интерполяцией.
В SwiftUI MeshGradient используется та же идея в параметре smoothsColors — за исключением того, что там он по умолчанию имеет значение true. Так что, если вы переносите дизайн с iOS и задаетесь вопросом, почему Android выглядит более резко, вот ваш ответ.
Заглянем под капот: что на самом деле делает bicubic
«Кэтмулл-Ром» звучит пугающе, но это всего лишь плавная кривая, проходящая через четыре управляющих значения. Вот вся базовая функция — без фреймворков, без необходимости использования Compose:
// Catmull-Rom basis — smooth (C1) interpolation through four control values.
function catmull(p0: number, p1: number, p2: number, p3: number, t: number): number {
const t2 = t * t, t3 = t2 * t;
return 0.5 * (
2 * p1 +
(-p0 + p2) * t +
(2 * p0 - 5 * p1 + 4 * p2 - p3) * t2 +
(-p0 + 3 * p1 - 3 * p2 + p3) * t3
);
}
Пропустите его по строкам вашей сетки, затем пропустите еще раз по полученным результатам, и вы получите бикубическую выборку поверхности — по одному разу для каждого цветового канала. Двухпроходный метод Кэтмулла — это именно то, что делает для вас внутри функция hasBicubicColor = true.
Зачем это знать? Потому что, как только вы освоите математические вычисления, сетка перестанет быть просто «черным ящиком» для Android. Чтобы доказать это, я перестроил тот же рендерер в виде небольшого предварительного просмотра на TypeScript/WebGL — те же нормализованные точки, те же цвета, то же смешение Кэтмулла и Рома — и теперь веб-холст отображает идентичный градиент, что и приложение для Android. В исходном коде есть примечание:
«Это точная интерполяция, которую использует MeshGradientPainter в Android с hasBicubicColor = true, поэтому веб-холст и рендеринг в Android совпадают».
Одно небольшое замечание, которое стоит знать: метод Кэтмулла и Рома может перенасыщаться. Когда вы пропускаете через него четыре насыщенных цвета, смешение может на короткое время выйти за пределы диапазона [0, 1] и выглядеть слегка перенасыщенным, поэтому веб-версия ограничивает каждый канал — и вы увидите ту же самую легкую резкость на Android.
Бонус: генерация цветов, которые не конфликтуют
Случайные цвета создают размытые сетки. Прием, используемый приложением (перенесенный дословно между Android и веб-версией), заключается в выборе одного базового оттенка плюс схемы гармонии, создании небольшой палитры и выборке каждой вершины из нее:
const HARMONY_SCHEMES = [
[0, 30, -30, 15, -15], // analogous
[0, 180, 30, 150], // complementary
[0, 120, 240], // triadic
[0, 150, 210], // split-complementary
[0, 90, 180, 270], // tetradic
];
// pick a random baseHue, offset by a scheme, then sample one color per vertex
Каждый раз получается целостный результат, а не случайная радуга. Забудьте о рутинной работе: генерируйте сетки визуально.
Ручное размещение шестнадцати вершин и настройка шестнадцатеричных цветов методом проб и ошибок быстро надоедают. Поэтому я создал бесплатный веб-инструмент Vectormotionkit, который делает всю кропотливую работу за вас:

Перетаскивайте точки, выбирайте размер сетки и продолжайте создавать новые согласованные палитры, пока что-нибудь не получится — затем нажмите «Копировать». В результате вы получите не скриншот или PNG, а настоящий код Jetpack Compose: импорты, блок MeshGradientPainter с уже заполненными setVertex(...) и блок кода, который его рисует. Вставьте блок в экран, и готово.
И да — он сам обрабатывает баланс точек и патчей из пункта #1 и включает hasBicubicColor, чтобы результат соответствовал предварительному просмотру. На холсте предварительного просмотра используется точная интерполяция Катмулла-Рома из раздела выше, поэтому то, что вы создаете в интернете, то и получаете на устройстве.
💡 Если вы предпочитаете написать собственный редактор, вот один совет: следите, чтобы перетаскиваемые точки сохраняли монотонный порядок — ограничивайте положение каждой точки координатами соседей по строке и столбцу. Если точка пересечёт соседнюю, патч сложится сам на себя, и рендерер отрисует некрасивый резкий треугольник.

Итак… стоит ли это использовать?
Если вы выпускаете продукт сегодня: используйте проверенную временем стороннюю библиотеку (подход AGSL от sinasamaki и библиотека compose-mesh-gradient — отличные варианты) — они работают даже на старых версиях Compose и не сломаются.
Но всё уже ясно. В Compose появится собственная библиотека сетчатых градиентов на основе Painter с управлением кривыми Безье для каждой вершины, и её действительно приятно использовать, как только вы поймете два вышеупомянутых нюанса. Стоит ознакомиться с ней сейчас, чтобы быть готовым к тому дню, когда она станет стабильной.
Вот и всё! Обновите альфа-канал Compose, откройте генератор и нарисуйте что-нибудь красивое.
Подписывайтесь на меня в LinkedIn и Twitter/X 👋 Спасибо!
-
Аналитика магазинов2 недели назадМобильный рынок Ближнего Востока: выручка растёт быстрее загрузок: исследование Bidease и Sensor Tower
-
Новости4 недели назадВидео и подкасты о мобильной разработке 2026.22
-
Новости3 недели назадВидео и подкасты о мобильной разработке 2026.23
-
Маркетинг и монетизация4 недели назадКак создать систему привлечения пользователей, если вы работаете в одиночку
