Compose の Flow レイアウト

FlowRowFlowColumnRowColumn に似たコンポーザブルですが、コンテナのスペースがなくなるとアイテムが次の行に流れる点が異なります。これにより、複数の行または列が作成されます。行内のアイテム数は、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 が生成されます。この 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 です。これにより、サイズが 1 行に収まる限り、できるだけ多くのアイテムを配置できます。

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

上限が設定されていない

maxItemsInEachRow = 3

フロー行に最大値が設定されていない フローの行に設定されたアイテムの最大数

アイテムの重み

重みは、アイテムの係数と、アイテムが配置された行の空きスペースに基づいてアイテムを拡大します。重要なのは、FlowRowRow では、アイテムの幅の計算に重みがどのように使用されるかが異なることです。Rows の場合、重みは Row 内のすべてのアイテムに基づきます。FlowRow の場合、重みは FlowRow コンテナ内のすべてのアイテムではなく、アイテムが配置されている行のアイテムに基づきます。

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

Modifier.weight と最大項目を FlowRow または FlowColumn と組み合わせて使用すると、グリッドのようなレイアウトを作成できます。このアプローチは、デバイスのサイズに合わせて調整されるレスポンシブ レイアウトを作成するのに便利です。

重み付けを使用して実現できることの例をいくつか示します。たとえば、次の図に示すように、アイテムのサイズが均等なグリッドがあります。

フロー行で作成されたグリッド
図 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 回繰り返すと、行全体の合計の重みが 1f になるため、最後のアイテムが最後の列全体を占めることです。

グリッドの最後のアイテムがフルサイズ
図 3. FlowRow を使用して、最後のアイテムが全幅を占めるグリッドを作成する

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

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

フロー行を含む交互のグリッド
図 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))
        }
    }
}

分数サイズ設定

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 の割合の中央のアイテム。

フロー行の分数幅

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

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

最大列幅が設定されていない