Skip to content

Android Jetpack Compose

Michael Bely edited this page Apr 29, 2024 · 33 revisions

Important

ВНИМАНИЕ!
ЭТОТ РАЗДЕЛ БОЛЬШЕ НЕ ПОДДЕРЖИВАЕТСЯ!
РОАДМАП ПЕРЕЕХАЛ В NOTION

Jetpack Compose Catalog

Compose
Cовременный декларативный набор инструментов для создания Android UI. Упрощает проектирование, связанное с созданием и обновлением UI. Сразу в compile time отображается на preview, compose берет на себя отрисовку компонентов, если они поменялись

Accompanist
Группа библиотек, дополняющие Jetpack Compose функциями, которые требуются разработчикам, но пока недоступны

CompositionLocal

StateFlow

collectAsStateWithLifecycle()
Предоставляет последнее значение из StateFlow с учетом жизненного цикла.

Modifier

Modifier
    .padding(0.dp)
    .fillMaxSize()
    .fillMaxWidth()
    .fillMaxHeight()
    .verticalScroll(rememberScrollState())
    .clip(MaterialTheme.shapes.small)
    .background(MaterialTheme.colorScheme.background)
    .statusBarsPadding()
    .clickable {}
    .width(0.dp)
    .height(0.dp)
    .wrapContentHeight()
    .pointerInput(Unit) { detectTapGestures { offset -> } }

Scaffold

Scaffold(
    modifier = Modifier,
    scaffoldState = ,
    topBar = {},
    bottomBar = {},
    snackbarHost = {},
    floatingActionButton = {},
    floatingActionButtonPosition = FabPosition.End,
    isFloatingActionButtonDocked = false,
    backgroundColor = MaterialTheme.colorScheme.background,
    contentColor = MaterialTheme.colorScheme.content
) { paddingValues ->
    
}

TopAppBar

TopAppBar(
    title = {},
    navigationIcon = {
        IconButton(
            onClick = {
                onNavigationIconClick()
            }
        ) {
            Icon(
                painter = painterResource(R.drawable.icon),
                contentDescription = null,
            )
        }
    },
    actions = {},
    backgroundColor = MaterialTheme.colorScheme.background,
    contentColor = MaterialTheme.colorScheme.content,
    elevation = 0.dp
)

Box

Box(
    modifier = Modifier,
    contentAlignment = Alignment.Center,
    propagateMinConstraints = false
) {
    
}

Row

Row(
    modifier = Modifier,
    horizontalArrangement = Arrangement.Start,
    verticalAlignment = Alignment.CenterVertically
) {
    
}

Column

Column(
    modifier = Modifier,
    verticalArrangement = Arrangement.Top,
    horizontalAlignment = Alignment.CenterHorizontally
) {
    
}

LazyColumn

LazyColumn(
    modifier = Modifier,
    state = rememberLazyListState(),
    contentPadding = PaddingValues(0.dp),
    reverseLayout = false,
    verticalArrangement = Arrangement.Top,
    horizontalAlignment = Alignment.Start,
    flingBehavior = ScrollableDefaults.flingBehavior(),
    userScrollEnabled = true
) {
    items()
}

Card

Card(
    modifier = Modifier,
    shape = ,
    backgroundColor = MaterialTheme.colors.background,
    contentColor = MaterialTheme.colors.content,
    border = ,
    elevation = 1.dp
) {
    
}

Text

Text(
    text = stringResource(R.string.text),
    modifier = Modifier,
    color = MaterialTheme.colorScheme.primary,
    fontSize = 16.sp,
    fontStyle = FontStyle.Normal,
    fontWeight = FontWeight.W400,
    fontFamily = FontFamily.Serif,
    textAlign = TextAlign.Center,
    overflow = TextOverflow.Ellipsis,
    maxLines = 1,
    style = MaterialTheme.typography.titleLarge
)

Текст фиксированной высоты + gravity center_vertical

Text(
    text = "Text",
    modifier = Modifier
        .constrainAs(anyText) {
            width = Dimension.wrapContent
            height = Dimension.value(48.dp)
            start.linkTo(parent.start)
            top.linkTo(parent.top)
        }
        .wrapContentHeight(Alignment.CenterVertically)
)

TextField

var titleText: String by remember { mutableStateOf("") }
val focusManager: FocusManager = LocalFocusManager.current
val focusRequester: FocusRequester = remember { FocusRequester() }
val keyboardController: SoftwareKeyboardController? = LocalSoftwareKeyboardController.current
var passwordVisible: Boolean by rememberSaveable { mutableStateOf(false) }

TextField(
    value = titleText,
    onValueChange = { value: String ->
        titleText = value.take(20)
    },
    modifier = Modifier
        .focusRequester(focusRequester),
    label = {
        Text(
            text = stringResource(R.string.label)
        )
    },
    trailingIcon = {
        AnimatedVisibility(
            visible = password.isNotEmpty(),
            enter = fadeIn(),
            exit = fadeOut()
        ) {
            IconButton(
                onClick = {
                    passwordVisible = !passwordVisible
                }
            ) {
                Icon(
                    imageVector = if (passwordVisible) MoviesIcons.Visibility else MoviesIcons.VisibilityOff,
                    contentDescription = null
                )
            }
        }
    },
    visualTransformation = if (passwordVisible) VisualTransformation.None else PasswordVisualTransformation(),
    keyboardOptions = KeyboardOptions(
        keyboardType = KeyboardType.Text,
        imeAction = ImeAction.Next
    ),
    keyboardActions = KeyboardActions(
        onDone = {
            focusManager.moveFocus(FocusDirection.Down)
            keyboardController?.hide()
        }
    ),
    maxLines = 1,
    colors = TextFieldDefaults.textFieldColors(
        textColor = MaterialTheme.colorScheme.primary,
        backgroundColor = MaterialTheme.colorScheme.primary,
        cursorColor = MaterialTheme.colorScheme.primary,
        focusedIndicatorColor = MaterialTheme.colorScheme.primary,
        unfocusedIndicatorColor = MaterialTheme.colorScheme.primary,
        focusedLabelColor = MaterialTheme.colorScheme.primary,
        unfocusedLabelColor = MaterialTheme.colorScheme.primary
    )
)

Icon

Icon(
   imageVector = Icons.Filled.Info,
   contentDescription = null,
   modifier = Modifier,
   tint = Color.Unspecified
)
Icon(
    painter = painterResource(R.drawable.icon),
    contentDescription = null,
    modifier = Modifier,
    tint = Color.Unspecified
)

Image

Image(
    imageVector = Icons.Filled.Info,
    contentDescription = null,
    modifier = Modifier,
    alignment = Alignment.Center,
    contentScale = ContentScale.Fit,
    colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.primary)
)
Image(
    painter = painterResource(R.drawable.icon),
    contentDescription = null,
    modifier = Modifier,
    alignment = Alignment.Center,
    contentScale = ContentScale.Fit,
    alpha = 1F,
    colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.primary)
)

AsyncImage

AsyncImage(
    model = ImageRequest.Builder(context)
        .data(imageUrl)
        .crossfade(true)
        .build(),
    contentDescription = null,
    modifier = Modifier
        .size(24.dp)
        .clip(CircleShape)
        .border(
            width = 1.dp,
            color = MaterialTheme.colorScheme.onPrimaryContainer,
            shape = CircleShape
        ),
    onState = { state -> },
    contentScale = ContentScale.Crop
)

Button

Button(
    onClick = {},
    modifier = Modifier,
    enabled = true,
    colors = ButtonDefaults.buttonColors(
        backgroundColor = MaterialTheme.colors.background,
        contentColor = MaterialTheme.colors.content,
        disabledBackgroundColor = MaterialTheme.colors.background,
        disabledContentColor = MaterialTheme.colors.content
    ),
    contentPadding = PaddingValues(
        horizontal = 0.dp,
        vertical = 0.dp
    )
) {
    Text(
        text = stringResource(R.string.text).uppercase(),            
    )
}
OutlinedButton(
    onClick = {},
    modifier = Modifier,
    enabled = true,
    elevation = ,
    shape = ,
    border = ,
    colors = ,
   contentPadding = ,
) {
    Text(
        text = stringResource(R.string.text)
    )
}
IconButton(
    onClick = {},
    modifier = Modifier,
    enabled = true
) {
    
}

Switch

Switch(
    checked = true,
    onCheckedChange = null,
    modifier = Modifier,
    enabled = true,
    colors = 
)

RadioButton

RadioButton(
    selected = true,
    onClick = {},
    colors = RadioButtonDefaults.colors(
        selectedColor = MaterialTheme.colorScheme.primary,
        unselectedColor = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.6F)
    ),
    modifier = Modifier                      
)

Slider

Slider(
    value = sliderPosition,
    onValueChange = { position ->
        sliderPosition = position
    },
    modifier = Modifier,
    valueRange = 0F..10000F,
    steps = 9,
    onValueChangeFinished = {
        onDelayChangeFinished(sliderPosition.roundToInt())
    },
    colors = SliderDefaults.colors(
        inactiveTrackColor = MaterialTheme.colorScheme.outline,
        inactiveTickColor = MaterialTheme.colorScheme.onPrimary
    )
)

CircularProgressIndicator

CircularProgressIndicator(
    modifier = Modifier,
    color = MaterialTheme.colorScheme.primary,
    strokeWidth = 1.dp
)

Divider

Divider(
    modifier = Modifier,
    color = MaterialTheme.colorScheme.divider,
    thickness = 1.dp,
    startIndent = 0.dp
)

AlertDialog

val dialog: MutableState<Boolean> = remember { mutableStateOf(false) }

AlertDialog(
    onDismissRequest = {
        dialog.value = false
    },
    confirmButton = {
        TextButton(
            onClick = {
                dialog.value = false
            },
        ) {
            Text(
                text = stringResource(R.string.cancel),
                color = MaterialTheme.colorScheme.primary,
                style = MaterialTheme.typography.labelLarge
            )
        }
    },
    icon = {
        Icon(
            painter = painterResource(R.drawable.icon),
            contentDescription = null
        )
    },
    title = {
        Text(
            text = stringResource(R.string.title),
            color = MaterialTheme.colorScheme.onSurface,
            style = MaterialTheme.typography.headlineSmall
        )
    },
    text = {},
    shape = RoundedCornerShape(28.dp),
    containerColor = MaterialTheme.colorScheme.surface,
    iconContentColor = MaterialTheme.colorScheme.secondary,
    titleContentColor = MaterialTheme.colorScheme.onSurface,
    properties = DialogProperties(
        dismissOnBackPress = true,
        dismissOnClickOutside = false
    )
)
val confirmDialog: MutableState<Boolean> = remember { mutableStateOf(false) }

if (confirmDialog.value) {
    AlertDialog(
        onDismissRequest = {
            confirmDialog.value = false
        },
    ) {
        Column(
            modifier = Modifier.background(MaterialTheme.colorScheme.dialogBackground)
        ) {
            Text(
                text = stringResource(R.string.text),
                modifier = Modifier.padding(16.dp)
            )

            Row(
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(end = 16.dp),
                horizontalArrangement = Arrangement.End
            ) {
                TextButton(
                    onClick = {
                        confirmDialog.value = false
                    },
                    textRes = R.string.button_positive
                )

                MainTextButton(
                    onClick = {
                        confirmDialog.value = false
                    },
                    textRes = R.string.button_negative
                )
            }
        }
    }
}

ConstraintLayout

ConstraintLayout {
    val (icon, text) = createRefs()

    Icon(
        painter = painterResource(R.drawable.ic_icon),
        contentDescription = null,
        modifier = Modifier
            .constrainAs(icon) {
                width = Dimension.value(16.dp)
                height = Dimension.value(16.dp)
                start.linkTo(parent.start, 16.dp)
                top.linkTo(parent.top)
                end.linkTo(parent.end, 16.dp)
                bottom.linkTo(parent.bottom)
            }
    )

    Text(
        text = "Text",
        modifier = Modifier
            .constrainAs(text) {
                width = Dimension.fillToConstraints
                height = Dimension.wrapContent
                start.linkTo(icon.end, 8.dp)
                top.linkTo(icon.top)
                end.linkTo(icon.end, 8.dp)
                bottom.lintTo(icon.bottom)
            }
        )
}

Enforcing constraints
Stackoverflow question

ConstraintLayout {
    val (row) = createRefs()
    
    Row(
        modifier = Modifier
            .constrainAs(row) {
                width = Dimension.fillToConstraints
                height = Dimension.wrapContent
                start.linkTo(parent.start, 8.dp)
                top.linkTo(parent.top)
                end.linkTo(parent.end, 8.dp)
                bottom.linkTo(parent.bottom)
            }
    ) {
        Text(
            text = "Text",
            overflow = TextOverflow.Ellipsis,
            maxLines = 1,
            modifier = Modifier
                .weight(
                    weight = 1F, 
                    fill = false
                )
        )

        Icon(
            painter = painterResource(R.drawable.ic_icon),
            contentDescription = null
        )
    }
}

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)
    )
}

AndroidView

AndroidView(
    factory = { context ->
        View(context)
    },
    modifier = Modifier
)

Get Element Height

val dencity: 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(dencity) { layoutCoordinates.size.height.toDp() }
        }
)
Clone this wiki locally