fa
Feedback
Compose Broadcast

Compose Broadcast

رفتن به کانال در Telegram

Все о Jetpack Compose и Compose Multiplatform YouTubе канал: https://youtube.com/androidBroadcast Android - @android_broadcast iOS - @ios_broadcast Kotlin - @kotlin_broadcast

نمایش بیشتر
5 912
مشترکین
-224 ساعت
-27 روز
+430 روز
آرشیو پست ها
⚙️ Запрещаем composable вне превью через @RequiresOptIn Если composable нужен только для превью из другого модуля, internal не поможет. Зато поможет кастомная аннотация поверх @RequiresOptIn с уровнем ERROR. Создаём @PreviewOnly:
@RequiresOptIn(
    message = "This composable is intended for preview usage only",
    level = RequiresOptIn.Level.ERROR
)
@Retention(AnnotationRetention.BINARY)
@Target(AnnotationTarget.FUNCTION)
annotation class PreviewOnly
Теперь любой, кто попробует вызвать его в продакшн коде без @OptIn(PreviewOnly::class), получит ошибку компиляции, а не warning. Небольшой минус: @OptIn(PreviewOnly::class) придётся писать в каждой превью-функции вручную, автоматического opt-in для @Preview нет. Но это копейки по сравнению с тем, что превью-composable физически не попадёт в продакшн. Источник le0nidas.gr #Compose

🐱 Kompass — ещё одна навигация для Compose Multiplatform Да, у нас уже есть Jetpack Navigation 3 с официальной поддержкой KMP. Тем не менее авторы Kompass смотрят на проблему иначе, и идея тут любопытная. Суть в том, что вся навигация строится на чистых редукторах. Любой переход — это State + Command → State, без побочных эффектов. Вот как это выглядит в коде:
// Граф описывает, какие экраны в нём живут и как рендерятся
class MainNavigationGraph : NavigationGraph {
    override fun canResolveDestination(id: String) =
        id in setOf("home", "profile")

    @Composable
    override fun Content(entry: BackStackEntry, destination: Destination, navController: NavController) {
        when (destination) {
            is MainDestination.Home -> HomeScreen(navController)
            is MainDestination.Profile -> ProfileScreen(navController)
        }
    }
}

// Хост принимает список графов и рендерит текущий экран
@Composable
fun AppNavigation() {
    val navController = rememberNavController(
        startDestination = MainDestination.Home
    )
    KompassNavigationHost(
        navController = navController,
        graphs = persistentListOf(MainNavigationGraph())
    )
}

// Навигация из экрана
navController.navigate(
    entry = BackStackEntry(destinationId = "profile", scopeId = newScope())
)

// Возврат с результатом
navController.pop(result = ProfileResult(userId = "123"))
NavigationState и BackStackEntry иммутабельны, поэтому всю навигацию можно покрыть обычными unit-тестами без инструментации: создаёшь NavigationHandler, кидаешь команду, проверяешь стейт. Особенности: 👉 Таргеты — Android, iOS, Desktop (JVM). 👉 Scopes вместо ViewModel. rememberScoped<T> живёт ровно пока BackStackEntry в стеке, автоматически чистится при pop. 👉 Multi-graph. Несколько независимых графов с собственными лейаутами. Из коробки есть поддержка master-detail для планшетов. 👉 Дип-линки. Через DeepLinkHandler — типизированный парсинг URI в NavigationCommand. 🛠 Библиотека ещё в активной разработке #KMP #ComposeMultiplatform #Navigation #Kotlin #AndroidDev

🐱 Tracey - чёрный ящик для Compose-приложения Tracey записывает все жесты, переходы между экранами и кастомные события в кольцевой буфер — и при краше сохраняет его для воспроизведения. Видишь не просто стектрейс, а весь путь пользователя до момента падения. Не сразу понял куда её применить, но пришла идея интеграции в флоу автоматического прокликивания экрана:
Разрабатываешь фичу локально, кликаешь руками, что-то идёт не так. Вместо того чтобы объяснять разработчику или агенту на словах "я нажал сюда, потом перешёл туда, потом кнопка не сработала" — просто скидываешь ему дамп сессии из Tracey. Он сам восстанавливает картину и сразу работает с контекстом, а не с твоим пересказом.
Структурированный контекст для дебаг-сессии с агентом, чтобы дать четкую информацию. Библиотека на версии 0.0.2, только вышла, в продакшен пока не потащу. Но для этапа разработки и связки с AI-агентами идея выглядит рабочей. #Compose #Android #AndroidDev

⚙️ Rebound — бюджеты рекомпозиций для Compose Одна из моих любимых тем в Compose-разработке — отладка рекомпозиций. Layout In
+5
⚙️ Rebound — бюджеты рекомпозиций для Compose Одна из моих любимых тем в Compose-разработке — отладка рекомпозиций. Layout Inspector, Rebugger, ComposeInvestigator — всё это хорошие инструменты, но у них общий слепой угол: они говорят *сколько* рекомпозиций, но не отвечают на вопрос *нормально ли это для данного компонента*. HomeScreen, который рекомпозируется 10 раз в секунду — это проблема. Анимация, которая делает то же самое — это норма. Число одинаковое, вывод противоположный. Rebound решает именно это. Библиотека работает на основе Kotlin compiler plugin, который классифицирует каждый @Composable по роли и назначает ему бюджет рекомпозиций: 1️⃣ Screen — 3/s. Если экранный компонент рекомпозируется чаще, state утекает вверх по дереву 2️⃣ Leaf — 5/s. Text, Icon, Image — дёшевы сами по себе, но не должны «молотить» 3️⃣ Animated — 120/s. Всё, что использует animate*, Transition, Animatable — пусть работает 4️⃣ Container — 10/s, Interactive — 30/s, List Item — 60/s При скролле бюджеты удваиваются, при анимации и вводе — умножаются на 1.5. Контекст учитывается. Когда что-то выходит за рамки, в логах появляется не просто число, а конкретика:
BUDGET VIOLATION: ProfileHeader rate=11/s exceeds LEAF budget=5/s
  -> params: avatarUrl=CHANGED, displayName=CHANGED
  -> forced: 0 | param-driven: 11 | interaction: IDLE
Какой компонент, какой rate, какой бюджет, какой параметр триггерит, и что делал пользователь в этот момент. 🔨 Плюс IDE-плагин с live-деревом, таблицей горячих точек, timeline-хитмапой и — самое полезное — цветными иконками прямо в редакторе. Зелёный/жёлтый/красный кружок рядом с каждым @Composable. Никакого переключения контекста. Попробую на своих проектах (только не отправляй в прод) — идея с контекстными бюджетами кажется мне намного честнее, чем единый порог для всех компонентов. Решение пока не достигло версии 1.0 но это и некритично, так как не влияет на продакшен код. 🔗 Источник: adital.dev 🐱 Исходники на GitHub #Android #Compose #AndroidDev #Производительность

⚙️ Анимации в Jetpack Compose LazyColumn: почему это сложнее, чем кажется Наткнулся на отличный разбор от ребят из Т-Банка — они переписывали главный экран с XML+View на Compose и столкнулись с проблемой, которую многие, думаю, обходят стороной. Суть: есть LazyColumn, в нём элемент с animateContentSize(). Когда элемент расширяется по высоте, нижние карточки не успевают сместиться в такт — небольшой, но заметный рассинхрон. Казалось бы — поменяй spring на tween, синхронизируй тайминги и готово. Спойлер: нет. 1️⃣ Замена на линейный tween — разницы визуально почти ноль. Скорость placementSpec тоже не влияет на смещение нижних айтемов при ресайзе верхнего. 2️⃣ Установка placementSpec = null — синхронизация появляется, но полностью ломает анимацию перемещения айтемов. Не вариант. 3️⃣ Попытка написать свой модификатор — обречена с самого начала. LazyLayoutAnimationSpecsNode помечен как internal, а внутренний LazyLayoutItemAnimator, который реально управляет анимациями, недоступен снаружи. Скопировать код не выйдет — каст по типу вернёт null, и вся механика рассыпается. Форкать 5000+ строк LazyColumn — очевидно нет. Самое интересное — анимация удаления при кастомной реализации не работает в принципе: к моменту DisposableEffect.onDispose элемент уже удалён из дерева. А стандартный animateItem работает на уровне layout-фазы и может буквально «удерживать» элемент в дереве до окончания анимации. Итог у команды — оставили RecyclerView для списка, айтемы внутри на Compose. Костыль, но рабочий, пока Google не откроет нужные API. Issue уже создан, подписывайтесь если сталкивались. Я лично не сталкивался с таким кейсом в продакшне, но статья хорошо показывает, где у Compose сейчас реальные границы расширяемости и что без View пока никуда. #Android #Compose #AndroidDev #JetpackCompose #Анимация

⚙️ Dejavu — тесты на рекомпозицию в Compose Мэтт МакКенна выпустил библиотеку, которая превращает рекомпозиции в обычные test assertions. Dejavu решает проблемы постоянного мониторинга за рекомпозияцими без изменений в продакшн-коде — только Modifier.testTag(), который скорее всего у вас уже есть:
// Пример теста
@get:Rule
val composeTestRule = createRecompositionTrackingRule()

@Test
fun incrementCounter_onlyValueRecomposes() {
    composeTestRule.onNodeWithTag("inc_button").performClick()

    composeTestRule.onNodeWithTag("counter_value")
        .assertRecompositions(exactly = 1)

    composeTestRule.onNodeWithTag("counter_title")
        .assertStable() // ноль рекомпозиций
}
Когда тест падает, получаете структурированный отчёт:
UnexpectedRecompositionsError: testTag='product_header'
  Composable: demo.app.ui.ProductHeader (ProductList.kt:29)
  Expected: exactly 0 recomposition(s)
  Actual: 1 recomposition(s)

  All tracked composables:
    ProductListScreen = 1
    ProductHeader     = 1  <-- FAILED
    ProductItem       = 1

  Recomposition timeline:
    #1 at +0ms — param slots changed: [1] | parent: ProductListScreen

  Possible cause:
    1 state change(s) of type Int
    Parameter/parent change detected (dirty bits set)
Видно какой composable, сколько раз рекомпозировался и почему. Под капотом используется CompositionTracer API из compose-runtime 1.2.0, никаких Gradle-плагинов и байткод-манипуляций. Запускается как instrumented тест. 🐱 Dejavu Github #Android #AndroidDev #Compose #JetpackCompose

👨🏻‍💻 Вы пишете тесты, потому что «так надо» Покрываете несколько функций, проверяете очевидные кейсы, ставите галочку. Тес
👨🏻‍💻 Вы пишете тесты, потому что «так надо» Покрываете несколько функций, проверяете очевидные кейсы, ставите галочку. Тесты проходят - но это формальность. Они не помогают находить баги, не улучшают код, не дают уверенности в изменениях. Проблема не в тестах. Проблема в том, как вы их пишете. 🗓 12 марта в 20:00 по МСК — открытый вебинар «Профессиональные Unit-тесты (модульные тесты) в Android: как тесты улучшают код» Разберём, что отличает хороший Модульный тест от теста «для галочки» и почему тестируемость — это не про процент покрытия, а про архитектуру. Что рассмотрим: ➡️как писать Модульные тесты (unit-тесты) для ViewModel, репозиториев и сценариев использования (use case'ов) ➡️как требования к тестируемости влияют на качество кода ➡️что делает тест действительно полезным ❗️ После урока вы поймёте, как тесты становятся инструментом улучшения архитектуры, а не обязательной формальностью. ➡️ Регистрация по ссылке. Для участников скидка на предстоящий курс. Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576, www.otus.ru

Repost from Android Broadcast
🤯 Конец Android View ближе чем кажется - в будущей версии Android Studio убирают поддержку превью для Custom View. Источник
🤯 Конец Android View ближе чем кажется - в будущей версии Android Studio убирают поддержку превью для Custom View. Источник - сайт Android Developers #Android #AndroidDev #Compose #AndroidStudio

Repost from Android Broadcast
⚙️ Compose Remote уже в Альфа. Шаг за шагом выходят версии и BDUI на Compose уже в пути! Подробнее писал тут #Android #Compos
⚙️ Compose Remote уже в Альфа. Шаг за шагом выходят версии и BDUI на Compose уже в пути! Подробнее писал тут #Android #Compose #BDUI #SDUI

#see Compose Multiplatform на ОС Аврора Демонстрация сборки приложения Compose Multiplatform на эмулятор ОС Аврора. ▶️ Rutube

Repost from Aurora Developers
🔥 Compose Multiplatform доступен на ОС Аврора! Отличные новости для Kotlin-сообщества и всех, кто интересуется российской мо
🔥 Compose Multiplatform доступен на ОС Аврора! Отличные новости для Kotlin-сообщества и всех, кто интересуется российской мобильной разработкой. Теперь вы можете создавать приложения для ОС Аврора, используя современный и привычный инструментарий — Kotlin и Compose Multiplatform. Что опубликовано: В репозитории на mos.hub выложены компоненты, необходимые для разработки: - Проекты Compose Multiplatform для сборки. - Готовые артефакты в репозитории Aurora Maven. - Плагин для Gradle, упрощающий сборку приложений. Что дальше? В планах — расширение поддержки платформы и стабилизация проекта. А уже сейчас в документации вы найдете подробное руководство по началу работы, а также два демонстрационных приложения, которые помогут быстро разобраться с особенностями платформы. 👉 Документация Хотите помочь проекту стать лучше? Присоединяйтесь к разработке на mos.hub! Мы открыты к вашему коду и экспертизе. Только #ВМЕСТЕ мы сможем сделать инструмент максимально полезным для всего сообщества. 👉 Репозиторий

⚙️ Compose Stability Analyzer 0.7.0 🔥 Вышел крупный релиз плагина для Android Studio. Две главные фичи: 👉 Recomposition Cascade Visualizer Правый клик на любой @Composable → "Analyze Recomposition Cascade" — и получаешь дерево всех downstream-компонентов, которые будут перерисованы. Для каждого показывается статус (skippable / non-skippable), общая статистика и максимальная глубина. Двойной клик по узлу — переход к исходнику. Работает с защитой от циклов и ограничением глубины до 10 уровней. 👉 Live Recomposition Heatmap Прямо в редакторе, над каждой composable-функцией, в реальном времени отображается количество рекомпозиций с подключённого устройства через ADB. Цветовая индикация: 🟢 < 10 — всё ок 🟡 10–50 — стоит присмотреться 🔴 50+ — проблема Данные читаются из TraceRecomposition событий logcat. Поддерживается несколько устройств одновременно. #Compose #Performance

👨🏻‍💻RecyclerView — один из тех мест, где качество кода видно сразу. Если в адаптере появляются разные элементы, свайпы и п
👨🏻‍💻RecyclerView — один из тех мест, где качество кода видно сразу. Если в адаптере появляются разные элементы, свайпы и перетаскивание, важно не “допилить”, а собрать правильно. 🗓 25 февраля в 20:00 по МСК — открытый вебинар «Работаем со списками как профессионалы». Разберём: 🟢разнородные элементы и ItemViewType 🟢 drag-and-drop и swipe-to-dismiss 🟢 принципы чистого и поддерживаемого кода для сложных адаптеров ❗️ Участникам — скидка на курс "Android Developer. Professional" 🔜 Регистрация по ссылке Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576

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

Интенсив по мобильной разработке в Сочи от Яндекса и Сириуса Регистрация до 18.02, интенсив 30.03–10.04 ⏺Где: Университет Сир
Интенсив по мобильной разработке в Сочи от Яндекса и Сириуса Регистрация до 18.02, интенсив 30.03–10.04 ⏺Где: Университет Сириус, Сочи ⏺Что будет: Работа с AI, изучение iOS или Flutter, возможность попасть в Яндекс ⏺Условия: Бесплатно (перелёт, проживание, питание), экскурсии, нетворкинг ⏺Требуется: знание Swift/Dart, умение решать алгоритмические задачи, опыт в мобильной разработке Более половины выпускников получают предложение о стажировке или работе в Яндексе Подробности и регистрация: https://yandex.ru/yaintern/siriusmobile #реклама

⚙️ Пример реализации подобной анимации в Compose.Весь подход выделили в библиотек #Compose #Анимация

Repost 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) } }
        }
    )
}
Выглядит неплохо, посмотрим, что будет дальше. Детали тут.

Монолит: не пережиток, а рабочая лошадка 🐎 Пока в твиттере спорят про микросервисы, в проде по‑прежнему крутятся монолиты —
Монолит: не пережиток, а рабочая лошадка 🐎 Пока в твиттере спорят про микросервисы, в проде по‑прежнему крутятся монолиты — там, где важны: 🔵 быстрый онбординг и разработка 🔵 прозрачная бизнес‑логика 🔵 предсказуемое сопровождение и отладка На открытом уроке OTUS разберём на практике: 👉 разработку монолитного приложения на Spring + Kotlin 👉 плюсы и минусы монолитной архитектуры 👉 как связать транспортные модели и собрать приложение 👉 как упаковать сервис в Docker и подготовить к запуску 💡 Урок для backend‑разработчиков, которые хотят уверенно чувствовать себя в Spring и собирать production‑готовые сервисы. 🗓 4 февраля, 19:00 МСКРегистрация открыта Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576

⚙️ 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

🔥 Compose Hot Reload 1.0.0 — стабильный релиз! JetBrains выпустили стабильную версию Compose Hot Reload, и это действительно
🔥 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