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


    如需转载,请注明出处:Flutter学习笔记(25)--ListView实现上拉刷新下拉加载

     

    前面我们有写过ListView的使用:Flutter学习笔记(12)--列表组件,当列表的数据非常多时,需要使用长列表,比如淘宝后台的订单列表,手机通讯录等,这些列表项数据很多,长列表也是使用ListView作为基础组件,只不过需要添加一个列表项构造器itemBuilder。Flutter的长列表组件其实相当于Android中的RecyclerView,它会自动为您回收列表元素。在创建ListView.builder时,需要传入两个参数,一个列表的初始长度,一个itemBuilder函数。ListVIew还支持基于Sliver的延迟构建模型。

    基于Sliver的延迟构建模式:

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

    使用ListVIew.separated给列表项之间添加一个分割组件

    import 'package:flutter/material.dart';
    
    void main() => runApp(DemoApp());
    
    class DemoApp extends StatelessWidget {
    
      //初始化数据源
      final List<String> items = new List<String>.generate(200, (i)=>"Item $i");
    
      @override
      Widget build(BuildContext context) {
        return new MaterialApp(
          title: 'SingleChildScrollView Demo',
          home: new Scaffold(
            appBar: AppBar(
              title: new Text('SingleChildScrollView Demo'),
            ),
            body:new ListView.separated(
              //列表滑动到边界时,显示iOS的弹出效果
              physics: BouncingScrollPhysics(),
              itemCount: items.length,
              //列表项构造器
              itemBuilder: (context,index){
                return ListTile(title: new Text('${items[index]}'),);
              },
              //分割构造器
              separatorBuilder: (context,index){
                //分割组件
                return new Divider(color: Colors.blue,);
              },
            ),
          ),
        );
      }
    }

    • 下拉刷新

     Flutter给我们提供了下拉刷新功能RefreshIndicator的组件,先整体说明一下下面Demo的代码逻辑,其实很简单,body返回一个RefreshIndicator组件,在该组件内的子组件是一个ListView,重点说一下RefreshIndicator的下拉回调方法onRefresh,在回调方法内延迟2秒中后将list内容清空,并且重新给list列表添加新的数据。

    import 'package:flutter/material.dart';
    
    void main() => runApp(DemoApp());
    
    class DemoApp extends StatefulWidget {
      @override
      State<StatefulWidget> createState() {
        return new _DemoAppState();
      }
    }
    
    class _DemoAppState extends State<DemoApp> {
      var _items = new List<String>();
      @override
      void initState() {
        super.initState();
        getData();
      }
      @override
      Widget build(BuildContext context) {
        return new MaterialApp(
          title: 'ListView Demo',
          home: new Scaffold(
            appBar: new AppBar(
              title: new Text('ListView Demo'),
            ),
            body: new RefreshIndicator(
                onRefresh: _onRefresh,
                 child: new ListView.separated(
                    physics: BouncingScrollPhysics(),
                    itemBuilder: (context,index){
                      return ListTile(title:new Text('${_items[index]}'));
                    },
                    //分割线构造器
                    separatorBuilder: (context,index){
                      return new Divider(color: Colors.blue,);
                    },
                    //_items.length + 1是为了给最后一行的加载loading留出位置
                    itemCount: _items.length
                ),
            ),
          ),
        );
      }
    
      void getData() {
        //初始数据源
        for (int i=0;i<20;i++){
          _items.insert(_items.length, "第${_items.length}条原始数据");
          print(_items[i]);
        }
      }
    
      Future<void> _onRefresh() async {
        await Future.delayed(Duration(seconds: 2)).then((e){
          setState(() {
            _items.clear();
            for (int i=0;i<20;i++){
              _items.insert(_items.length, "第${_items.length}条下拉刷新后的数据");
            }
          });
        });
      }
    
      @override
      void dispose() {
        super.dispose();
      }
    }

    这里需要注意的是,onRefresh回调方法要增加async....await,不然会出现下拉刷新的loading不会消失的问题:

      Future<void> _onRefresh() async {
        await Future.delayed(Duration(seconds: 2)).then((e){
          setState(() {
            _items.clear();
            for (int i=0;i<20;i++){
              _items.insert(_items.length, "第${_items.length}条下拉刷新后的数据");
            }
          });
        });
      }

     

    • 上拉加载

     先缕一下实现的思路,我们想要实现的效果是每页20条内容,共5页的内容,1-4页末尾数据后要展示加载新数据的loading,到第5页末尾数据展示“我是有底线的”,因此,我们的itemCount就要是itemCount: _items.length + 1.而不是itemCount: _items.length + 1,这是因为要在最后留出来loading的位置,接下来就是要处理构建LisvtView里面的每一条item,如果当前item的索引是列表数据的最后一条数据,并且不是最后一页的话,展示loading,如果当前item的索引是列表数据的最后一条数据,并且是最后一页的话,展示“我是有底线的”,如果当前item的索引不是列表数据的最后一条,则展示下一条数据的内容。最后要处理的就是当页面滑动到最后了,要怎么获取新的数据。前面我们在写页面滑动的部分有讲到过controller属性(此属性接收一个ScrollController对象,ScrollController的主要作用是控制滚动位置和监听滚动事件),我们现在需要做的就是通过ListView的controller控制器来判断页面是否滑动到了最底部,如果滑动到了最底部,则获取新的数据并插入到list里面,最后通过setState通知页面重新构建。

    import 'package:flutter/material.dart';
    
    void main() => runApp(DemoApp());
    
    class DemoApp extends StatefulWidget {
      @override
      State<StatefulWidget> createState() {
        return new _DemoAppState();
      }
    }
    
    class _DemoAppState extends State<DemoApp> {
      ScrollController _controller = new ScrollController();
      var _items = new List<String>();
      var _mPage = 0;
      @override
      void initState() {
        super.initState();
        getData();
        //给_controller添加监听
        _controller.addListener((){
          //判断是否滑动到了页面的最底部
          if(_controller.position.pixels == _controller.position.maxScrollExtent){
            //如果不是最后一页数据,则生成新的数据添加到list里面
            if(_mPage < 4){
              _retrieveData();
            }
          }
        });
      }
      @override
      Widget build(BuildContext context) {
        return new MaterialApp(
          title: 'ListView Demo',
          home: new Scaffold(
            appBar: new AppBar(
              title: new Text('ListView Demo'),
            ),
            body: new RefreshIndicator(
                onRefresh: _onRefresh,
                 child: new ListView.separated(
                    controller: _controller,
                    physics: BouncingScrollPhysics(),
                    itemBuilder: (context,index){
                      //判断是否构建到了最后一条item
                      if(index == _items.length){
                        //判断是不是最后一页
                        if(_mPage < 4){
                          //不是最后一页,返回一个loading窗
                          return new Container(
                            padding: EdgeInsets.all(16.0),
                            alignment: Alignment.center,
                            child: SizedBox(
                               24.0,
                              height: 24.0,
                              child: CircularProgressIndicator(strokeWidth: 2.0,),
                            ),
                          );
                        }else{
                          //是最后一页,显示我是有底线的
                          return new Container(
                            padding: EdgeInsets.all(16.0),
                            alignment: Alignment.center,
                            child: new Text('我是有底线的!!!',style:TextStyle(color: Colors.blue),),
                          );
                        }
                      }else{
                        return ListTile(title:new Text('${_items[index]}'));
                      }
                    },
                    //分割线构造器
                    separatorBuilder: (context,index){
                      return new Divider(color: Colors.blue,);
                    },
                    //_items.length + 1是为了给最后一行的加载loading留出位置
                    itemCount: _items.length + 1
                ),
            ),
          ),
        );
      }
    
      void getData() {
        //初始数据源
        for (int i=0;i<20;i++){
          _items.insert(_items.length, "第${_items.length}条原始数据");
          print(_items[i]);
        }
      }
    
      void _retrieveData() {
        //上拉加载新的数据
        _mPage++;
        Future.delayed(Duration(seconds: 2)).then((e){
          for (int i=0;i<20;i++){
            _items.insert(_items.length, "这是新加载的第${_items.length}条数据");
          }
          setState(() {
          });
        });
      }
    
      Future<void> _onRefresh() async {
        await Future.delayed(Duration(seconds: 2)).then((e){
          setState(() {
            _mPage = 0;
            _items.clear();
            for (int i=0;i<20;i++){
              _items.insert(_items.length, "第${_items.length}条下拉刷新后的数据");
            }
          });
        });
      }
    
      @override
      void dispose() {
        //移除监听,防止内存泄漏
        _controller.dispose();
        super.dispose();
      }
    }

     

    以上就是今天下拉刷新和上拉加载的全部内容了,如果有错误的地方或者有任何疑问,欢迎留言!!!

  • 相关阅读:
    今天入住博客园,希望有个好的开始,自己在这边可以学习成长
    浅谈 C# ref 和 out 的使用方法
    类之间的几种关系
    VB6.0 文件日志读取
    基于NPOI的Excel导入和导出功能
    WebService的创建,发布与调用
    C# OfType 的使用
    Vb6.0 文件日志记录
    ZipInputStream
    [转载]遗传算法入门
  • 原文地址:https://www.cnblogs.com/upwgh/p/11435721.html
Copyright © 2020-2023  润新知