Compose Broadcast – Telegram
Compose Broadcast
5.81K subscribers
344 photos
88 videos
565 links
Все о Jetpack Compose и Compose Multiplatform

YouTubе канал: https://youtube.com/androidBroadcast
Android - @android_broadcast
iOS - @ios_broadcast
Kotlin - @kotlin_broadcast
Download Telegram
⚙️ Pausable composition in lazy prefetch: как Compose теперь борется с лагами при прокрутке

Важное изменение в Compose 1.10: pausable composition в lazy prefetch теперь включен по умолчанию. Это фундаментальное улучшение в работе runtime, которое значительно уменьшает лаги при сложных UI-нагрузках.

Раньше композиция, раз начавшись, должна была выполниться до конца. Если она была сложной (много элементов, тяжелые вычисления), это могло заблокировать главный поток дольше, чем длится один кадр и получали Freeze Frame и визуальные лаги скролла.

Теперь Compose Runtime может приостанавливать работу, если время на отрисовку кадра заканчивается, и продолжить её в следующем интервале. Особенно эффективно это работает в связке с предзагрузкой (prefetch) ленивых списков.

🔄 Как это работает с Lazy layouts:

// Увеличиваем окно кэша для большего пространства предзагрузки
val cacheWindow = LazyLayoutCacheWindow(
ahead = 0.5f, // 50% вперед
behind = 0.3f // 30% назад
)

val state = rememberLazyListState(cacheWindow = cacheWindow)

LazyColumn(state = state) {
items(heavyItems) { item ->
HeavyComposable(item) // Теперь не заблокирует UI
}
}


🎯 Ключевые преимущества:
1. Плавная прокрутка — даже с тяжелыми элементами
2. Композиция подстраивается под время для отрисовки кадра — композиция «уступает» место другим операциям
3. Никакой сложной настройки — не требует изменения кода приложения

Эта оптимизация — часть продолжающейся работы Google над производительностью Compose. Уже пробовали? Делитесь наблюдениями в комментариях!

#Compose #Производительность #AndroidDev #JetpackCompose
Please open Telegram to view this post
VIEW IN TELEGRAM
👍45🔥177
🎭 Динамическое управление shared element анимациями в Compose

В Compose 1.10.0 вы можете динамически включать и отключать анимации shared element в зависимости от условий навигации или состояния UI. Это особенно полезно, когда нужно анимировать переход только в определенных сценариях.

Раньше sharedElement() и sharedBounds() автоматически анимировали изменения layout при нахождения совпадению по ключу. Теперь можно контролировать эту анимацию через конфигурацию SharedContentConfig.


// отим анимировать переход только с экрана A на экран B, но не обратно
SharedTransitionLayout {
val transition = updateTransition(currentState)

transition.AnimatedContent { targetState ->
// Конфигурация, зависящая от состояния
fun animationConfig(): SharedTransitionScope.SharedContentConfig {
return object : SharedTransitionScope.SharedContentConfig {
override val SharedTransitionScope.SharedContentState.isEnabled: Boolean
get() = transition.currentState == "A" &&
transition.targetState == "B"
}
}

...
}
}


⚠️ Важно: По умолчанию, если shared element отключается во время анимации, текущая анимация завершается до удаления элемента. Это предотвращает резкие обрывы.

Новая фича даёт разработчикам больше контроля над анимациями, делая интерфейсы более предсказуемыми и оптимизированными.

#Compose #AndroidDev #Анимация #UI
👍9
⚙️ Новый API `retain` в Compose: сохраняем сложные объекты при смене конфигурации

Compose 1.10 представляет новую функцию retain, которая заполняет важный пробел между существующими API управления состоянием. Теперь можно сохранять объекты между изменениями конфигурации без необходимости их сериализации!

- remember — сохраняет между рекомпозициями смена конфигурации
- rememberSavable — сохраняет между пересозданиями активити ⚠️ требует сериализации
- retain — сохраняет при смене конфигурации без сериализации не работает при убийстве процесса

@Composable
fun MediaPlayer() {
val applicationContext = LocalContext.current.applicationContext

// ExoPlayer будет сохранен при повороте экрана
val exoPlayer = retain {
ExoPlayer.Builder(applicationContext)
.setSeekBackIncrementMs(5000)
.setSeekForwardIncrementMs(5000)
.build()
}

// Воспроизведение не прервется при смене конфигурации
DisposableEffect(Unit) {
onDispose { exoPlayer.release() }
}
// ...
}


Под капотом сохранение объекта происходит через механизм ViewModel и имеет такой же цикл жизни

Фича разработана при активном участии AndroidDev-сообщества, особенно команды Circuit. Отличный пример того, как обратная связь разработчиков влияет на развитие платформы!

#Compose #AndroidDev
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥65👍148👎1
⚙️ Доступен первый RC Compose Multiplatform 1.10.0 с фиксами багов и улучшениями

#compose
Please open Telegram to view this post
VIEW IN TELEGRAM
12👍7
⚙️ Создание кастомного Toolbar в Jetpack Compose: от проблемы к решению

Разработчик из ПСБ поделился опытом создания кастомного Toolbar в Compose. Основная задача — правильное центрирование заголовка и подзаголовка при динамическом контенте слева и справа (иконки, текст переменной длины).

Решения "в лоб" не сработали:
- Row с Weight приводит к лишним рекомпозициям
- Ручной расчет ширины текста — непредсказуемо и сложно
- Проблема в разных фазах измерения Compose

Решением стал кастомный Layout. Вместо стандартных компоновок используется Layout, который измеряет все элементы за один проход.

#Compose #Android #UI
Please open Telegram to view this post
VIEW IN TELEGRAM
👍15🤔3👎1
⚙️ Modifier.clip() — изменение формы Composable

Когда нужен скруглённый угол, закруглённая аватарка или обрезка по кастомной форме — используйте clip(). Это не просто визуальный эффект: clip обрезает и содержимое, и границы, и все клики внутри.

// Скруглённые углы
Box(
modifier = Modifier
.size(100.dp)
.clip(RoundedCornerShape(8.dp))
.background(Color.Blue)
)

// Круглая аватарка
Image(
painter = painterResource(id = R.drawable.avatar),
contentDenoscription = null,
modifier = Modifier
.size(64.dp)
.clip(CircleShape)
)


Вы можете использовать не только стандартные Shape, но и реализовать свою форму:
// Звёздочка
val starShape = object : Shape {
override fun createOutline(
size: Size,
layoutDirection: LayoutDirection,
density: Density
): Outline {
return Outline.Generic(Path().apply {
// рисуешь путь звёздочки
})
}
}

Box(
modifier = Modifier
.size(100.dp)
.clip(starShape)
.background(Color.Yellow)
)


Если в LazyColumn много элементов с clip — это не критично для производительности, но помни:
👉 clip() работает на GPU (относительно дёшево)
👉 Если в clip используется сложная форма (кастомный Path) — может работать медленнее
👉 Для списков с большим количеством элементов профилируй через Compose Layout Inspector

#Compose
Please open Telegram to view this post
VIEW IN TELEGRAM
29👍15🤯3👎1
⚙️ Пример настройки скриншот тестирования для Compose UI с помощью Roborazzi

Я всегда скептически относился к Pixel Perfect тестированию UI, коим и являются Screenshot тесты: сложно, масштабируется так себе, да еще и настраиваешься тольк под определенные экраны.

Что вы думаете про Pixel Perfect тесты ?

#Compose #Тестирование
Please open Telegram to view this post
VIEW IN TELEGRAM
👎13🤯11👍7🔥5
This media is not supported in your browser
VIEW IN TELEGRAM
🛠 Riflesso - плагин для IDEA с показом областей кода, где происходит рекомпозиция в реальном времени. Поддерживается Android и Desktop таргеты

📃 Подробнее про реализацию плагина автор рассказал в статье (RU,30м)

#Compose #IDEA #AndroidStudio #Android #Desktop
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥108👍76👎1
🎄 Итоги года для @compose_broadcast

Классный год для Compose. Ключевыми событиями отмечу выход Stable Compose for iOS, а также Compose 1.10, который наконец-то смог сравняться в производительности UI под Android на View в релизных сборках со всеми оптимизация.

Канал уходит на новогодние каникулы. Встретимся с вами в 2026 в @compose_broadcast и других каналах broadcast

💬 Чем для вас отметился 2025 и что ждёте в будущем году?
Please open Telegram to view this post
VIEW IN TELEGRAM
🎉2111👎8🔥3👍2
⚙️ Compose Multiplatform 1.10.0 — крупнейший релиз года! (ну он пока единственный)

Привет! Вышла стабильная версия Compose Multiplatform 1.10.0 с революционными изменениями для кроссплатформенной разработки. Давайте разберём главные фичи, которые изменят ваш подход к KMP.

🔥 Главная фишки релиз - Preview аннотация теперь работает в коде из commonMain — наконец-то!

// Работает в commonMain!
@Preview
@Composable
fun MyComponentPreview() {
MyTheme {
MyComponent()
}
}


МИГРАЦИЯ! Депрекейт org.jetbrains.compose.ui.tooling.preview.Preview → переходите на androidx.compose.ui.tooling.preview.Preview

👉 Compose Hot Reload встроен в Compose плагин и подключать отдельно больше не нужно
⚙️ Базовая поддержка библиотеки navigation3 и публикация adaptive-navigation3
🍎 iOS: Поддержка Liquid Glass и полная поддержка WindowInsets
⚙️ Улучшили производительность Compose Desktop

⚠️ Breaking Changes & Миграция

Требования к версиям Kotlin
- Kotlin 2.1.20+ (обязательно из-за Hot Reload)
- Kotlin 2.2+ для native и web платформ

🐘 Депрекейт dependency aliases в Gradle plugin → используйте координаты артефактов из Maven
// build.gradle.kts
kotlin {
sourceSets {
commonMain.dependencies {
implementation(compose.ui)
// Заменяем на
implementation("org.jetbrains.compose.ui:ui:1.10.0")
}
}
}


Кто уже обновился? Какие впечатления от Preview в commonMain? Делитесь в комментариях!

#compose #gradle #kmp #android #ios #desktop
Please open Telegram to view this post
VIEW IN TELEGRAM
37🔥27👍3👎1
⚙️ Вышел Jetpack Compose 1.10.1

Обновляйте зависимости или сразу всё через BOM файл:
dependencies {
implementation(
platform("androidx.compose:compose-bom-beta:2026.01.00")
)
}

#compose #android
Please open Telegram to view this post
VIEW IN TELEGRAM
👍25👎1
⚙️ В Compose появится FlexBoxLayout - аналог подобного из CSS и уже есть подобная реализация на View (🐱 исходники на GitHub)

🔗 Pull Request в AOSP

#Compose
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥31👍81👎1
Пример использования FlexBoxLayout в Compose

// По умолчанию FlexBox работает как Row (FlexDirection.Row).
FlexBox(
modifier = Modifier.fillMaxWidth(),
config = {
direction =
if (constraints.maxWidth < 400.dp.roundToPx()) FlexDirection.Column
else FlexDirection.Row
},
) {
// Этот элемент имеет фиксированный размер
// и не участвует в распределении свободного пространства.
Box(
modifier = Modifier.size(80.dp).background(Color.Magenta),
contentAlignment = Alignment.Center,
) {
Text("Fixed")
}

// Этот элемент имеет коэффициент grow = 1
// и займет 1/3 оставшегося пространства.
Box(
modifier = Modifier
.height(80.dp)
.flex { grow = 1f }
.background(Color.Yellow),
contentAlignment = Alignment.Center,
) {
Text("Grow = 1")
}

// Этот элемент имеет коэффициент grow = 2
// и займет 2/3 оставшегося пространства.
Box(
modifier = Modifier
.height(80.dp)
.flex { grow = 2f }
.background(Color.Green),
contentAlignment = Alignment.Center,
) {
Text("Grow = 2")
}
}


#Compose
🔥20👍94👎2
This media is not supported in your browser
VIEW IN TELEGRAM
⚙️ Кастомизируемый жест прокрутки (Fling) для списков в Compose. Исходники на 🐱 GitHub

#Compose
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥20👍43👎1
🔥 Compose Hot Reload 1.0.0 — стабильный релиз!

JetBrains выпустили стабильную версию Compose Hot Reload, и это действительно круто!

Теперь когда меняете код Compose UI → жмете Cmd+S/Ctrl+S → изменения мгновенно применяются в запущенном приложении. Без перезапуска. Без потери state.

Можно добавлять/удалять функции, классы, параметры — практически любые изменения кода работают.

Стабильная версия 1.0.0 — больше не beta
Отдельный плагин НЕ нужен — встроен в Compose Multiplatform 1.10+, работает из коробки (zero configuration)
⚠️ Работает только на JVM Desktop — и вот почему это важно понять:

Почему только JVM Desktop?
Hot Reload требует JetBrains Runtime с DCEVM (Dynamic Code Evolution VM). Обычная JVM умеет перезагружать только тела методов. DCEVM может делать произвольные изменения кода — добавлять поля в классы, менять иерархию, интерфейсы и т.д.
Android и iOS не могут использовать JetBrains Runtime с DCEVM. Поэтому Hot Reload технически невозможен на этих платформах.

💡 Зачем это мобильным разработчикам?
Еще один повод добавить Desktop таргет в ваш KMP проект!

Даже если вы не планируете запускать Desktop версию в прод - это мощный инструмент для разработки:
👉 Быстро итерируете UI на Desktop с Hot Reload
👉 Проверяете изменения
👉 Переносите на Android/iOS

Скорость итераций UI вырастает в разы. Вместо "изменил → собрал → запустил → дождался" получается "изменил → Cmd+S → увидел результат".

Короче: если делаете Compose Multiplatform — попробуйте обязательно. Desktop таргет окупится только ради Hot Reload.

🔗 Подробности в блоге JetBrains и в документации

#Compose #KMP #Desktop #JVM #CMP
👍28🔥8👎31
⚙️ Landscapist - новая библиотека для загрузки изображений в Compose Multiplatform

Landscapist — это модульная библиотека для загрузки изображений, построенная специально для Compose. В отличие от монолитных решений, она предлагает гибкую архитектуру с поддержкой различных движков загрузки: Glide, Coil и Fresco.

Ключевые фичи:
👉 Compose Multiplatform из коробки. Библиотека изначально проектировалась для работы на Android, iOS, Desktop и Web. Один API для всех платформ.
👉 Модульная архитектура. Вы подключаете только то, что нужно. Core-модуль весит минимум, а специфичные функции (placeholder, эффекты, анимации) добавляются отдельными зависимостями.
👉 Декларативные модификаторы. Все возможности библиотеки доступны через Compose-модификаторы, что делает код чище и понятнее.
👉 Продвинутая обработка состояний. Встроенная поддержка loading, success, failure с возможностью кастомизации под каждое состояние.
👉 Эффекты и анимации. Circular Reveal, crossfade, shimmer-эффект — всё это доступно out-of-the-box.
👉 Palette API. Автоматическое извлечение цветовой палитры из изображения для создания адаптивного UI.

suspend fun loadImage(url: String) {
val request = ImageRequest.builder()
.model(url)
.size(width = 800, height = 600)
.build()

landscapist.load(request).collect { result ->
when (result) {
is ImageResult.Loading -> {
println("Loading...")
}
is ImageResult.Success -> {
val imageBitmap = result.data
val dataSource = result.dataSource // MEMORY, DISK, or NETWORK
println("Loaded from: $dataSource")
}
is ImageResult.Failure -> {
val error = result.reason
println("Error: ${error.message}")
}
}
}
}


🐱 Исходный код
📃 Документация

#Compose
Please open Telegram to view this post
VIEW IN TELEGRAM
👍365👎4🤔2
Forwarded from Android Live 🤖
Styles API в Jetpack Compose 🚀

Интересного завезли. В Compose появился экспериментальный API для работы со стилями, который делает их изменение гораздо удобнее.

Сейчас работа с динамическими стилями требует немало ручного труда. И хотя InteractionSource неплохо приспособлен для этих задач, Styles API упрощает процесс в разы.

Ниже приведён пример кнопки, которая меняет цвет при состояниях hovered и pressed.


@Composable
fun InteractiveButton(onClick: () -> Unit) {
ClickableStyleableBox(
onClick = onClick,
style = {
background(Color.Green)
size(150.dp)
hovered { animate { background(Color.Yellow) } }
pressed { animate { background(Color.Red) } }
}
)
}


Выглядит неплохо, посмотрим, что будет дальше. Детали тут.
21🔥11👍4👎1
This media is not supported in your browser
VIEW IN TELEGRAM
⚙️ Пример реализации подобной анимации в Compose. Весь подход выделили в библиотеку 🐱 ThemeAnimator, которую можете подключить к себе.

Анимация смены темы реализована через перехват отрисовки в Modifier.Node: сначала делается снимок UI в старой теме, затем тема переключается, фиксируется новое состояние и запускается анимация между двумя скриншотами. Переход рисуется как круговое раскрытие новой темы, что позволяет избежать мерцаний и добиться плавного эффекта.

#Compose #Анимация
Please open Telegram to view this post
VIEW IN TELEGRAM
1🔥81👍85👎4