Какие модификаторы и где нужно применить, чтобы все элементы помещались в альбомной ориентации?

import ...

data class ArtInfo(
    val artId: Int,
    val drawableRes: Int,
    val title: String,
    val author: String,
    val year: Int,
)

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContent {
            ComposeTheme {
                Surface(
                    modifier = Modifier
                        .statusBarsPadding()
                        .navigationBarsPadding()
                        .fillMaxSize(),
                    color = MaterialTheme.colorScheme.onPrimary
                ) {
                    ArtSpaceApp()
                }
            }
        }
    }
}

@Composable
fun ArtSpaceApp(modifier: Modifier = Modifier) {
    val arts = listOf(
        ...
    )

    var artState by remember {
        mutableStateOf(arts[0])
    }

    fun navigateToNextArt() {
        ...
    }

    fun navigateToPreviousArt() {
        ...
    }

    Column(
        modifier = modifier
            .fillMaxSize()
            .padding(horizontal = 24.dp),
        verticalArrangement = Arrangement.SpaceBetween,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Spacer(modifier = Modifier.height(20.dp))
        Art(imageResource = artState.drawableRes)
        Spacer(modifier = Modifier.height(60.dp))
        AboutArt(
            title = artState.title,
            author = artState.author,
            year = artState.year
        )
        NavigationArts(
            { navigateToNextArt() },
            { navigateToPreviousArt() }
        )
    }
}

@Composable
fun Art(@DrawableRes imageResource: Int) {
    Surface(
        modifier = Modifier
            .border(width = 32.dp, color = Color.White)
            .shadow(elevation = 16.dp),
        ) {
        Image(
            painter = painterResource(id = imageResource),
            contentDescription = null
        )
    }
}

@Composable
fun AboutArt(
    modifier: Modifier = Modifier
        .background(color = colorResource(id = R.color.background_about_art)),
    title: String,
    author: String,
    year: Int,
) {
    Surface(
        modifier = modifier
            .padding(16.dp)
    ) {
        Column(
            modifier = modifier
                .fillMaxWidth()
        ) {
            Text(
                text = title,
                fontSize = 24.sp,
                fontWeight = FontWeight.W300,
            )

            Row {
                Text(
                    text = author,
                    fontWeight = FontWeight.W800
                )

                Text(
                    text = " ($year)"
                )
            }
        }
    }
}

@Composable
fun NavigationArts(
    navigateToNext: () -> Unit,
    navigateToPrevious: () -> Unit,
) {
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .padding(horizontal = 8.dp),
        horizontalArrangement = Arrangement.SpaceBetween
    ) {
        Button(onClick = ... }) {
            Text(text = "Previous")
        }

        Button(onClick = ... }) {
            Text(text = "Next")
        }
    }
}

Книжная ориентация

Альбомная ориентация

Проблема в том, что Art(...) - изображение занимает все пространство по вертикали, не оставляя место под AboutArt(...) - описание изображения и NavigationArts(...) - кнопки навигации между изображениями.


Ответы (2 шт):

Автор решения: Роман Юмагулов
@Composable
fun ArtSpaceApp(modifier: Modifier = Modifier) {
    ...
    Column(
        modifier = modifier
            .fillMaxSize()
            .padding(horizontal = 24.dp),
        verticalArrangement = Arrangement.SpaceBetween,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Spacer(modifier = Modifier.height(20.dp))
        Column(
            modifier = modifier,
            verticalArrangement = Arrangement.Center,
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            Art(
                imageResource = artState.drawableRes,
                modifier = Modifier
                    .padding(top = 32.dp, bottom = 16.dp)
                    .requiredHeightIn(min = 0.dp, max = 500.dp)
            )
            Spacer(modifier = Modifier.height(24.dp))
            AboutArt(
                title = artState.title,
                author = artState.author,
                year = artState.year,
            )
        }
        NavigationArts(
            { navigateToNextArt() },
            { navigateToPreviousArt() }
        )
    }
}

@Composable
fun Art(
    @DrawableRes imageResource: Int,
    modifier: Modifier = Modifier,
) {
    Surface(modifier = modifier.shadow(elevation = 16.dp)) {
        Surface(
            modifier = Modifier
                .border(width = 32.dp, color = Color.White)
        ) {
            Image(
                modifier = Modifier.padding(32.dp),
                painter = painterResource(id = imageResource),
                contentDescription = null,
                contentScale = ContentScale.Inside
            )
        }
    }
}

Art(), Spacer() и AboutArt() обернул в отдельный контейнер Column, чтобы кнопки навигации были "прибиты" к нижней части экрана. В самом Art() контейнер Surface пришлось обернуть в еще один контейнер, иначе не отображалась тень modifier.shadow(elevation = 16.dp).

На лучшую реализацию не претендую)

→ Ссылка
Автор решения: vitidev

Размещение нужно такое

отступ
картинка
отступ
подпись
отступ до самого низа
навигация

И чтобы подпись была всегда видна, и чтобы картинка ужималась в размерах.Проблема в том, что картинка не знает сколько высоты ей можно взять. Column не лимитирует размер. Modifier.weight(1f) не помогает, ведь применяясь к Surface он растягивает его по всей высоте, а если применить к картинке, то навигация прижмется к подписи.

У вас слишком много вложенных блоков. Убираем лишнее

data class ArtState(
    @DrawableRes val drawableRes: Int,
    val title: String,
    val author: String,
    val year: Int
)

@Composable
fun Art(
    @DrawableRes imageResource: Int,
    modifier: Modifier = Modifier,
) {
    Surface(
        modifier = modifier
            .shadow(elevation = 16.dp)
            .border(width = 32.dp, color = Color.White)
    ) {
        Image(
            modifier = Modifier.padding(32.dp),
            painter = painterResource(id = imageResource),
            contentDescription = null,
            contentScale = ContentScale.Inside
        )
    }
}

@Composable
fun NavigationArts(
    navigateToNext: () -> Unit,
    navigateToPrevious: () -> Unit,
) {
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .padding(horizontal = 8.dp),
        horizontalArrangement = Arrangement.SpaceBetween
    ) {
        Button(onClick = navigateToNext) { Text(text = "Previous") }
        Button(onClick = navigateToPrevious) { Text(text = "Next") }
    }
}

@Composable
fun AboutArt(
    title: String,
    author: String,
    year: Int,
    modifier: Modifier = Modifier
) {
    Surface(
        modifier = modifier
            .background(color = Color.Gray)
            .padding(16.dp)
    ) {
        Column(
            modifier = Modifier
                .fillMaxWidth()
        ) {
            Text(
                text = title,
                fontSize = 24.sp,
                fontWeight = FontWeight.W300,
            )

            Row {
                Text(
                    text = author,
                    fontWeight = FontWeight.W800
                )
                Text(text = " ($year)")
            }
        }
    }
}

Вот решение через слоты. Нет смысла комбинировать Spacer + padding, если можно задать просто padding. Также убрал лишнюю вложенность Column->Column

@Composable
fun ArtSpaceApp(artState: ArtState, modifier: Modifier = Modifier) {
    Column(
        modifier = modifier
            .fillMaxSize()
            .padding(horizontal = 24.dp),
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        MyLayout(
            modifier = Modifier.weight(1f),
            content = {
                Art(
                    imageResource = artState.drawableRes,
                    modifier = Modifier
                        // убрал лишние Spacer
                        .padding(top = (32.dp + 20.dp), bottom = (16.dp + 24.dp))
                )
            },
            bottomContent = {
                AboutArt(
                    title = artState.title,
                    author = artState.author,
                    year = artState.year,
                )
            }
        )

        NavigationArts(
            { },
            { }
        )
    }
}

@Composable
fun MyLayout(
    modifier: Modifier = Modifier,
    content: @Composable () -> Unit,
    bottomContent: @Composable () -> Unit,
) {
    SubcomposeLayout(modifier = modifier) { constraints: Constraints ->
        // тут bottom определяет свой размер. По ширине пусть берет все, а по высоте не более ей нужного
        val bottomPlaceable: Placeable = subcompose(SlotsEnum.Bottom, bottomContent).map {
            it.measure(Constraints(maxWidth = constraints.maxWidth))
        }.first()

        // а тут основной контент определяет свой размер. Из общей высоты вычитаем bottom и пусть умещается
        // по высоте, а по ширине там сработает ContentScale
        val contentPlaceable: Placeable = subcompose(SlotsEnum.Main, content).map {
            it.measure(
                Constraints(
                    maxWidth = constraints.maxWidth,
                    maxHeight = constraints.maxHeight - bottomPlaceable.height
                )
            )
        }.first()

        // размещаем
        layout(constraints.maxWidth, constraints.maxHeight) {
            // центрируем
            contentPlaceable.placeRelative((constraints.maxWidth - contentPlaceable.width) / 2, 0)
            // приклеиваем bottom к картинке
            bottomPlaceable.placeRelative(0, contentPlaceable.height)
        }
    }
}

enum class SlotsEnum {
    Main,
    Bottom
}

Если нужно вывести мелкую картинку, то подпись может быть слишком высоко, поэтому в MyLayout можно передать минимальную высоту и сделать что-то типа

maxHeight = min(myminHeight, constraints.maxHeight - bottomPlaceable.height)

Попробую то же самое с ConstraintLayout. Если получится, то дополню ответ.

→ Ссылка