Skip to main content

带有间距的列表项

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

带有间距的项目

同时,您可能希望允许用户在项目列表无法容纳时滚动浏览列表,这可能是因为设备太小、用户调整了窗口大小或项目数量超过屏幕大小。

可滚动的项目

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

此示例演示如何使用 LayoutBuilderConstrainedBox 在有足够空间时均匀地分隔列表项,并在没有足够空间时允许用户滚动,使用以下步骤:

  1. 使用 SingleChildScrollView 添加一个 LayoutBuilder
  2. SingleChildScrollView 内添加一个 ConstrainedBox
  3. 创建一个带有间距项的 Column

1. 使用 SingleChildScrollView 添加 LayoutBuilder

#

首先创建一个 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: 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)),
      ),
    );
  }
}