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 が表示され、最初の行にスペースがなくなると、アイテムが自動的に次の行に送られます。

フロー レイアウトの機能

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

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

主軸は、アイテムが配置される軸です(たとえば、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 と最大アイテム数と 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) を使用すると、アイテムが占めるコンテナのサイズを指定できます。これは、Modifier.fillMaxWidth(fraction)Row または Column に適用した場合の動作とは異なります。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 分の 1 である中央のアイテム。

小数の幅とフロー行

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

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

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