Flutter 06:listView实现下拉刷新,上拉加载

1,885 阅读2分钟

基本信息

ListView({
    Key key, 
    Axis scrollDirection: Axis.vertical, //滚动方向
    bool reverse: false,  //是否翻转数据
    ScrollController controller, //控制器
    bool primary,
    ScrollPhysics physics, 
    bool shrinkWrap: false, 
    EdgeInsetsGeometry padding, 
    double itemExtent, 
    bool addAutomaticKeepAlives: true, 
    bool addRepaintBoundaries: true, 
    bool addSemanticIndexes: true, 
    double cacheExtent, 
    List<Widget> children: const [], 
    int semanticChildCount, 
    DragStartBehavior dragStartBehavior: DragStartBehavior.start
})

在创建ListView.builder时,需要传入两个参数,一个列表的初始长度,一个itemBuilder函数。ListVIew还支持基于Sliver的延迟构建模型。

基于Sliver的延迟构建模式:通常可滚动组件的子组件可能会非常多,占用的总高度也会非常大,如果要一次性将子组件全部构建出将会导致性能差的问题出现,为此,Flutter中提出一个Sliver(中文为"薄片"的意思)概念,如果一个可滚动组件支持Sliver模型,那么该滚动组件可以将子组件分成好多个薄片(Sliver),只有当Sliver出现在视口时才会去构建它,这种模型也成为"基于Sliver的延迟构建模型"。可滚动组件中有很多都支持基于Sliver的延迟构建模型,如ListView、GridView,但是也有不支持该模型的,如SingleChildScrollView

下拉刷新指示器

RefreshIndicator({
    Key key, 
    @required Widget child, //宽度
    double displacement: 40.0, //高度
    @required RefreshCallback onRefresh, //刷新回调
    Color color, //圆圈颜色
    Color backgroundColor, //背景颜色
    ScrollNotificationPredicate notificationPredicate: defaultScrollNotificationPredicate, 
    String semanticsLabel,  语义文本
    String semanticsValue
})

下拉刷新和上拉加载

import 'package:flutter/material.dart';

class TestList extends StatefulWidget {
  @override
  _TestListState createState() => _TestListState();
}

class _TestListState extends State<TestList> {
  final List<String> list = [];
  int page = 0;
  ScrollController _controller = ScrollController();

  @override
  void initState() {
    _getData();
    _controller.addListener((){ //当前位置==最大滑动范围,滑到底了
      if(_controller.position.pixels == _controller.position.maxScrollExtent){
        if(page < 3){
          _retrieveData();
        }
      }
    });
    super.initState();
  }
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '轻松学',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        appBar: AppBar(title: Text('测试')),
        body: RefreshIndicator(
          onRefresh: _onRefresh, //下拉刷新回调
          color: Colors.red,
          //backgroundColor: Colors.blue,
          displacement: 40, //指示器显示时距顶部位置
          child: ListView.separated(
            controller: _controller,
            physics: BouncingScrollPhysics(),//列表滑动到边界时,显示iOS的弹出效果
            itemBuilder: (BuildContext context, int i){ //列表构造器

              if(i == list.length){
                if(page < 3){
                  return Container(
                    padding: EdgeInsets.only(top: 10, bottom: 10),
                    alignment: Alignment.center,
                    child: SizedBox(
                      child: CircularProgressIndicator(strokeWidth: 2,),
                      height: 20,
                      width: 20,
                    )
                  );
                } else {
                  return Container(
                    padding: EdgeInsets.only(top: 10, bottom: 10),
                     alignment: Alignment.center,
                    child: Text(
                      '~我是底线~', 
                      textAlign: TextAlign.center,
                      style: TextStyle(
                        color: Colors.blue,
                      )
                    ),
                  );
                }

              } else {
                return ListTile(
                  title: Text('${list[i]}'),
                  leading: Icon(Icons.home),
                );
              }
            }, 
            separatorBuilder:(BuildContext context, int i){
              return Divider(color: Colors.blue);
            }, 
            itemCount: list.length+1 // 注意多加一个,loading的位置
          )
        )
      ),
    );
  }

  void _getData(){
    for(var i= 0; i < 20; i++){
      list.insert(i, "第${list.length}条原始数据");
    }
  }

  Future<void> _onRefresh()async{
    await Future.delayed(Duration(seconds: 2)).then((e){ //
      setState(() {
        page = 0;
        list.clear();
        for(var i= 0; i < 20; i++){
          list.insert(i, "第${list.length}条原始数据");
        }
      });
    });
  }
  Future<void> _retrieveData()async{
    await Future.delayed(Duration(seconds: 2)).then((e){ //
      setState(() {
        page++;
        for(var i= 0; i < 20; i++){
          list.insert(list.length, "第${list.length}条原始数据");
        }
      });
    });
  }


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

下拉刷新,使用RefreshIndicator组件在回调onRefresh中实现,上拉加载使用ScrollController去判断滚动条位置

注意: itemCount多加一个,给loading占给位置。

嵌套的 ListView 无法滚动的问题

可滑动控件都有这两个属性,给子滑动部件设置上这两个参数,即可办到子ListView跟随父ListView滑动

shrinkWrap: true, //解决无限高度问题 physics:NeverScrollableScrollPhysics(),//禁用滑动事件

参考链接

Flutter学习笔记(25)--ListView实现上拉刷新下拉加载

Flutter 下拉刷新之RefreshIndicator