Все о Jetpack Compose и Compose Multiplatform
По сотрудничеству - @ab_manager
Post #972
2.24K
В Compose 1.13 появится полноценный Blur для контента через Modifier
#Compose
#Compose
CO @compose_broadcast
wasmJsBrowserDistribution отдельная задача (исходный код в issue) копирует статику, вычисляет SHA-256 по путям и содержимому и генерирует service-worker.js.index.html, JS, Wasm и Compose Resources кладутся в Cache Storagecache.addAll() падает, если не скачался хотя бы один файл из спискаForwarded from Aurora Developers


@Composable
fun UserProfileCard(user: User) {
Card(
modifier = Modifier
.fillMaxWidth()
// Attach highlighter
.recompositionHighlighter("UserProfileCard")
) {
Text(text = user.name)
}
}
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: так же можно подсунуть свою пересборку любой чужой библиотеки, у которой не хватает таргета.compose.runtime и compose.ui в проде из такого источника решение уровня «мы понимаем, на что подписались». Форк сейчас держит линию 1.12, но догонять апстрим ему придётся руками
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 }}")

@Composable вслепую: не знал, прошёл ли reload, что на экране, не упало ли. Теперь есть сервер с нормальными тулами. Поднимается таской, в .mcp.json достаточно hotMcpServer:{
"mcpServers": {
"compose-hot-reload": {
"command": "./gradlew",
"args": ["--no-daemon", "--quiet", "--console=plain", "hotMcpServer"]
}
}
}
Adaptive.value():val padding = Adaptive.value(
sm = 12.dp,
md = 16.dp,
lg = 20.dp,
tab = 24.dp,
desktop = 28.dp
)
Adaptive.layoutValue() (по текущей ширине, включая split-screen и resize) и Adaptive.onOrientationChange для портрета/ландшафта.mediaQuery — лямбда, которая смотрит на текущий UiMediaScope и возвращает boolean. Внутри можно проверять:windowWidth / windowHeightwindowPosture (Tabletop, Book, Flat)pointerPrecision (Fine → Coarse → Blunt → None)keyboardKind (Physical / Virtual / None)hasCamera / hasMicrophoneviewingDistance (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()
}
// Composable code
if (mediaQuery { windowPosture == UiMediaScope.Posture.Tabletop }) {
TabletopLayout()
} else {
FlatLayout()
}
@Composable
fun Card(title: String, count: Int) {
Div({ classes("card") }) {
H3 { Text(title) }
Span { Text(count.toString()) }
}
}
renderToString / renderToBytes, чтобы один раз прогнать composition и получить строку HTML. Без браузера, без DOM, без recomposition и effects (как классический SSR). Пока речь идет только про JVM тартег
reload — пересобрать и хотрелоудитьtake_screenshot — снять текущее окноget_semantic_tree — получить semantic tree (включая Dialog/Popup)click / type_text / scroll — симулировать вводget_logs — читать runtime-логи и ошибки.mcp.json):{
"mcpServers": {
"compose-hot-reload": {
"command": "./gradlew",
"args": [
"--no-daemon",
"--quiet",
"--console=plain",
"hotMcpServer"
]
}
}
}adaptive-navigation3. Теперь можно легко строить адаптивные Material-интерфейсы в связке с Navigation 3. Что внутри:ListDetailSceneStrategy + rememberListDetailSceneStrategy — канонический list-detail SupportingPaneSceneStrategy + rememberSupportingPaneSceneStrategy — supporting paneListDetailPaneScaffold и SupportingPaneScaffold. Теперь поддерживают margins и edge-to-edge.remember, ключи и связи между элементами. С момента первого релиза Slot Table был реализован на основе Gap Buffer — структуры, вдохновлённой текстовыми редакторами.IntArray с пустой областью (gap), которая перемещается к месту следующей записи. Его проблема что при удалении или переупорядочивании элементов gap должен переместиться к нужной позиции, копируя все элементы на своём пути.// ДО вызова setContent()
ComposeRuntimeFlags.isLinkBufferComposerEnabled = true
-assumevalues public class androidx.compose.runtime.ComposeRuntimeFlags {
static boolean isLinkBufferComposerEnabled return true;
}This post (sticker, poll or similar) has no web preview. Open 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")
}
}
credentialRequest. Passkeys, сохранённые учётные данные — прямо из поля ввода.KeyboardType: Date, Time, DateTime, SignedDecimal.BasicSecureTextField теперь по умолчанию использует TextObfuscationMode.System
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"))
}