列表组件详解

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

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

1. ListView 列表视图

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

核心属性

属性 类型 说明
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.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
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 可以创建复杂的滚动效果,如固定头部、吸顶效果等。

使用示例

// 带 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
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 用于控制滚动行为,如滚动到指定位置、监听滚动事件等。

使用示例

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

列表组件最佳实践

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