Android/Compose

[Android] Compose의 Flow Layout

태크민 2025. 5. 31. 22:40

Compose의 Flow 레이아웃

FlowRow와 FlowColumn은 Row와 Column과 유사한 컴포저블이지만, 컨테이너의 공간이 부족할 경우 아이템이 다음 줄로 넘어가는 점에서 차이가 있습니다.
이로 인해 여러 개의 행(Row)이나 열(Column)이 생성될 수 있습니다.

maxItemsInEachRow 또는 maxItemsInEachColumn을 설정하여 한 줄에 들어갈 최대 아이템 수를 제어할 수도 있습니다.

이러한 FlowRow와 FlowColumn은 반응형 레이아웃을 구성할 때 유용하게 사용할 수 있습니다.
예를 들어 아이템이 너무 커서 한 줄에 다 들어가지 않아도 콘텐츠가 잘리지 않고 자연스럽게 다음 줄로 넘어가며,
maxItemsInEach*와 Modifier.weight(weight)를 함께 사용하면 필요할 때 행 또는 열의 너비를 채우거나 확장하는 레이아웃을 쉽게 만들 수 있습니다.

 

가장 대표적인 예는 Chip UI나 필터 선택 UI입니다.

그림 1.  FlowRow의 예

 

기본 사용법

FlowRow 또는 FlowColumn을 사용하려면, 해당 컴포저블을 생성한 후 표준 플로우 방식으로 배치될 아이템들을 내부에 배치하면 됩니다.

@Composable
private fun FlowRowSimpleUsageExample() {
    FlowRow(modifier = Modifier.padding(8.dp)) {
        ChipItem("Price: High to Low")
        ChipItem("Avg rating: 4+")
        ChipItem("Free breakfast")
        ChipItem("Free cancellation")
        ChipItem("£50 pn")
    }
}

 

위 코드 스니펫은 첫 번째 줄에 공간이 부족할 경우 아이템이 자동으로 다음 줄로 넘어가는 UI를 생성합니다.
이는 위에 표시된 UI와 같은 결과를 보여줍니다.

 


플로우 레이아웃의 특징

플로우 레이아웃은 앱 내에서 다양한 레이아웃을 구성할 수 있도록 다음과 같은 특징 및 속성을 제공합니다.

 

주 축(main axis) 정렬: 수평 또는 수직 정렬

주 축(main axis)은 아이템이 배치되는 기준 축입니다.
예를 들어 FlowRow에서는 아이템들이 수평 방향으로 정렬됩니다.
이때 horizontalArrangement 파라미터를 통해 아이템 간의 여유 공간을 어떻게 분배할지를 제어할 수 있습니다.

 

아래 내용은 FlowRow에서 horizontalArrangement를 설정했을 때 아이템의 정렬 방식 예시를 보여줍니다.

FlowRow에 설정된 수평 정렬 (Horizontal arrangement)

1. Arrangement.Start (Default)

2.  Arrangement.SpaceBetween

3. Arrangement.Center

4. Arrangement.End

5. Arrangement.SpaceAround

6. Arrangement.SpaceBy(8.dp)

 

FlowColumn의 경우에도 verticalArrangement를 사용하여 유사한 옵션을 설정할 수 있으며, 기본값은 Arrangement.Top입니다.

 

교차 축 정렬 (Cross axis arrangement)

교차 축(cross axis)은 주 축(main axis)의 반대 방향에 있는 축을 의미합니다.
예를 들어 FlowRow에서는 주 축이 수평이므로, 교차 축은 수직 방향이 됩니다.
컨테이너 내부의 전체 콘텐츠를 교차 축 기준으로 어떻게 정렬할지를 변경하려면 FlowRow에서는 verticalArrangement를, FlowColumn에서는 horizontalArrangement를 사용합니다.

FlowRow에 설정된 수직 정렬 (Vertical arrangement)

1. Arrangement.Top (Default)

2. Arrangement.Bottom

3. Arrangement.Center

 

FlowColumn에서는 horizontalArrangement를 통해 유사한 옵션을 설정할 수 있습니다.
기본 교차 축 정렬 값은 Arrangement.Start입니다.

 

개별 아이템 정렬 (Individual item alignment)

경우에 따라 한 줄 내에서 각각의 아이템을 서로 다른 정렬 방식으로 배치하고 싶을 수 있습니다.
이는 verticalArrangement나 horizontalArrangement와는 다르며, 현재 라인(line) 내에서의 정렬을 의미합니다.
이럴 때는 Modifier.align()을 사용하여 적용할 수 있습니다.

 

예를 들어, FlowRow 내 아이템들의 높이가 서로 다를 경우, 해당 줄(row)은 가장 큰 아이템의 높이를 기준으로 정렬되며, 각 아이템에 Modifier.align(alignmentOption)을 적용하면 라인 내에서의 정렬 방식을 개별적으로 지정할 수 있습니다.

 

FlowRow에 설정된 수직 정렬 (Vertical alignment)

1. Alignment.Top (Default)

2. Alignment.Bottom

3. Alignment.CenterVertically

FlowColumn에서도 유사한 옵션을 사용할 수 있으며, 기본 정렬 값은 Alignment.Start입니다.

 

행 또는 열의 최대 아이템 수

maxItemsInEachRow 또는 maxItemsInEachColumn 파라미터는 한 줄(한 열 또는 한 행)에 배치될 수 있는 최대 아이템 수를 정의합니다.
지정된 수를 초과하면 다음 줄(또는 열)로 자동 줄바꿈(wrapping) 됩니다.
기본값은 Int.MAX_INT이며, 이는 아이템 크기가 허용하는 한도 내에서 최대한 많은 아이템을 한 줄에 배치하도록 합니다.

 

예를 들어 maxItemsInEachRow를 설정하면, 초기 레이아웃은 한 줄에 최대 3개의 아이템만 배치되도록 강제할 수 있습니다.

 

1. 최대 개수 제한 없음

2. maxItemsInEachRow = 3

 

아이템 가중치 (Item weights)

weight는 아이템이 위치한 줄에서 사용 가능한 공간과 가중치 비율에 따라 아이템을 확장합니다.
중요한 점은, FlowRow와 Row에서 가중치가 아이템의 너비를 계산하는 방식에 차이가 있다는 것입니다.

Row에서는 Row 내 모든 아이템을 기준으로 가중치를 계산합니다.
반면, FlowRow에서는 아이템이 위치한 줄(line) 내의 아이템들만을 기준으로 계산합니다.

예를 들어, 하나의 줄에 가중치가 각각 1f, 2f, 1f, 3f인 4개의 아이템이 있다면, 전체 가중치는 7f가 됩니다.
이때 남은 공간은 7f로 나누어지고, 각 아이템의 너비는 다음과 같이 계산됩니다:
weight × (남은 공간 ÷ 전체 가중치)

 

Modifier.weight와 maxItemsInEachRow 또는 maxItemsInEachColumn을 함께 사용하면 FlowRow나 FlowColumn에서 격자(grid) 형태의 레이아웃을 만들 수 있습니다.
이 방법은 기기 크기에 맞춰 조절되는 반응형 레이아웃을 구현할 때 유용합니다.

아래에는 가중치를 사용하여 동일한 크기의 아이템들로 구성된 그리드를 만드는 다양한 예시가 소개되어 있습니다.

그림 2. FlowRow를 사용한 그리드 생성

 

동일한 크기의 아이템으로 구성된 그리드를 만들기 위해서는 다음과 같이 할 수 있습니다:

val rows = 3
val columns = 3
FlowRow(
    modifier = Modifier.padding(4.dp),
    horizontalArrangement = Arrangement.spacedBy(4.dp),
    maxItemsInEachRow = rows
) {
    val itemModifier = Modifier
        .padding(4.dp)
        .height(80.dp)
        .weight(1f)
        .clip(RoundedCornerShape(8.dp))
        .background(MaterialColors.Blue200)
    repeat(rows * columns) {
        Spacer(modifier = itemModifier)
    }
}

 

중요하게도, 아이템을 하나 더 추가해서 9번이 아닌 10번 반복하면, 마지막 아이템은 마지막 행에서 전체 너비를 차지하게 됩니다.
이는 해당 행의 전체 weight가 1f이기 때문입니다.

그림 3. 마지막 아이템이 전체 너비를 차지하는 그리드를 FlowRow로 구성한 예

 

Modifier.weight는 Modifier.width(정확한 dp 값), Modifier.aspectRatio(비율), 또는 Modifier.fillMaxWidth(비율) 같은 다른 Modifier들과 함께 사용할 수 있습니다.
이러한 Modifier들은 서로 결합되어, FlowRow(또는 FlowColumn) 내에서 반응형 크기 조절이 가능한 아이템 배치를 가능하게 합니다.

또한, 두 아이템이 각각 너비의 절반을 차지하고, 다음 아이템 하나가 전체 너비를 차지하는크기가 다른 아이템들이 번갈아 배치되는 그리드 레이아웃도 만들 수 있습니다.

그림 4. 행마다 크기가 다른 아이템들이 번갈아 배치된 FlowRow

 

다음 코드를 통해 이러한 레이아웃을 구현할 수 있습니다:

FlowRow(
    modifier = Modifier.padding(4.dp),
    horizontalArrangement = Arrangement.spacedBy(4.dp),
    maxItemsInEachRow = 2
) {
    val itemModifier = Modifier
        .padding(4.dp)
        .height(80.dp)
        .clip(RoundedCornerShape(8.dp))
        .background(Color.Blue)
    repeat(6) { item ->
        // if the item is the third item, don't use weight modifier, but rather fillMaxWidth
        if ((item + 1) % 3 == 0) {
            Spacer(modifier = itemModifier.fillMaxWidth())
        } else {
            Spacer(modifier = itemModifier.weight(0.5f))
        }
    }
}

 

비율 기반 크기 지정 (Fractional sizing)

Modifier.fillMaxWidth(fraction)을 사용하면, 아이템이 차지해야 할 컨테이너 너비의 비율을 지정할 수 있습니다.
하지만 이 동작은 Row나 Column에 적용될 때와는 다르게 작동합니다.

Row나 Column에서는 fillMaxWidth(fraction)이 전체 컨테이너 너비의 일부가 아니라, 남은 공간의 일부를 차지하도록 작동합니다.
반면, FlowRow에서는 이 값이 전체 컨테이너의 너비 비율로 적용됩니다.

 

예를 들어, 아래 코드에서는 FlowRow와 Row에 따라 서로 다른 결과를 보여줍니다.

FlowRow(
    modifier = Modifier.padding(4.dp),
    horizontalArrangement = Arrangement.spacedBy(4.dp),
    maxItemsInEachRow = 3
) {
    val itemModifier = Modifier
        .clip(RoundedCornerShape(8.dp))
    Box(
        modifier = itemModifier
            .height(200.dp)
            .width(60.dp)
            .background(Color.Red)
    )
    Box(
        modifier = itemModifier
            .height(200.dp)
            .fillMaxWidth(0.7f)
            .background(Color.Blue)
    )
    Box(
        modifier = itemModifier
            .height(200.dp)
            .weight(1f)
            .background(Color.Magenta)
    )
}

 

1. FlowRow: 가운데 아이템이 전체 컨테이너 너비의 70%를 차지함.

2. Row: 가운데 아이템이 Row의 남은 너비의 70%를 차지함.

 

fillMaxColumnWidth() 및 fillMaxRowHeight()

FlowColumn 또는 FlowRow 내부의 아이템에 Modifier.fillMaxColumnWidth() 또는 Modifier.fillMaxRowHeight()를 적용하면,
같은 열 또는 같은 행에 있는 아이템들이 가장 큰 아이템의 너비 또는 높이에 맞춰 동일한 크기를 가지게 됩니다.

 

예를 들어, 아래 예제에서는 FlowColumn을 사용하여 안드로이드 디저트 목록을 표시합니다.
이때 Modifier.fillMaxColumnWidth()를 아이템에 적용했을 때와 적용하지 않았을 때의 아이템 너비 차이(아이템이 자동 줄바꿈되는 경우와 그렇지 않은 경우)를 확인할 수 있습니다.

FlowColumn(
    Modifier
        .padding(20.dp)
        .fillMaxHeight()
        .fillMaxWidth(),
    horizontalArrangement = Arrangement.spacedBy(8.dp),
    verticalArrangement = Arrangement.spacedBy(8.dp),
    maxItemsInEachColumn = 5,
) {
    repeat(listDesserts.size) {
        Box(
            Modifier
                .fillMaxColumnWidth()
                .border(1.dp, Color.DarkGray, RoundedCornerShape(8.dp))
                .padding(8.dp)
        ) {

            Text(
                text = listDesserts[it],
                fontSize = 18.sp,
                modifier = Modifier.padding(3.dp)
            )
        }
    }
}

 

1. 각 아이템에 Modifier.fillMaxColumnWidth() 적용됨

2. 너비 설정 없음 (아이템이 감싸지며 배치됨)

 

 

 

끝.


참고자료

https://developer.android.com/develop/ui/compose/layouts/flow?_gl=1*1qek915*_up*MQ..*_ga*MTQyMzEzNTM3OC4xNzQ4Njg3MjY0*_ga_6HH9YJMN9M*czE3NDg2ODcyNjQkbzEkZzAkdDE3NDg2ODc0NDYkajYwJGwwJGg2MDc1NDUyODc.#cross-axis

 

Compose의 Flow 레이아웃  |  Android Developers

이 페이지는 Cloud Translation API를 통해 번역되었습니다. Compose의 Flow 레이아웃 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. FlowRow 및 FlowColumn는 Row 및 Column와

developer.android.com