跳至主要內容

具有間隔項目的清單

您可能想要建立一個清單,其中所有清單項目都間隔均勻,以便項目佔據可見空間。例如,下圖中的四個項目間隔均勻,「項目 0」位於頂部,「項目 3」位於底部。

Spaced items

同時,您可能希望允許使用者在項目清單不適合時捲動清單,可能是因為裝置太小、使用者調整了視窗大小,或項目數量超過螢幕大小。

Scrollable items

通常,您會使用 Spacer 來調整 Widget 之間的間距,或使用 Expanded 來展開 Widget 以填滿可用空間。然而,這些解決方案在可捲動的 Widget 內部是不可行的,因為它們需要有限的高度約束。

本食譜示範如何使用 LayoutBuilderConstrainedBox,在空間足夠時均勻間隔清單項目,並允許使用者在空間不足時捲動,使用以下步驟

  1. 加入具有 SingleChildScrollViewLayoutBuilder
  2. SingleChildScrollView 內部加入 ConstrainedBox
  3. 建立具有間隔項目的 Column

1. 加入具有 SingleChildScrollViewLayoutBuilder

#

首先,建立一個 LayoutBuilder。您需要提供一個具有兩個參數的 builder 回呼函式

  1. LayoutBuilder 提供的 BuildContext
  2. 父 Widget 的 BoxConstraints

在本食譜中,您不會使用 BuildContext,但在下一步中您會需要 BoxConstraints

builder 函式內部,傳回一個 SingleChildScrollView。此 Widget 可確保子 Widget 可以捲動,即使父容器太小。

dart
LayoutBuilder(builder: (context, constraints) {
  return SingleChildScrollView(
    child: Placeholder(),
  );
});

2. 在 SingleChildScrollView 內部加入 ConstrainedBox

#

在此步驟中,將 ConstrainedBox 加入為 SingleChildScrollView 的子項。

ConstrainedBox Widget 會對其子項施加額外的約束。

minHeight 參數設定為 LayoutBuilder 約束的 maxHeight,來設定約束。

這可確保子 Widget 的最小高度被約束為等於 LayoutBuilder 約束提供的可用空間,也就是 BoxConstraints 的最大高度。

dart
LayoutBuilder(builder: (context, constraints) {
  return SingleChildScrollView(
    child: ConstrainedBox(
      constraints: BoxConstraints(minHeight: constraints.maxHeight),
      child: Placeholder(),
    ),
  );
});

然而,您不設定 maxHeight 參數,因為您需要允許子項大於 LayoutBuilder 的大小,以防項目不適合螢幕。

3. 建立具有間隔項目的 Column

#

最後,將 Column 加入為 ConstrainedBox 的子項。

為了均勻間隔項目,請將 mainAxisAlignment 設定為 MainAxisAlignment.spaceBetween

dart
LayoutBuilder(builder: (context, constraints) {
  return SingleChildScrollView(
    child: ConstrainedBox(
      constraints: BoxConstraints(minHeight: constraints.maxHeight),
      child: Column(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: [
          ItemWidget(text: 'Item 1'),
          ItemWidget(text: 'Item 2'),
          ItemWidget(text: 'Item 3'),
        ],
      ),
    ),
  );
});

或者,您可以使用 Spacer Widget 來調整項目之間的間距,或使用 Expanded Widget,如果您希望一個 Widget 比其他 Widget 佔用更多空間。

為此,您必須使用 IntrinsicHeight Widget 包裹 Column,這會強制 Column Widget 將自身調整為最小高度,而不是無限展開。

dart
LayoutBuilder(builder: (context, constraints) {
  return SingleChildScrollView(
    child: ConstrainedBox(
      constraints: BoxConstraints(minHeight: constraints.maxHeight),
      child: IntrinsicHeight(
        child: Column(
          children: [
            ItemWidget(text: 'Item 1'),
            Spacer(),
            ItemWidget(text: 'Item 2'),
            Expanded(
              child: ItemWidget(text: 'Item 3'),
            ),
          ],
        ),
      ),
    ),
  );
});

互動式範例

#

此範例顯示一個清單,其中的項目在一個 Column 內均勻間隔。當項目不適合螢幕時,可以上下捲動清單。項目數量由變數 items 定義,變更此值以查看當項目不適合螢幕時會發生什麼。

import 'package:flutter/material.dart';

void main() => runApp(const SpacedItemsList());

class SpacedItemsList extends StatelessWidget {
  const SpacedItemsList({super.key});

  @override
  Widget build(BuildContext context) {
    const items = 4;

    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        cardTheme: CardTheme(color: Colors.blue.shade50),
        useMaterial3: true,
      ),
      home: Scaffold(
        body: LayoutBuilder(builder: (context, constraints) {
          return SingleChildScrollView(
            child: ConstrainedBox(
              constraints: BoxConstraints(minHeight: constraints.maxHeight),
              child: Column(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                crossAxisAlignment: CrossAxisAlignment.stretch,
                children: List.generate(
                    items, (index) => ItemWidget(text: 'Item $index')),
              ),
            ),
          );
        }),
      ),
    );
  }
}

class ItemWidget extends StatelessWidget {
  const ItemWidget({
    super.key,
    required this.text,
  });

  final String text;

  @override
  Widget build(BuildContext context) {
    return Card(
      child: SizedBox(
        height: 100,
        child: Center(child: Text(text)),
      ),
    );
  }
}