Compose の Flow レイアウト

FlowRowFlowColumn は、RowColumn に似ていますが、コンテナの容量が足りなくなるとアイテムが次の行に移動する点が異なります。これにより、複数の行または列が作成されます。行内のアイテム数は、maxItemsInEachRow または maxItemsInEachColumn を設定して制御することもできます。多くの場合、FlowRowFlowColumn を使用してレスポンシブ レイアウトを作成できます。アイテムが 1 次元に対して大きすぎてもコンテンツが切り取られることはありません。また、maxItemsInEach*Modifier.weight(weight) を組み合わせて使用すると、必要に応じて行または列の幅を埋めたり拡大したりするレイアウトを作成できます。

典型的な例は、チップまたはフィルタリング UI です。

FlowRow に 5 つのチップ。利用可能なスペースがなくなると次の行にオーバーフローが表示されます。
図 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 が表示されます。最初の行にスペースがなくなると、アイテムは自動的に次の行に移動します。

フロー レイアウトの機能

フロー レイアウトには、アプリでさまざまなレイアウトを作成するために使用できる次の機能とプロパティがあります。

主軸の配置: 水平または垂直の配置

主軸は、アイテムが配置される軸です(たとえば、FlowRow ではアイテムは水平方向に配置されます)。FlowRowhorizontalArrangement パラメータは、アイテム間の空き容量の分配方法を制御します。

次の表に、FlowRow の項目に horizontalArrangement を設定する例を示します。

FlowRow に水平方向の配置を設定しました

結果

Arrangement.StartDefault

先頭に並べ替えられたアイテム

Arrangement.SpaceBetween

間にスペースがあるアイテムの配置

Arrangement.Center

アイテムを中央に配置しました

Arrangement.End

アイテムは最後に配置されています

Arrangement.SpaceAround

周囲にスペースを空けて配置されたアイテム

Arrangement.spacedBy(8.dp)

特定の dp の間隔のアイテム

FlowColumn の場合、verticalArrangement でも同様のオプションを使用できます(デフォルトは Arrangement.Top)。

交差軸の配置

交差軸は、主軸とは逆方向の軸です。たとえば、FlowRow では縦軸です。コンテナ内のコンテンツ全体の交差軸での配置を変更するには、FlowRowverticalArrangement を、FlowColumnhorizontalArrangement を使用します。

次の表に、FlowRow についてアイテムに異なる verticalArrangement を設定する例を示します。

FlowRow に垂直方向の配置を設定しました

結果

Arrangement.TopDefault

コンテナの上面の配置

Arrangement.Bottom

コンテナの下部の配置

Arrangement.Center

コンテナ センターの配置

FlowColumn の場合は、horizontalArrangement でも同様のオプションを使用できます。デフォルトの交差軸の配置は Arrangement.Start です。

個々のアイテムの配置

行内の個々のアイテムをさまざまな配置で配置できます。これは verticalArrangementhorizontalArrangement とは異なり、現在の行内でアイテムを配置します。これは Modifier.align() で適用できます。

たとえば、FlowRow 内のアイテムの高さが異なる場合、行は最大のアイテムの高さを取得し、Modifier.align(alignmentOption) をアイテムに適用します。

FlowRow に垂直方向の配置を設定しました

結果

Alignment.TopDefault

アイテムを上揃えにしました

Alignment.Bottom

アイテムを下揃えにしました

Alignment.CenterVertically

アイテムを中央に配置しました

FlowColumn にも同様のオプションがあります。デフォルトのアライメントは Alignment.Start です。

行または列のアイテム数の上限

パラメータ maxItemsInEachRow または maxItemsInEachColumn は、次の行に折り返す前に 1 行に許容されるメイン軸の最大項目数を定義します。デフォルトは Int.MAX_INT で、サイズが行に収まる限り、できるだけ多くのアイテムが許可されます。

たとえば、maxItemsInEachRow を設定すると、初期レイアウトのアイテムは 3 つのみになります。

最大値が設定されていません

maxItemsInEachRow = 3

フロー行に最大値が設定されていません フロー行に設定可能な最大アイテム数

遅延読み込みのフローアイテム

ContextualFlowRowContextualFlowColumnFlowRowFlowColumn の特殊なバージョンであり、フローの行またはフローの列の内容の遅延読み込みを行うことができます。また、アイテムが最初の行にあるかどうかなど、アイテムの位置に関する情報(インデックス、行番号、使用可能なサイズ)も提供します。これは、大規模なデータセットの場合や、アイテムに関するコンテキスト情報が必要な場合に役立ちます。

maxLines パラメータは表示される行数を制限し、overflow パラメータはアイテムのオーバーフローに達したときに表示する内容を指定します。これにより、カスタムの expandIndicator または collapseIndicator を指定できます。

たとえば、[+(残りのアイテム数)] ボタンまたは [一部を表示] ボタンを表示するには、次のようにします。

val totalCount = 40
var maxLines by remember {
    mutableStateOf(2)
}

val moreOrCollapseIndicator = @Composable { scope: ContextualFlowRowOverflowScope ->
    val remainingItems = totalCount - scope.shownItemCount
    ChipItem(if (remainingItems == 0) "Less" else "+$remainingItems", onClick = {
        if (remainingItems == 0) {
            maxLines = 2
        } else {
            maxLines += 5
        }
    })
}
ContextualFlowRow(
    modifier = Modifier
        .safeDrawingPadding()
        .fillMaxWidth(1f)
        .padding(16.dp)
        .wrapContentHeight(align = Alignment.Top)
        .verticalScroll(rememberScrollState()),
    verticalArrangement = Arrangement.spacedBy(4.dp),
    horizontalArrangement = Arrangement.spacedBy(8.dp),
    maxLines = maxLines,
    overflow = ContextualFlowRowOverflow.expandOrCollapseIndicator(
        minRowsToShowCollapse = 4,
        expandIndicator = moreOrCollapseIndicator,
        collapseIndicator = moreOrCollapseIndicator
    ),
    itemCount = totalCount
) { index ->
    ChipItem("Item $index")
}

コンテキスト フローの行の例。
図 2. ContextualFlowRow の例

商品の重量

重みは、その係数と、アイテムが配置された線上の使用可能なスペースに基づいて、アイテムを拡大します。重要な点として、FlowRowRow では、アイテムの幅を計算する際の重みの使用方法に違いがあります。Rows の場合、重みは Row 内のすべてのアイテムに基づきます。FlowRow の場合、重み付けは FlowRow コンテナ内のすべてのアイテムではなく、アイテムが配置されている行のアイテムに基づきます。

たとえば、4 つのアイテムがすべて 1 行に、それぞれ重みが 1f, 2f, 1f3f の場合、総重みは 7f になります。行または列の残りのスペースは 7f で除算されます。次に、weight * (remainingSpace / totalWeight) を使用して各アイテムの幅が計算されます。

Modifier.weight と max のアイテムの組み合わせを FlowRow または FlowColumn で使用すると、グリッド状のレイアウトを作成できます。この方法は、デバイスのサイズに合わせて調整されるレスポンシブ レイアウトを作成する場合に役立ちます。

重みを使用して実現できることの例がいくつかあります。一例として、以下に示すように、アイテムのサイズを同じにするグリッドがあります。

フロー行を使用してグリッドを作成しました
図 3. 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 回繰り返すと、行全体の重みの合計が 1f になるため、最後の項目が最後の列全体を占有します。

グリッド上の最後のアイテムをフルサイズで表示します
図 4. FlowRow を使って、最後のアイテムが全幅に表示されるグリッドを作成する

重みは、Modifier.width(exactDpAmount), Modifier.aspectRatio(aspectRatio)Modifier.fillMaxWidth(fraction) などの他の Modifiers と組み合わせることができます。これらの修飾子はすべて連携して動作し、FlowRow(または FlowColumn)内のアイテムのサイズをレスポンシブに調整できるようにします。

また、異なるアイテムサイズの交互のグリッドを作成することもできます。この場合、2 つのアイテムの幅がそれぞれ半分になり、1 つのアイテムが次の列の幅いっぱいになります。

交互のグリッドとフロー行
図 5. 行のサイズを交互に入れる 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))
        }
    }
}

小数サイズ

Modifier.fillMaxWidth(fraction) を使用すると、アイテムが占めるコンテナのサイズを指定できます。これは、Row または Column に適用した場合の Modifier.fillMaxWidth(fraction) の動作とは異なります。Row/Column アイテムは、コンテナの幅全体ではなく、残りの幅の割合を占有します。

たとえば、次のコードでは、FlowRowRow を使用した場合で結果が異なります。

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

FlowRow: コンテナ全体の幅の 0.7 分の 0.7 の中央アイテム。

フロー行を含む小数の幅

Row: 中間のアイテムが残りの Row の幅の 0.7% を占めています。

行を含む小数の幅

fillMaxColumnWidth()fillMaxRowHeight()

FlowColumn または FlowRow 内のアイテムに Modifier.fillMaxColumnWidth() または Modifier.fillMaxRowHeight() を適用すると、同じ列または行内のアイテムの幅または高さが、列/行内で最大のアイテムと同じ幅または高さになります。

たとえば、この例では FlowColumn を使用して Android のデザートのリストを表示しています。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)
            )
        }
    }
}

各アイテムに Modifier.fillMaxColumnWidth() を適用しました

fillMaxColumnWidth

幅の変更が設定されていません(折り返しアイテム)

塗りつぶしの最大列幅が設定されていません