列表组件详解

列表组件是展示数据的常用方式,Flutter 提供了多种列表组件来满足不同的需求。本教程将详细介绍 ListView、GridView 等列表组件。

重要提示:Flutter 3.28+ 进一步优化了列表性能,特别是在大量数据和滚动场景下。

1. ListView 列表视图

ListView 是最常用的列表组件,支持垂直和水平滚动,可以动态生成列表项。

📐 ListView 渲染效果

垂直 ListView

列表项 1
列表项 2
列表项 3
列表项 4
列表项 5

水平 ListView

项1
项2
项3
项4

核心属性

属性 类型 说明
scrollDirection Axis 滚动方向(垂直或水平)
reverse bool 是否反向滚动
controller ScrollController 滚动控制器
primary bool 是否为主滚动视图
physics ScrollPhysics 滚动物理特性
shrinkWrap bool 是否根据子组件尺寸调整大小
padding EdgeInsets 内边距

使用示例

// 基本 ListView(静态列表)
ListView(
  children: const [
    ListTile(
      leading: Icon(Icons.person),
      title: Text('Alice'),
      subtitle: Text('Software Engineer'),
    ),
    ListTile(
      leading: Icon(Icons.person),
      title: Text('Bob'),
      subtitle: Text('Designer'),
    ),
    ListTile(
      leading: Icon(Icons.person),
      title: Text('Charlie'),
      subtitle: Text('Manager'),
    ),
  ],
)

// ListView.builder(动态列表)
final List _items = List.generate(100, (index) => 'Item $index');

ListView.builder(
  itemCount: _items.length,
  itemBuilder: (context, index) {
    return ListTile(
      title: Text(_items[index]),
      onTap: () {
        print('点击: ${_items[index]}');
      },
    );
  },
)

// ListView.separated(带分隔符)
ListView.separated(
  itemCount: _items.length,
  separatorBuilder: (context, index) => const Divider(),
  itemBuilder: (context, index) {
    return ListTile(
      title: Text(_items[index]),
    );
  },
)

// 水平 ListView
ListView(
  scrollDirection: Axis.horizontal,
  children: [
    Container(
      width: 200,
      color: Colors.red,
      child: const Center(child: Text('红色')),
    ),
    Container(
      width: 200,
      color: Colors.green,
      child: const Center(child: Text('绿色')),
    ),
    Container(
      width: 200,
      color: Colors.blue,
      child: const Center(child: Text('蓝色')),
    ),
  ],
)

// 带 Card 的 ListView
ListView.builder(
  padding: const EdgeInsets.all(8),
  itemCount: 10,
  itemBuilder: (context, index) {
    return Card(
      margin: const EdgeInsets.symmetric(vertical: 4),
      child: ListTile(
        leading: CircleAvatar(
          child: Text('${index + 1}'),
        ),
        title: Text('项目 ${index + 1}'),
        subtitle: Text('这是项目 ${index + 1} 的描述'),
        trailing: const Icon(Icons.arrow_forward),
        onTap: () {
          print('点击项目 ${index + 1}');
        },
      ),
    );
  },
)
技巧:对于大量数据,使用 ListView.builder 而不是 ListView(children: []),以提高性能。

2. GridView 网格视图

GridView 以网格形式展示子组件,支持多种布局模式。

📐 GridView 渲染效果

2列网格

1
2
3
4
5
6

3列网格

1
2
3
4
5
6
7
8
9

使用示例

// GridView.count(固定列数)
GridView.count(
  crossAxisCount: 2,
  crossAxisSpacing: 10,
  mainAxisSpacing: 10,
  children: List.generate(20, (index) {
    return Container(
      color: Colors.primaries[index % Colors.primaries.length],
      child: Center(
        child: Text(
          '$index',
          style: const TextStyle(
            color: Colors.white,
            fontSize: 24,
          ),
        ),
      ),
    );
  }),
)

// GridView.extent(固定宽度)
GridView.extent(
  maxCrossAxisExtent: 150,
  crossAxisSpacing: 10,
  mainAxisSpacing: 10,
  children: List.generate(20, (index) {
    return Card(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Icon(
            Icons.star,
            size: 48,
            color: Colors.primaries[index % Colors.primaries.length],
          ),
          const SizedBox(height: 8),
          Text('项目 $index'),
        ],
      ),
    );
  }),
)

// GridView.builder(动态网格)
GridView.builder(
  gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
    crossAxisCount: 3,
    crossAxisSpacing: 10,
    mainAxisSpacing: 10,
    childAspectRatio: 1.0,
  ),
  itemCount: 50,
  itemBuilder: (context, index) {
    return Container(
      decoration: BoxDecoration(
        color: Colors.primaries[index % Colors.primaries.length],
        borderRadius: BorderRadius.circular(8),
      ),
      child: Center(
        child: Text(
          '$index',
          style: const TextStyle(
            color: Colors.white,
            fontSize: 20,
          ),
        ),
      ),
    );
  },
)

// 自定义网格布局
GridView.builder(
  gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
    maxCrossAxisExtent: 200,
    mainAxisSpacing: 10,
    crossAxisSpacing: 10,
    childAspectRatio: 3/4,
  ),
  itemCount: 12,
  itemBuilder: (context, index) {
    return Card(
      clipBehavior: Clip.antiAlias,
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          Expanded(
            child: Container(
              color: Colors.primaries[index % Colors.primaries.length],
              child: Center(
                child: Icon(
                  Icons.image,
                  size: 48,
                  color: Colors.white,
                ),
              ),
            ),
          ),
          Padding(
            padding: const EdgeInsets.all(8.0),
            child: Text(
              '图片 $index',
              textAlign: TextAlign.center,
            ),
          ),
        ],
      ),
    );
  },
)

3. ListTile 列表项

ListTile 是 Material Design 风格的列表项组件,提供了丰富的布局选项。

📐 ListTile 渲染效果

👤
Alice
Software Engineer
启用通知
接收推送通知
飞行模式
禁用无线连接
U
用户名
用户简介

使用示例

// 基本 ListTile
ListTile(
  leading: const Icon(Icons.person),
  title: const Text('Alice'),
  subtitle: const Text('Software Engineer'),
  trailing: const Icon(Icons.arrow_forward),
  onTap: () {
    print('点击 Alice');
  },
)

// 带复选框的 ListTile
CheckboxListTile(
  title: const Text('启用通知'),
  subtitle: const Text('接收推送通知'),
  value: _notificationsEnabled,
  onChanged: (bool? value) {
    setState(() {
      _notificationsEnabled = value ?? false;
    });
  },
)

// 带开关的 ListTile
SwitchListTile(
  title: const Text('飞行模式'),
  subtitle: const Text('禁用无线连接'),
  value: _airplaneMode,
  onChanged: (bool value) {
    setState(() {
      _airplaneMode = value;
    });
  },
)

// 带图片的 ListTile
ListTile(
  leading: const CircleAvatar(
    backgroundImage: NetworkImage('https://via.placeholder.com/150'),
  ),
  title: const Text('用户名'),
  subtitle: const Text('用户简介'),
  trailing: const Icon(Icons.more_vert),
  onTap: () {
    print('查看用户详情');
  },
)

// 三行 ListTile
ListTile(
  leading: const Icon(Icons.email),
  title: const Text('邮件标题'),
  subtitle: const Text('邮件副标题'),
  trailing: const Icon(Icons.star_border),
  isThreeLine: true,
  onTap: () {
    print('查看邮件');
  },
)

4. CustomScrollView 自定义滚动视图

CustomScrollView 可以创建复杂的滚动效果,如固定头部、吸顶效果等。

📐 CustomScrollView 渲染效果

展开状态

标题
📋 标题
项目 0
项目 1
项目 2

折叠状态

📋 标题
项目 0
项目 1
项目 2
项目 3
项目 4

使用示例

// 带 SliverAppBar 的 CustomScrollView
CustomScrollView(
  slivers: [
    SliverAppBar(
      expandedHeight: 200,
      floating: false,
      pinned: true,
      flexibleSpace: FlexibleSpaceBar(
        title: const Text('标题'),
        background: Container(
          color: Colors.blue,
          child: const Center(
            child: Icon(
              Icons.image,
              size: 100,
              color: Colors.white,
            ),
          ),
        ),
      ),
    ),
    SliverList(
      delegate: SliverChildBuilderDelegate(
        (context, index) {
          return ListTile(
            title: Text('项目 $index'),
          );
        },
        childCount: 20,
      ),
    ),
  ],
)

// 多个 Sliver 的组合
CustomScrollView(
  slivers: [
    SliverToBoxAdapter(
      child: Container(
        height: 200,
        color: Colors.red,
        child: const Center(child: Text('头部内容')),
      ),
    ),
    SliverList(
      delegate: SliverChildBuilderDelegate(
        (context, index) {
          return ListTile(
            title: Text('项目 $index'),
          );
        },
        childCount: 10,
      ),
    ),
    SliverGrid(
      gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
        crossAxisCount: 2,
        crossAxisSpacing: 10,
        mainAxisSpacing: 10,
      ),
      delegate: SliverChildBuilderDelegate(
        (context, index) {
          return Container(
            color: Colors.blue,
            child: Center(child: Text('Grid $index')),
          );
        },
        childCount: 10,
      ),
    ),
  ],
)
技巧:使用 CustomScrollView 可以实现吸顶效果、视差滚动等复杂效果。

5. RefreshIndicator 下拉刷新

RefreshIndicator 为可滚动组件添加下拉刷新功能。

📐 RefreshIndicator 渲染效果

正常状态

项目 0
项目 1
项目 2
项目 3
项目 4

下拉状态

刷新中...
项目 0
项目 1
项目 2
项目 3

使用示例

// 基本 RefreshIndicator
RefreshIndicator(
  onRefresh: _refreshData,
  child: ListView.builder(
    itemCount: _items.length,
    itemBuilder: (context, index) {
      return ListTile(
        title: Text(_items[index]),
      );
    },
  ),
)

// 刷新函数
Future _refreshData() async {
  await Future.delayed(const Duration(seconds: 2));
  setState(() {
    _items = List.generate(20, (index) => '刷新项目 $index');
  });
}

6. ScrollController 滚动控制

ScrollController 用于控制滚动行为,如滚动到指定位置、监听滚动事件等。

📐 ScrollController 渲染效果

顶部(无滚动按钮)

项目 0
项目 1
项目 2
项目 3
项目 4

底部(显示滚动按钮)

项目 50
项目 51
项目 52
项目 53
项目 54

使用示例

class MyListView extends StatefulWidget {
  const MyListView({super.key});

  @override
  State createState() => _MyListViewState();
}

class _MyListViewState extends State {
  final ScrollController _scrollController = ScrollController();
  bool _showScrollToTop = false;

  @override
  void initState() {
    super.initState();
    _scrollController.addListener(() {
      setState(() {
        _showScrollToTop = _scrollController.offset > 200;
      });
    });
  }

  @override
  void dispose() {
    _scrollController.dispose();
    super.dispose();
  }

  void _scrollToTop() {
    _scrollController.animateTo(
      0,
      duration: const Duration(milliseconds: 500),
      curve: Curves.easeInOut,
    );
  }

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        ListView.builder(
          controller: _scrollController,
          itemCount: 100,
          itemBuilder: (context, index) {
            return ListTile(
              title: Text('项目 $index'),
            );
          },
        ),
        if (_showScrollToTop)
          Positioned(
            bottom: 20,
            right: 20,
            child: FloatingActionButton(
              onPressed: _scrollToTop,
              child: const Icon(Icons.arrow_upward),
            ),
          ),
      ],
    );
  }
}

🔀 列表组件选择流程图

开始选择列表
数据量大小?
少量(<20)
ListView / GridView
大量(≥20)
需要分割线?
ListView.separated
ListView.builder
完成选择

📊 列表组件性能对比

Flutter 列表组件性能对比表
组件 数据量 性能 内存 使用场景
ListView 少量 固定小列表
ListView.builder 大量 动态大列表
ListView.separated 大量 带分割线的列表
GridView 少量 固定网格
GridView.builder 大量 动态网格

列表组件最佳实践

  • 对于大量数据,使用 ListView.builder 或 GridView.builder
  • 为列表项添加合适的分割线或间距
  • 使用 const 构造函数提高性能
  • 为图片列表使用缓存,如 CachedNetworkImage
  • 使用 ListView.separated 添加分割线
  • 合理使用 Padding 和 margin 调整间距
  • 为长列表添加加载更多功能
  • 使用 RefreshIndicator 提供下拉刷新
  • 使用 ScrollController 监听和控制滚动
下一步:掌握列表组件后,继续学习手势组件。