SliverList

一个沿主轴将多个盒子子组件按线性排列的sliver

SliverList是Flutter中用于创建可滚动子项列表的Sliver组件,专为与CustomScrollView配合使用而设计。与普通的ListView不同,SliverList能够与其他Sliver组件(如SliverAppBarSliverGrid等)协同工作,实现复杂的自定义滚动效果。

SliverList通过delegate来懒加载其子元素,只有在子元素即将出现在视口中时才会被构建,这大大提高了长列表的性能表现。它遵循Sliver协议,能够响应父滚动容器的滚动通知。

使用场景

  • 复杂滚动界面: 需要与AppBarGrid等其他滚动元素协同工作的列表
  • 长列表性能优化: 处理大量数据时使用懒加载提高性能
  • 自定义滚动效果: 实现视差滚动、折叠头部等高级UI效果
  • 多类型列表布局: 与其他Sliver组件组合创建混合布局

示例

1. 基础SliverList使用

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: CustomScrollView(
          slivers: <Widget>[
            // 滑动时折叠的AppBar
            SliverAppBar(
              expandedHeight: 200.0,
              floating: false,
              pinned: true,
              flexibleSpace: FlexibleSpaceBar(
                title: Text('SliverList 示例'),
                background: Image.network(
                  'https://picsum.photos/800/600',
                  fit: BoxFit.cover,
                ),
              ),
            ),
            // SliverList 主体部分
            SliverList(
              delegate: SliverChildBuilderDelegate(
                (BuildContext context, int index) {
                  return ListTile(
                    leading: CircleAvatar(
                      child: Text('${index + 1}'),
                    ),
                    title: Text('列表项 ${index + 1}'),
                    subtitle: Text('这是第 ${index + 1} 个列表项的描述内容'),
                    trailing: Icon(Icons.arrow_forward),
                    onTap: () {
                      print('点击了第 ${index + 1} 项');
                    },
                  );
                },
                childCount: 50, // 列表项数量
              ),
            ),
          ],
        ),
      ),
    );
  }
}

2. 多种Sliver组件组合

import 'package:flutter/material.dart';

class CombinedSliverExample extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return CustomScrollView(
      slivers: <Widget>[
        SliverAppBar(
          title: Text('混合Sliver布局'),
          floating: true,
          expandedHeight: 250.0,
          actions: [IconButton(icon: Icon(Icons.search), onPressed: () {})],
        ),
        
        // 标题部分
        SliverToBoxAdapter(
          child: Padding(
            padding: EdgeInsets.all(16.0),
            child: Text(
              '热门推荐',
              style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
            ),
          ),
        ),
        
        // 网格布局
        SliverGrid(
          gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
            crossAxisCount: 2,
            crossAxisSpacing: 10.0,
            mainAxisSpacing: 10.0,
            childAspectRatio: 0.8,
          ),
          delegate: SliverChildBuilderDelegate(
            (context, index) {
              return Card(
                child: Column(
                  children: [
                    Expanded(
                      child: Image.network(
                        'https://picsum.photos/200/300?random=$index',
                        fit: BoxFit.cover,
                        width: double.infinity,
                      ),
                    ),
                    Padding(
                      padding: EdgeInsets.all(8.0),
                      child: Text('推荐项目 $index'),
                    ),
                  ],
                ),
              );
            },
            childCount: 6,
          ),
        ),
        
        // 另一个标题
        SliverToBoxAdapter(
          child: Padding(
            padding: EdgeInsets.all(16.0),
            child: Text(
              '全部列表',
              style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
            ),
          ),
        ),
        
        // 主要的SliverList
        SliverList(
          delegate: SliverChildBuilderDelegate(
            (context, index) {
              return Container(
                margin: EdgeInsets.symmetric(vertical: 4.0, horizontal: 8.0),
                decoration: BoxDecoration(
                  color: Colors.white,
                  borderRadius: BorderRadius.circular(8.0),
                  boxShadow: [
                    BoxShadow(
                      color: Colors.black12,
                      blurRadius: 2.0,
                      offset: Offset(0, 1),
                    ),
                  ],
                ),
                child: ListTile(
                  contentPadding: EdgeInsets.all(16.0),
                  leading: Icon(Icons.star, color: Colors.amber),
                  title: Text('高级功能 ${index + 1}'),
                  subtitle: Text('包含更多详细信息和操作选项'),
                  trailing: Chip(
                    label: Text('NEW'),
                    backgroundColor: Colors.blue[50],
                  ),
                ),
              );
            },
            childCount: 20,
          ),
        ),
      ],
    );
  }
}

3. 高性能无限列表

import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';

class InfiniteSliverList extends StatefulWidget {
  
  _InfiniteSliverListState createState() => _InfiniteSliverListState();
}

class _InfiniteSliverListState extends State<InfiniteSliverList> {
  final List<String> _items = [];
  final ScrollController _scrollController = ScrollController();
  bool _isLoading = false;
  int _currentPage = 0;

  
  void initState() {
    super.initState();
    _loadMoreItems();
    _scrollController.addListener(_scrollListener);
  }

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

  void _scrollListener() {
    if (_scrollController.position.pixels ==
        _scrollController.position.maxScrollExtent) {
      _loadMoreItems();
    }
  }

  Future<void> _loadMoreItems() async {
    if (_isLoading) return;
    
    setState(() => _isLoading = true);
    
    // 模拟网络请求延迟
    await Future.delayed(Duration(seconds: 1));
    
    final newItems = List.generate(20, (i) => '项目 ${_items.length + i + 1}');
    
    setState(() {
      _items.addAll(newItems);
      _isLoading = false;
      _currentPage++;
    });
  }

  
  Widget build(BuildContext context) {
    return CustomScrollView(
      controller: _scrollController,
      slivers: <Widget>[
        SliverAppBar(
          title: Text('无限滚动列表'),
          pinned: true,
        ),
        
        SliverList(
          delegate: SliverChildBuilderDelegate(
            (context, index) {
              if (index < _items.length) {
                return ListTile(
                  title: Text(_items[index]),
                  leading: CircleAvatar(
                    child: Text('${index + 1}'),
                  ),
                );
              } else if (index == _items.length && _isLoading) {
                return Center(child: CircularProgressIndicator());
              } else {
                return SizedBox.shrink();
              }
            },
            childCount: _items.length + (_isLoading ? 1 : 0),
          ),
        ),
      ],
    );
  }
}

注意事项

常见问题

  1. 性能问题:虽然SliverList是懒加载的,但复杂的子项构建仍会影响性能
  2. 内存泄漏:忘记在StatefulWidget中释放ScrollController
  3. 布局错误:不正确的SliverChildBuilderDelegate配置导致列表项重复或缺失
  4. 滚动冲突:多个滚动控制器同时使用时可能产生冲突

优化技巧

  1. 使用const构造函数:尽可能为列表项使用const构造函数
  2. 合理使用Key:为动态列表项提供合适的Key以提高性能
  3. 避免重建:使用AutomaticKeepAliveClientMixin保持列表项状态
  4. 图片优化:对网络图片使用cached_network_image等优化库

最佳实践

  1. 预估高度:为SliverChildBuilderDelegate提供estimateChildExtent以提高性能
  2. 分离构建逻辑:将复杂的列表项构建逻辑提取到单独的Widget
  3. 错误边界:为异步加载提供错误处理和重试机制
  4. 空状态处理:为空的列表提供友好的用户界面

构造函数

//默认的构造函数
SliverList.new({
  Key? key, 
  required SliverChildDelegate delegate
})


//使用itemBuilder来构建子项
SliverList.builder({
  Key? key, 
  required NullableIndexedWidgetBuilder itemBuilder, 
  ChildIndexGetter? findChildIndexCallback, 
  int? itemCount, 
  bool addAutomaticKeepAlives = true, 
  bool addRepaintBoundaries = true, 
  bool addSemanticIndexes = true
})

//使用固定子项的构造函数
SliverList.list({
  Key? key, 
  required List<Widget> children, 
  bool addAutomaticKeepAlives = true, 
  bool addRepaintBoundaries = true, 
  bool addSemanticIndexes = true
})

//可以添加分割线的构造函数
SliverList.separated({
  Key? key, 
  required NullableIndexedWidgetBuilder itemBuilder, 
  ChildIndexGetter? findChildIndexCallback, 
  required NullableIndexedWidgetBuilder separatorBuilder, 
  int? itemCount, 
  bool addAutomaticKeepAlives = true, 
  bool addRepaintBoundaries = true, 
  bool addSemanticIndexes = true
})

属性

属性名类型说明
delegateSliverChildDelegate负责创建和估计子项大小的委托对象
keyKey?组件的键,用于控制组件的唯一性

SliverChildBuilderDelegate关键属性

属性名类型说明
builderNullableIndexedWidgetBuilder构建子项的回调函数,接收上下文和索引
childCountint?子项总数,null 表示无限列表
addAutomaticKeepAlivesbool自动管理子项的生命周期状态
addRepaintBoundariesbool为每个子项添加重绘边界,提高性能
addSemanticIndexesbool为每个子项添加语义索引,辅助功能
estimateChildExtentdouble?预估子项高度,优化滚动性能

关键属性介绍

  1. delegate
  • 重要性: 核心属性,决定了列表的行为和性能
  • 性能影响: 使用SliverChildBuilderDelegate可实现懒加载,大幅提升长列表性能
  • 使用建议: 对于固定数量的列表,提供准确的childCount;对于动态列表,使用适当的加载策略
  1. estimateChildExtent
  • 重要性: 高级优化属性
  • 性能影响: 提供准确的预估高度可以显著改善滚动性能,特别是对于高度可变的项目
  • 使用建议: 如果所有项目高度相近,提供平均高度;如果高度差异大,考虑使用SliverPrototypeExtentList
  1. addRepaintBoundaries
  • 重要性: 性能优化属性
  • 性能影响: 设置为true可以避免不必要的重绘,但会增加内存使用
  • 使用建议: 对于简单项目可以设置为false,复杂项目建议保持默认的true
  1. addAutomaticKeepAlives
  • 重要性: 状态管理属性
  • 性能影响: 保持项目状态可以避免重建开销,但会增加内存占用