UI Components
https://developer.android.com/develop/ui/compose/layouts/flow |
https://developer.android.com/develop/ui/compose/layouts/pager |
https://developer.android.com/develop/ui/compose/layouts/constraintlayout |
Text
Отображает текст на экране.
Text(text = "Hello, Compose!")
Текст фиксированной высоты c расположением center_vertical
Text(
text = stringResource(R.string.text),
modifier = Modifier
.constrainAs(anyText) {
width = Dimension.wrapContent
height = Dimension.value(48.dp)
start.linkTo(parent.start)
top.linkTo(parent.top)
}
.wrapContentHeight(Alignment.CenterVertically)
)
Enforcing constraints. Текст сокращается в зависимости от контента рядом
Row {
Text(
text = "Text",
overflow = TextOverflow.Ellipsis,
maxLines = 1,
modifier = Modifier.weight(weight = 1F, fill = false)
)
Icon(
painter = painterResource(R.drawable.ic_icon),
contentDescription = null
)
}
BasicText
Базовый компонент для отображения текста.
BasicText("Hello, World!")
ClickableText
Текст, который реагирует на клики.
ClickableText(
text = AnnotatedString("Click me"),
onClick = { offset -> /* handle click */ }
)
Button
Кнопка, которая выполняет действие при нажатии.
Button(onClick = { /* Действие при нажатии */ }) {
Text("Click Me")
}
TextButton
Кнопка с текстом, без фона и границ.
TextButton(onClick = { /* Handle click */ }) {
Text("Click Me")
}
OutlinedButton
Кнопка с контуром вместо заполнения.
OutlinedButton(onClick = { /* Действие */ }) {
Text("Outlined Button")
}
IconButton
Кнопка, которая содержит только иконку.
IconButton(onClick = { /* Действие */ }) {
Icon(Icons.Default.Favorite, contentDescription = "Like")
}
FilledIconButton
Кнопка с иконкой и заполненным фоном.
FilledIconButton(onClick = { /* Handle click */ }) {
Icon(Icons.Filled.Favorite, contentDescription = null)
}
FilledTonalIconButton
Кнопка с иконкой и менее ярким фоном.
FilledTonalIconButton(onClick = { /* Handle click */ }) {
Icon(Icons.Filled.Favorite, contentDescription = null)
}
OutlinedIconButton
Кнопка с иконкой и обводкой.
OutlinedIconButton(onClick = { /* Handle click */ }) {
Icon(Icons.Filled.Favorite, contentDescription = null)
}
CircleIconButton
Круглая кнопка с иконкой.
CircleIconButton(onClick = { /* Handle click */ }) {
Icon(Icons.Filled.Favorite, contentDescription = null)
}
ElevatedButton
Кнопка с тенью.
ElevatedButton(onClick = { /* Handle click */ }) {
Text("Click Me")
}
FilledButton
Кнопка с заполненным фоном.
FilledButton(onClick = { /* Handle click */ }) {
Text("Click Me")
}
FilledTonalButton
Кнопка с заполненным, но менее ярким фоном.
FilledTonalButton(onClick = { /* Handle click */ }) {
Text("Click Me")
}
InlineSliderButton
Кнопка для управления значениями ползунка.
InlineSliderButton(onClick = { /* Handle click */ }) {
Text("Slider Button")
}
IconToggleButton
Кнопка-переключатель с иконкой.
IconToggleButton(checked = false, onCheckedChange = { /* Handle check change */ }) {
Icon(Icons.Filled.Favorite, contentDescription = null)
}
FilledIconToggleButton
Кнопка-переключатель с заполненным фоном.
FilledIconToggleButton(checked = false, onCheckedChange = { /* Handle check change */ }) {
Icon(Icons.Filled.Favorite, contentDescription = null)
}
FilledTonalIconToggleButton
Кнопка-переключатель с менее ярким фоном.
FilledTonalIconToggleButton(checked = false, onCheckedChange = { /* Handle check change */ }) {
Icon(Icons.Filled.Favorite, contentDescription = null)
}
OutlinedIconToggleButton
Кнопка-переключатель с обводкой.
OutlinedIconToggleButton(checked = false, onCheckedChange = { /* Handle check change */ }) {
Icon(Icons.Filled.Favorite, contentDescription = null)
}
Image
Отображает изображение.
Image(
painter = painterResource(id = R.drawable.image),
contentDescription = "Example Image"
)
Icon
Отображает иконку, например из набора Material Icons.
Icon(imageVector = Icons.Default.Home, contentDescription = "Home Icon")
TextField
Поле для ввода текста.
var text by remember { mutableStateOf("") }
TextField(
value = text,
onValueChange = { text = it },
label = { Text("Enter text") }
)
Floating Suffix
internal class SuffixTransformation(
private val suffix: String
): VisualTransformation {
override fun filter(text: AnnotatedString): TransformedText {
val result = text + AnnotatedString(
text = suffix
)
val textWithSuffixMapping = object: OffsetMapping {
override fun originalToTransformed(offset: Int): Int {
return offset
}
override fun transformedToOriginal(offset: Int): Int {
if (text.isEmpty()) return 0
if (offset >= text.length) return text.length
return offset
}
}
return TransformedText(result, textWithSuffixMapping )
}
}
TextField(
value = text,
onValueChange = { value: String ->
text = value
},
visualTransformation = if (text.isNotEmpty()) SuffixTransformation(suffix = "₽") else VisualTransformation.None
)
Password Field
var password by remember { mutableStateOf("") }
var passwordVisible by rememberSaveable { mutableStateOf(false) }
TextField(
value = titleText,
onValueChange = { value: String ->
password = value
},
trailingIcon = {
AnimatedVisibility(
visible = password.isNotEmpty()
) {
IconButton(
onClick = {
passwordVisible = !passwordVisible
}
) {
Icon(
imageVector = if (passwordVisible) MoviesIcons.Visibility else MoviesIcons.VisibilityOff,
contentDescription = null
)
}
}
},
visualTransformation = if (passwordVisible) VisualTransformation.None else PasswordVisualTransformation()
)
OutlinedTextField
Текстовое поле с обводкой.
var text by remember { mutableStateOf("") }
OutlinedTextField(
value = text,
onValueChange = { text = it },
label = { Text("Enter text") }
)
Checkbox
Компонент для выбора “включено/выключено”.
var checked by remember { mutableStateOf(false) }
Checkbox(
checked = checked,
onCheckedChange = { checked = it }
)
Switch
Переключатель для изменения состояния (аналог чекбокса, но в стиле тумблера).
var switched by remember { mutableStateOf(false) }
Switch(
checked = switched,
onCheckedChange = { switched = it }
)
Slider
Ползунок для выбора значения.
var sliderValue by remember { mutableStateOf(0f) }
Slider(
value = sliderValue,
onValueChange = { sliderValue = it }
)
RangeSlider
Ползунок с диапазоном значений.
var range by remember { mutableStateOf(0f..100f) }
RangeSlider(values = range, onValueChange = { range = it })
InlineSlider
Ползунок для выбора значений.
InlineSlider(value = 0.5f, onValueChange = { /* Handle change */ })
RadioButton
Радио-кнопка для выбора одного элемента из группы.
var selected by remember { mutableStateOf(false) }
RadioButton(
selected = selected,
onClick = { selected = !selected }
)
RadioGroup
Группа радиокнопок для выбора одного варианта.
Column {
RadioButton(selected = true, onClick = { /* Действие */ })
RadioButton(selected = false, onClick = { /* Действие */ })
}
Divider
Линия-разделитель между элементами.
Divider()
Spacer
Невидимый элемент, используемый для создания отступов между элементами.
Spacer(modifier = Modifier.height(16.dp))
CircularProgressIndicator
Индикатор выполнения в виде круга.
CircularProgressIndicator()
LinearProgressIndicator
Индикатор выполнения в виде полосы.
LinearProgressIndicator()
Card
Карточка с закругленными углами.
Card(elevation = 4.dp) {
Text("This is a card")
}
ElevatedCard
Карточка с тенью.
ElevatedCard {
Text("Elevated Card")
}
OutlinedCard
Карточка с обводкой.
OutlinedCard {
Text("Outlined Card")
}
AppCard
Карточка для отображения информации.
AppCard {
Text("App Card")
}
TitleCard
Карточка с заголовком.
TitleCard {
Text("Title Card")
}
ClassicCard
Классическая карточка.
ClassicCard {
Text("Classic Card")
}
WideClassicCard
Широкая классическая карточка.
WideClassicCard {
Text("Wide Classic Card")
}
CompactCard
Компактная карточка.
CompactCard {
Text("Compact Card")
}
SwipeToRevealCard
Карточка с функцией свайпа для раскрытия.
SwipeToRevealCard(
swipeThreshold = 100.dp,
onSwipe = { /* Handle swipe action */ }
) {
Text("Swipe Me")
}
FloatingActionButton (FAB)
Плавающая кнопка для важных действий.
FloatingActionButton(onClick = { /* Действие */ }) {
Icon(Icons.Default.Add, contentDescription = "Add")
}
SmallFloatingActionButton
Небольшая плавающая кнопка действия.
SmallFloatingActionButton(onClick = { /* Handle click */ }) {
Icon(Icons.Filled.Add, contentDescription = null)
}
LargeFloatingActionButton
Большая плавающая кнопка действия.
LargeFloatingActionButton(onClick = { /* Handle click */ }) {
Icon(Icons.Filled.Add, contentDescription = null)
}
ExtendedFloatingActionButton
Расширенная версия FloatingActionButton с текстом.
ExtendedFloatingActionButton(
text = { Text("Action") },
onClick = { /* Handle click */ }
)
TopAppBar
Компонент для верхней панели приложения.
TopAppBar(title = { Text("App Title") })
BottomAppBar
Нижняя панель приложения, часто используется с FAB.
BottomAppBar {
Text("Bottom App Bar")
}
BottomNavigation
Нижняя панель для навигации по приложению.
BottomNavigation {
BottomNavigationItem(icon = { Icon(Icons.Default.Home, contentDescription = null) }, selected = false, onClick = { /* Действие */ })
}
NavigationDrawer
Панель навигации сбоку, используемая для отображения меню.
ModalDrawer(
drawerContent = { Text("Drawer content") },
content = { Text("Main content") }
)
TabRow
Компонент для создания вкладок.
var selectedTabIndex by remember { mutableStateOf(0) }
TabRow(selectedTabIndex = selectedTabIndex) {
Tab(selected = selectedTabIndex == 0, onClick = { selectedTabIndex = 0 }) {
Text("Tab 1")
}
Tab(selected = selectedTabIndex == 1, onClick = { selectedTabIndex = 1 }) {
Text("Tab 2")
}
}
ScrollableTabRow
Панель с вкладками, которая поддерживает горизонтальную прокрутку.
ScrollableTabRow(selectedTabIndex = 0) {
Text("Tab 1")
Text("Tab 2")
}
PrimaryTabRow
Панель вкладок без прокрутки.
PrimaryTabRow(selectedTabIndex = 0) {
Tab(selected = true, onClick = { /* Handle click */ }) {
Text("Tab 1")
}
}
PrimaryScrollableTabRow
Панель вкладок с возможностью прокрутки.
PrimaryScrollableTabRow(selectedTabIndex = 0) {
Tab(selected = true, onClick = { /* Handle click */ }) {
Text("Tab 1")
}
}
SecondaryTabRow
Дополнительная панель вкладок без прокрутки.
SecondaryTabRow(selectedTabIndex = 0) {
Tab(selected = true, onClick = { /* Handle click */ }) {
Text("Tab 1")
}
}
SecondaryScrollableTabRow
Дополнительная панель вкладок с прокруткой.
SecondaryScrollableTabRow(selectedTabIndex = 0) {
Tab(selected = true, onClick = { /* Handle click */ }) {
Text("Tab 1")
}
}
Surface
Контейнер для отображения компонентов с поддержкой стилей, таких как elevation и фоны.
Surface(elevation = 4.dp, shape = RoundedCornerShape(8.dp)) {
Text("Surface with elevation")
}
Chip
Элемент для отображения маленьких блоков текста или иконок.
Chip(onClick = { /* Handle click */ }) {
Text("Chip")
}
AssistChip
Чип для дополнительных действий или помощи.
AssistChip(onClick = { /* Handle click */ }) {
Text("Assist Chip")
}
FilterChip
Чип для фильтрации данных.
FilterChip(onClick = { /* Handle click */ }) {
Text("Filter Chip")
}
InputChip
Чип для ввода данных.
InputChip(onClick = { /* Handle click */ }) {
Text("Input Chip")
}
SuggestionChip
Чип для предложений.
SuggestionChip(onClick = { /* Handle click */ }) {
Text("Suggestion Chip")
}
Badge
Отображает небольшой значок или метку.
Badge {
Text("New")
}
Carousel
Компонент для прокрутки элементов в виде карусели.
Carousel(
items = listOf("Item 1", "Item 2", "Item 3"),
content = { item -> Text(item) }
)
DatePicker
Выбор даты.
DatePickerDialog(
onDateSelected = { /* Handle date selection */ }
)
TimePicker
Выбор времени.
TimePickerDialog(
onTimeSelected = { /* Handle time selection */ }
)
NavigationRail
Боковая панель для навигации
NavigationRail {
NavigationRailItem(
icon = { Icon(Icons.Filled.Home, contentDescription = null) },
label = { Text("Home") },
selected = false,
onClick = { /* Handle click */ }
)
}
SearchBar
Панель для поиска.
SearchBar(onSearch = { /* Handle search */ }) {
Text("Search")
}
Layouts
Column
Размещает элементы вертикально.
Column {
Text("Item 1")
Text("Item 2")
}
Row
Размещает элементы горизонтально.
Row {
Text("Item 1")
Text("Item 2")
}
Box
Контейнер, который накладывает элементы друг на друга.
Box {
Text("First item")
Text("Second item")
}
FlowRow
Располагает элементы в строках, которые автоматически переносятся на новую строку при переполнении ширины.
FlowRow {
repeat(10) { index ->
Text("Item $index")
}
}
FlowColumn
Аналог FlowRow, но с вертикальной ориентацией.
FlowColumn {
repeat(10) { index ->
Text("Item $index")
}
}
ContextualFlowRow
Компонент для размещения элементов в строку с поддержкой контекстного управления.
ContextualFlowRow(
context = /* Provide context */,
modifier = Modifier.padding(8.dp)
) {
items(/* List of items */) { item ->
Text(text = item)
}
}
ContextualFlowColumn
Компонент для размещения элементов в колонку с поддержкой контекстного управления.
ContextualFlowColumn(
context = /* Provide context */,
modifier = Modifier.padding(8.dp)
) {
items(/* List of items */) { item ->
Text(text = item)
}
}
LazyColumn
Вертикальный список с возможностью прокрутки (аналог RecyclerView).
LazyColumn {
items(10) { index ->
Text("Item $index")
}
}
LazyRow
Горизонтальный список с возможностью прокрутки.
LazyRow {
items(10) { index ->
Text("Item $index")
}
}
LazyVerticalGrid
Отображает элементы в виде сетки с возможностью прокрутки.
LazyVerticalGrid(cells = GridCells.Fixed(2)) {
items(10) { index ->
Text("Item $index")
}
}
Добавить Header
LazyVerticalGrid(
columns = GridCells.Fixed(count = 8),
) {
item(
span = { GridItemSpan(maxLineSpan) }
) {
Text(
text = "Header",
)
}
items(list) { item ->
...
}
}
LazyStaggeredGrid
Прокручиваемая сетка с переменной высотой элементов.
LazyStaggeredGrid(columns = StaggeredGridCells.Fixed(2)) {
items(100) { index ->
Text("Item #$index")
}
}
Scaffold
Структурный компонент для создания макетов с верхними панелями, навигацией, FAB и контентом.
Scaffold(
topBar = { TopAppBar(title = { Text("AppBar Title") }) },
floatingActionButton = { FloatingActionButton(onClick = { /* Действие */ }) { Text("+") } },
content = { Text("Hello, Scaffold!") }
)
BackdropScaffold
Специальный макет, где один элемент (фоновый) может быть скрыт или отображен позади другого контента.
BackdropScaffold(
appBar = { TopAppBar(title = { Text("Backdrop Scaffold") }) },
backLayerContent = { Text("Back Layer") },
frontLayerContent = { Text("Front Layer") }
)
HorizontalPager
Пейджер для горизонтальной прокрутки страниц.
HorizontalPager(count = 5) { page ->
Text("Page $page")
}
VerticalPager
Пейджер для вертикальной прокрутки страниц (как и HorizontalPager, но с вертикальной ориентацией).
VerticalPager(count = 5) { page ->
Text("Page $page")
}
ConstraintLayout
Chain
ConstraintLayout {
val (icon, text) = createRefs()
createHorizontalChain(icon, text, chainStyle = ChainStyle.Packed)
Icon(
modifier = Modifier
.constrainAs(icon) {
width = Dimension.wrapContent
height = Dimension.wrapContent
start.linkTo(parent.start)
top.linkTo(parent.top)
end.linkTo(text.start)
bottom.linkTo(parent.bottom)
}
)
Text(
modifier = Modifier
.constrainAs(text) {
width = Dimension.wrapContent
height = Dimension.wrapContent
start.linkTo(icon.end)
top.linkTo(parent.top)
end.linkTo(parent.end)
bottom.linkTo(parent.bottom)
}
.padding(start = 8.dp)
)
}
Flow
FlowRow {
Text(
text = "Text 1",
modifier = Modifier.padding(end = 8.dp)
)
Text(
text = "Text 2",
modifier = Modifier.padding(end = 8.dp)
)
Text(
text = "Text 3"
)
}
Adaptive Layouts
BoxWithConstraints
Контейнер, который может изменять свою компоновку в зависимости от доступных размеров.
BoxWithConstraints { constraints ->
if (constraints.maxWidth < 600.dp) {
// Компоновка для узкого экрана
Column {
Text("Узкий экран")
// Дополнительные элементы
}
} else {
// Компоновка для широкого экрана
Row {
Text("Широкий экран")
// Дополнительные элементы
}
}
}
NavigationSuiteScaffold
Интегрируется с NavController для навигации между экранами и поддерживает стандартные элементы интерфейса, такие как TopAppBar и BottomNavigation.
val navController = rememberNavController()
NavigationSuiteScaffold(
navController = navController,
topBar = { TopAppBar(title = { Text("MyApp") }) },
bottomBar = {
BottomNavigation {
// Навигационные элементы
}
}
) { innerPadding ->
NavHost(
navController = navController,
startDestination = "home",
Modifier.padding(innerPadding)
) {
composable("home") { HomeScreen(navController) }
composable("details/{itemId}") { backStackEntry ->
DetailsScreen(navController, itemId = backStackEntry.arguments?.getString("itemId"))
}
}
}
ListDetailPaneScaffold
Управляет макетом, разделяя экран на две основные панели: список и детальную информацию.
ListDetailPaneScaffold(
listContent = { innerPadding ->
LazyColumn(
Modifier.padding(innerPadding)
) {
items(items = itemsList) { item ->
ListItem(
modifier = Modifier.clickable { /* Отображаем детальную информацию */ },
text = { Text(item.title) }
)
}
}
},
detailContent = { innerPadding ->
// Отображение детальной информации о выбранном элементе
DetailView(
modifier = Modifier.padding(innerPadding),
item = selectedItem
)
}
)
SupportingPaneScaffold
Создает интерфейсы с двумя панелями — основной и вспомогательной.
SupportingPaneScaffold(
mainContent = { innerPadding ->
// Основной контент
Column(
modifier = Modifier.padding(innerPadding)
) {
Text("Основной контент")
// Дополнительные элементы
}
},
supportingContent = { innerPadding ->
// Вспомогательный контент
Column(
modifier = Modifier.padding(innerPadding)
) {
Text("Вспомогательный контент")
// Дополнительные элементы
}
}
)
NavigableListDetailPaneScaffold
Обеспечивает структуру для приложений с навигацией между списком элементов и детализированным просмотром выбранного элемента.
val navigator = rememberListDetailPaneScaffoldNavigator<Any>()
NavigableListDetailPaneScaffold(
navigator = navigator,
listPane = {}, // панель списка
detailPane = {}, // Панель деталей
)
Диалоги
Popup
Всплывающее окно для отображения временного контента поверх текущего интерфейса.
Popup {
Text("Popup content")
}
DropdownMenu
Выпадающее меню для выбора из нескольких вариантов.
DropdownMenu(expanded = expanded, onDismissRequest = { expanded = false }) {
DropdownMenuItem(onClick = { /* Действие */ }) {
Text("Option 1")
}
DropdownMenuItem(onClick = { /* Действие */ }) {
Text("Option 2")
}
}
AlertDialog
Диалоговое окно для отображения сообщений и взаимодействия с пользователем.
AlertDialog(
onDismissRequest = { /* Закрыть диалог */ },
title = { Text("Title") },
text = { Text("Dialog content goes here") },
confirmButton = {
Button(onClick = { /* Действие подтверждения */ }) {
Text("OK")
}
}
)
ModalBottomSheet
Модальное нижнее окно, которое появляется снизу экрана и блокирует взаимодействие с остальными элементами, пока не будет закрыто.
@Composable
fun ExampleModalBottomSheet() {
val sheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden)
val scope = rememberCoroutineScope()
ModalBottomSheetLayout(
sheetState = sheetState,
sheetContent = {
Column(modifier = Modifier.padding(16.dp)) {
Text("Title")
Button(onClick = { scope.launch { sheetState.hide() } }) {
Text("Close Bottom Sheet")
}
}
}
) {
Button(onClick = { scope.launch { sheetState.show() } }) {
Text("Open Bottom Sheet")
}
}
}
ModalBottomSheetLayout
Открывает листовое окно снизу для отображения контента.
ModalBottomSheetLayout(
sheetContent = { Text("Bottom Sheet Content") },
content = { Text("Main Content") }
)
TooltipBox
Контейнер для отображения подсказок.
TooltipBox(tooltip = { Text("Tooltip") }) {
Text("Hover me")
}
BasicTooltipBox
Простой контейнер для подсказок.
BasicTooltipBox(tooltip = { Text("Tooltip") }) {
Text("Hover me")
}
Canvas
Компонент для рисования 2D-графики.
Canvas(modifier = Modifier.size(100.dp)) {
drawCircle(Color.Red)
}
Gradient
val gradientBrush = Brush.horizontalGradient(listOf(MaterialTheme.colorScheme.red, MaterialTheme.colorScheme.blue))
Canvas(
modifier = Modifier,
onDraw = { drawRect(gradientBrush) }
)
AnimatedVisibility
Компонент для плавного отображения или скрытия контента.
var visible by remember { mutableStateOf(true) }
AnimatedVisibility(visible = visible) {
Text("Animated Content")
}
Crossfade
Компонент для анимации плавной смены контента.
Crossfade(targetState = selectedScreen) { screen ->
when (screen) {
Screen.Home -> HomeScreen()
Screen.Profile -> ProfileScreen()
}
}
Placeholder
Контейнер для отображения placeholder-заполнителя (например, для состояния загрузки).
Placeholder(visible = true, modifier = Modifier.size(100.dp))
AndroidView
Позволяет встроить классический Android View в Compose.
AndroidView(factory = { context -> TextView(context).apply { text = "Hello, View!" } })
Snackbar
Всплывающее уведомление в нижней части экрана.
class MyViewModel: ViewModel() {
var isSnackbarShowed: Boolean by mutableStateOf(false)
fun showSnackbarMessage() {
isSnackbarShowed = true
}
fun hideSnackbarMessage() {
isSnackbarShowed = false
}
}
@Composable
fun Composable(
viewModel: MyViewModel = hiltViewModel()
) {
val scope = rememberCoroutineScope()
val snackbarHostState = remember { SnackbarHostState() }
val isSnackbarShowed = viewModel.isSnackbarShowed
val onShowSnackbar: (String) -> Unit = { message ->
scope.launch {
snackbarHostState.currentSnackbarData?.dismiss()
val snackbarResult = snackbarHostState.showSnackbar(
message = message,
duration = SnackbarDuration.Short
)
if (snackbarResult == SnackbarResult.Dismissed) {
viewModel.hideSnackbarMessage()
}
}
}
if (isSnackbarShowed) {
onShowSnackbar(stringResource(R.string.message))
}
}
Scaffold(
snackbarHost = {
SnackbarHost(
hostState = snackbarHostState
)
}
) { innerPadding ->
}
Get Widget Height
val density: Density = LocalDensity.current
var columnHeightPx: Float by remember { mutableStateOf(0F) }
var columnHeightDp: Dp by remember { mutableStateOf(0.dp) }
Box(
modifier = Modifier
.onGloballyPositioned { layoutCoordinates ->
columnHeightPx = layoutCoordinates.size.height.toFloat()
columnHeightDp = with(density) { layoutCoordinates.size.height.toDp() }
}
)
Compose UI Components. Вопросы на собесе
- Лагает список LazyList. Что будем делать?
- Как сделать отступ у Box?
- Какие основные Layout есть в Compose?
Box
Row
Column