也许你希望创建一个列表,其中所有列表项都均匀分布,以便这些项占据可见空间。例如,下图中的四个项均匀分布,“项 0”在顶部,“项 3”在底部。

Spaced items

同时,你可能希望当列表项不适合时允许用户滚动浏览列表,这可能是因为设备太小、用户调整了窗口大小,或者项目数量超出了屏幕大小。

Scrollable items

通常,你使用 Spacer 来调整小部件之间的间距,或者使用 Expanded 来扩展小部件以填充可用空间。但是,这些解决方案在可滚动小部件中是不可行的,因为它们需要一个有限的高度约束。

本教程演示了如何使用 LayoutBuilderConstrainedBox,通过以下步骤在有足够空间时均匀地间隔列表项,并在空间不足时允许用户滚动

  1. 添加一个带 SingleChildScrollViewLayoutBuilder
  2. SingleChildScrollView 中添加一个 ConstrainedBox
  3. 创建一个带间隔项目的 Column

1. 添加一个带 SingleChildScrollViewLayoutBuilder

#

首先创建一个 LayoutBuilder。你需要提供一个带两个参数的 builder 回调函数

  1. LayoutBuilder 提供的 BuildContext
  2. 父小部件的 BoxConstraints

在本教程中,你不会使用 BuildContext,但在下一步中你需要 BoxConstraints

builder 函数中,返回一个 SingleChildScrollView。这个小部件确保子小部件可以滚动,即使父容器太小。

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

2. 在 SingleChildScrollView 中添加一个 ConstrainedBox

#

在此步骤中,添加一个 ConstrainedBox 作为 SingleChildScrollView 的子级。

ConstrainedBox 小部件对其子级施加额外的约束。

通过将 minHeight 参数设置为 LayoutBuilder 约束的 maxHeight 来配置约束。

这确保子小部件被约束为具有一个最小高度,该高度等于 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 小部件来调整项目之间的间距,或者使用 Expanded 小部件。

为此,你必须用 IntrinsicHeight 小部件包装 Column,这会强制 Column 小部件将自身调整为最小高度,而不是无限扩展。

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')),
            ],
          ),
        ),
      ),
    );
  },
);

互动示例

#

此示例显示了一个在列中均匀间隔的项目列表。当项目不适合屏幕时,列表可以上下滚动。项目数量由变量 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: CardThemeData(color: Colors.blue.shade50),
      ),
      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))),
    );
  }
}