Compose の Flow レイアウト

FlowRow と FlowColumn は、Row と Column に似たコンポーザブルですが、コンテナのスペースがなくなると、アイテムが次の行に流れる点が異なります。これにより、複数の行または列が作成されます。1 行のアイテム数は、maxItemsInEachRow または maxItemsInEachColumn を設定して制御することもできます。多くの場合、FlowRow と FlowColumn を使用してレスポンシブ レイアウトを構築できます。アイテムが大きすぎて 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 では、アイテムは水平方向に配置されます)。FlowRow の horizontalArrangement パラメータは、アイテム間で空きスペースを分散する方法を制御します。

次の表に、FlowRow のアイテムに horizontalArrangement を設定する例を示します。

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

結果

Arrangement.Start(Default)

アイテムが先頭に配置されている

Arrangement.SpaceBetween

アイテム間にスペースを空けた配置

Arrangement.Center

中央に配置されたアイテム

Arrangement.End

最後に配置されたアイテム

Arrangement.SpaceAround

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

Arrangement.spacedBy(8.dp)

特定の dp で区切られたアイテム

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

交差軸の配置

交差軸は、主軸とは逆方向の軸です。たとえば、FlowRow では、これは縦軸です。コンテナ内のコンテンツ全体のクロス軸方向の配置を変更するには、FlowRow には verticalArrangement を、FlowColumn には horizontalArrangement を使用します。

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

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

結果

Arrangement.Top(Default)

コンテナの上部配置

Arrangement.Bottom

コンテナの下部配置

Arrangement.Center

コンテナの中央配置

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

個々のアイテムの配置

行内の個々のアイテムを異なる配置で配置したい場合があります。これは、verticalArrangement や horizontalArrangement とは異なり、現在の行内のアイテムを配置します。これは Modifier.align() で適用できます。

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

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

結果

Alignment.Top(Default)

上揃えにされたアイテム

Alignment.Bottom

下揃えにされたアイテム

Alignment.CenterVertically

中央揃えにされたアイテム

FlowColumn の場合、同様のオプションを使用できます。デフォルトのアライメントは Alignment.Start です。

行または列の最大アイテム数

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

たとえば、maxItemsInEachRow を設定すると、初期レイアウトに 3 つのアイテムのみが強制的に配置されます。

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

maxItemsInEachRow = 3

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

アイテムの重み

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

たとえば、1 つの行に 4 つのアイテムがあり、それぞれに 1f, 2f, 1f と 3f の異なる重みが設定されている場合、合計の重みは 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) を使用すると、アイテムが占めるコンテナのサイズを指定できます。これは、Modifier.fillMaxWidth(fraction) を Row または Column に適用した場合の動作とは異なります。Row/Column アイテムは、コンテナ全体の幅ではなく、残りの幅の割合を占めます。

たとえば、次のコードでは、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)
    )
}

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

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

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