Для обновления просто обновите BOM
implementation(platform("androidx.compose:compose-bom:2026.08.00"))⚠️ Важно: требуется
compileSdk = 37 и AGP 9.2.0+.В этом релизе:
🎨 Mesh‑градиенты и HDR
📐 Именованные области в Grid
✍️ Редактируемое форматирование текста
🎬 Двухстадийные анимации
⚡️ Ускоренный старт и Keyed SideEffect
🔧 Не обошлось без breaking changes
В ближайших постах разберу каждую фичу подробно с примерами. Подпишись на @ComposeBroadcast, чтобы ничего не пропустить!
Compose Multiplatform 1.12.0 уже на стадии RC
#Compose #Android
Please open Telegram to view this post
VIEW IN TELEGRAM
Теперь области можно задавать через имя в Grid в дополнение к индексу. Дайте областям имена и размещайте элементы с помощью
gridItem(areaId = "header"):Grid(
config = {
area("header", row = 0, column = 0, rowSpan = 1, columnSpan = 2)
area("sidebar", row = 1, column = 0)
area("content", row = 1, column = 1)
}
) {
HeaderSection(modifier = Modifier.gridItem(areaId = "header"))
Sidebar(modifier = Modifier.gridItem(areaId = "sidebar"))
Content(modifier = Modifier.gridItem(areaId = "content"))
}
#Compose
Please open Telegram to view this post
VIEW IN TELEGRAM
Очень хочется верить в то, что это реально, а также что это можно сделать просто без отладки и замера характеристик рендеринга.
#Compose #Performance
Please open Telegram to view this post
VIEW IN TELEGRAM
👉 В
BasicTextField можно теперь программно применять SpanStyle и ParagraphStyle через edit { addStyle(...) }:state.edit {
addStyle(SpanStyle(fontWeight = FontWeight.Bold), start = 0, end = 9)
}Стили сохраняются при смене конфигурации.
👉 Программное управление выделением. Новый
SelectionState с методами selectAll(), clear(), select(range) и extendSelectionByWord()val selectionState = rememberSelectionState()
val characterCount = selectionState.selectedTexts.sumOf { it.text.length }
Column {
SelectionContainer(state = selectionState) {
Column { BasicText(text = "Text to be selected...") }
}
if (characterCount > 0) {
BasicText(text = "Characters selected: $characterCount")
}
}
👉 Текстовые поля теперь нативно поддерживают Android Credential Manager (API 34+) через семантическое свойство
credentialRequest. Passkeys, сохранённые учётные данные — прямо из поля ввода.👉 Новые
KeyboardType: Date, Time, DateTime, SignedDecimal.👉
BasicSecureTextField теперь по умолчанию использует TextObfuscationMode.System#Compose
Please open Telegram to view this post
VIEW IN TELEGRAM
Slot Table — это внутренняя структура данных, в которой Compose Runtime хранит всё дерево композиции: каждый Composable, каждое состояние
remember, ключи и связи между элементами. С момента первого релиза Slot Table был реализован на основе Gap Buffer — структуры, вдохновлённой текстовыми редакторами.Gap Buffer хранит все группы в плоском
IntArray с пустой областью (gap), которая перемещается к месту следующей записи. Его проблема что при удалении или переупорядочивании элементов gap должен переместиться к нужной позиции, копируя все элементы на своём пути.Для композиции с 1000 группами удаление группы №500 требует перемещения gap через 500 позиций. Переупорядочивание одного элемента в списке из 100 записей требует двух перемещений gap, каждое с копированием части массива. По итогу чем больше композиция, тем дороже структурные изменения.
Новая экспериментальная реализация Slot Table на основе связного списка — LinkBuffer. Вместо плоского массива группы хранятся в виде связного списка с явными указателями на родителя, первого потомка и следующего соседа. Ключевое преимущество - структурные операции (удаление, перемещение, переупорядочивание) теперь требуют обновления указателей вместо копирования массивов.
По тестам разработчиков переупорядочивание списков стало более чем в 2 раза быстрее. Большинство других операций стали примерно на 10% быстрее
Флаг новой реализации отключён по умолчанию. Включить можно двумя способами:
1. Программно (только для debug-сборок):
// ДО вызова setContent()
ComposeRuntimeFlags.isLinkBufferComposerEnabled = true
2. Через R8/ProGuard (для release-сборок):
-assumevalues public class androidx.compose.runtime.ComposeRuntimeFlags {
static boolean isLinkBufferComposerEnabled return true;
}В R8 release-сборках правило ProGuard всегда имеет приоритет над программной установкой.
#Compose #UnderHood
Please open Telegram to view this post
VIEW IN TELEGRAM
В Compose Multiplatform 1.12.0-rc01 (через Compose Hot Reload 1.2.0) появился экспериментальный MCP-сервер для Hot Reload.
Агент теперь может:
👉
reload — пересобрать и хотрелоудить👉
take_screenshot — снять текущее окно👉
get_semantic_tree — получить semantic tree (включая Dialog/Popup)👉
click / type_text / scroll — симулировать ввод👉
get_logs — читать runtime-логи и ошибкиПодключается через Gradle-таску. В конфиге агента (
.mcp.json):{
"mcpServers": {
"compose-hot-reload": {
"command": "./gradlew",
"args": [
"--no-daemon",
"--quiet",
"--console=plain",
"hotMcpServer"
]
}
}
}#Compose #CMP #Desktop #HotReload
Please open Telegram to view this post
VIEW IN TELEGRAM
JetBrains исследует Compose HTML для Server-Side Rendering (SSR)
Frederik Pietzko из JetBrains решил проверить идею: что если Compose-компоненты рендерить в HTML на сервере, а не только в canvas/JS. В Compose добавляется обычная функция с типами:
Идея — добавить функции Compose HTML
Мне нравится направление. Если Compose HTML получит нормальный серверный рендер, у Kotlin наконец появится шанс на type-safe fullstack без ухода в JS-фреймворки. Пока же это просто интересный разговор, который стоит почитать.
🔗 Источник
#Compose #ComposeHTML #Kotlin #SSR
Frederik Pietzko из JetBrains решил проверить идею: что если Compose-компоненты рендерить в HTML на сервере, а не только в canvas/JS. В Compose добавляется обычная функция с типами:
@Composable
fun Card(title: String, count: Int) {
Div({ classes("card") }) {
H3 { Text(title) }
Span { Text(count.toString()) }
}
}
Идея — добавить функции Compose HTML
renderToString / renderToBytes, чтобы один раз прогнать composition и получить строку HTML. Без браузера, без DOM, без recomposition и effects (как классический SSR). Пока речь идет только про JVM тартегМне нравится направление. Если Compose HTML получит нормальный серверный рендер, у Kotlin наконец появится шанс на type-safe fullstack без ухода в JS-фреймворки. Пока же это просто интересный разговор, который стоит почитать.
🔗 Источник
#Compose #ComposeHTML #Kotlin #SSR
Официально появился
mediaQuery — лямбда, которая смотрит на текущий UiMediaScope и возвращает boolean. Внутри можно проверять:👉
windowWidth / windowHeight👉
windowPosture (Tabletop, Book, Flat)👉
pointerPrecision (Fine → Coarse → Blunt → None)👉
keyboardKind (Physical / Virtual / None)👉
hasCamera / hasMicrophone👉
viewingDistance (Near / Medium / Far)Включается одним флагом при запуске приложения:
ComposeUiFlags.isMediaQueryIntegrationEnabled = true
Для размеров, которые дёргаются часто, лучше использовать
derivedMediaQuery (обёртка над derivedStateOf):// Composable code
val narrowerThanMedium by derivedMediaQuery {
windowWidth < WindowSizeClass.WIDTH_DP_MEDIUM_LOWER_BOUND.dp
}
val narrowerThanExpanded by derivedMediaQuery {
windowWidth < WindowSizeClass.WIDTH_DP_EXPANDED_LOWER_BOUND.dp
}
when {
narrowerThanMedium -> SinglePaneLayout()
narrowerThanExpanded -> TwoPaneLayout()
else -> ThreePaneLayout()
}
Самый чистый кейс — foldable в tabletop:
// Composable code
if (mediaQuery { windowPosture == UiMediaScope.Posture.Tabletop }) {
TabletopLayout()
} else {
FlatLayout()
}
Это API не замена WindowSizeClass, но удобный единый вход для всего, что меняется в рантайме. Если пишете адаптив под foldable / desktop / TV — возьмите на заметку. Пока не перенесли в Compose Multiplatform.
Документация Media Query API
#Compose #JetpackCompose #Adaptive #UI #Android
Please open Telegram to view this post
VIEW IN TELEGRAM
KitFlow — небольшая KMP-либа, которая показывает, как можно держать адаптив в одном месте.
Главная идея —
Adaptive.value():val padding = Adaptive.value(
sm = 12.dp,
md = 16.dp,
lg = 20.dp,
tab = 24.dp,
desktop = 28.dp
)
То же самое для высоты кнопки, ширины карточки, scale шрифта, Boolean, цветов, любых кастомных значений. Есть
Adaptive.layoutValue() (по текущей ширине, включая split-screen и resize) и Adaptive.onOrientationChange для портрета/ландшафта.Подключать как зависимость я не рекомендую. Всё, что там есть, легко реализовать самому, и тогда получаешь полную гибкость под свой дизайн-систем и breakpoints. А вот разобрать реализацию и вдохновиться будет полезно, особенно если пишешь shared UI под Android + iOS + Desktop + Web. Я еще для себя делал расширение в зависимости от таргет платформы.
#Compose #CMP #Adaptive #UI
Please open Telegram to view this post
VIEW IN TELEGRAM
Вышел стабильный CMP 1.12.0.
Главное в релизе - MCP в Compose Desktop Hot Reload 1.2.0. Раньше агент правил
@Composable вслепую: не знал, прошёл ли reload, что на экране, не упало ли. Теперь есть сервер с нормальными тулами. Поднимается таской, в .mcp.json достаточно hotMcpServer:{
"mcpServers": {
"compose-hot-reload": {
"command": "./gradlew",
"args": ["--no-daemon", "--quiet", "--console=plain", "hotMcpServer"]
}
}
}Много фиксов и улучшений для Desktop, Web, iOS. Еще отдельно расскажу постами в @compose_broadcast
🔗 Полный список изменений
#Compose #CMP #Desktop #AI
Please open Telegram to view this post
VIEW IN TELEGRAM
В Compose 1.12 у SelectionContainer появился hoisted-state: SelectionState. Раньше весь механизм выделения был спрятан внутри текстовых полей. Теперь же стало возможно:
👉 читать выбранный текст реактивно
👉 выделять программно: всё / диапазон / по словам
👉 сбрасывать выделение
👉 искать цитату по всему контейнеру, даже если она размазана по нескольким
Text// Один SelectionState нельзя шарить между несколькими SelectionContainer
val state = rememberSelectionState()
SelectionContainer(state = state) {
Text("Text to be selected...")
}
val selectedTexts : List<AnnotatedString> = state.selectedTexts
Text("Выбрано символов: ${selectedTexts.sumOf { it.text.length }}")
#Compose
Please open Telegram to view this post
VIEW IN TELEGRAM
#Compose #Android #Remote #BDUI
Please open Telegram to view this post
VIEW IN TELEGRAM
Плагин через
ComponentMetadataRule подвешивает available-at вариант с tvOS-klib к официальному org.jetbrains.compose.*. Дальше решает штатный variant resolution Gradle: tvOS-таргет забирает форк, а iOS/Android/Desktop продолжают резолвить артефакт JetBrains. Отдельно перехватывается маркер плагина через
pluginManagement.resolutionStrategy.eachPlugin, чтобы работала упаковка Compose Resources.// settings.gradle.kts
plugins {
id("dev.sajidali.compose-tvos") version "1.3.0"
}
Всё,
dependencies {} не меняешь. Приём годится далеко за пределами tvOS: так же можно подсунуть свою пересборку любой чужой библиотеки, у которой не хватает таргета.⚠️ Под капотом форк 15 групп библиотек Compose и AndroidX, опубликованный одним человеком. Автор прямо пишет, что тянет его ровно настолько, насколько нужно для собственного приложения: ни обязательств, ни расписания релизов. Держать
compose.runtime и compose.ui в проде из такого источника решение уровня «мы понимаем, на что подписались». Форк сейчас держит линию 1.12, но догонять апстрим ему придётся рукамиСмотреть стоит ради механики. Тащить в прод - явным фанатам поддержки tvOS из CMP.
#Compose #ComposeMultiplatform #KMP #Gradle
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Следит за теми Composable где проведете инициализацию
@Composable
fun UserProfileCard(user: User) {
Card(
modifier = Modifier
.fillMaxWidth()
// Attach highlighter
.recompositionHighlighter("UserProfileCard")
) {
Text(text = user.name)
}
}
#Compose
Please open Telegram to view this post
VIEW IN TELEGRAM
В Issue CMP-10781 в официальном баг-трекере CMP Константин Цховребов расписал рецепт офлайн-приложения Compose Web: после Gradle-задачи
wasmJsBrowserDistribution отдельная задача (исходный код в issue) копирует статику, вычисляет SHA-256 по путям и содержимому и генерирует service-worker.js.Что получается на практике:
👉
index.html, JS, Wasm и Compose Resources кладутся в Cache Storage👉 SW перехватывает только эти файлы
👉 запросы к API идут в сеть как обычно
👉 новая сборка меняет версию кэша и удаляет старый
Ограничения:
👉
cache.addAll() падает, если не скачался хотя бы один файл из списка👉 source maps тоже попадают в кэш, если не отфильтровать
👉 новая версия подхватится только при следующем запуске
Это не официальная задача в плагине CMP и не «PWA из коробки», а лишь рецепт, который вполне может стать частью официального плагина в будущем.
#Compose #CMP #Wasm
Please open Telegram to view this post
VIEW IN TELEGRAM
This media is not supported in your browser
VIEW IN TELEGRAM
Полезно как для людей, так и для разработки с AI агентами, так как не приходится прокликивать приложение до экрана при каждых изменениях, что экономит токены.
💰Решение платное - 5$/месяц, есть триал на 2 недели.
#Compose #CMP #Android #Desktop #iOS
Please open Telegram to view this post
VIEW IN TELEGRAM