Flutter 18: 图解 ListView 下拉刷新与上拉加载 (二)【NotificationListener】

简介: 0 基础学习 Flutter,第十八步:ListView 上拉加载更多与下拉刷新,解决方案二!

      小菜上次尝试 ListView 异步加载列表数据时,用了三方库 flutter_refresh,这种方式使用很简单。但列表数据的加载也绝非一种,小菜这次准备用原生尝试一下。因为种种原因,小菜这次的整理距离上次时间很长,还是应该加强自控力。
      小菜这次的列表并没有单独处理动画效果,只是对数据的刷新与加载更多进行正常加载进行处理,还需要进一步的学习研究。

ListView + NotificationListener

      小菜参考了很多大神的实现方式,发现 NotificationListener 很像 Android 的滑动监听事件,再顶部和底部添加事件处理,集成方式也很简单。

@override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text('新闻列表'),
        elevation: 0.0, // 阴影高度
      ),
      body: new NotificationListener(
        onNotification: dataNotification,
        child: childWidget(),
      ),
    );
  }

问题小结

一:如何区分列表滑动到顶部或底部?

      NotificationListener 中可以根据如下状态进行判断,并在相应的状态下进行需要的处理:

  1. (notification.metrics.extentAfter == 0.0) 为滑动到 底部
  2. (notification.metrics.extentBefore == 0.0) 为滑动到 顶部
bool dataNotification(ScrollNotification notification) {
  if (notification is ScrollEndNotification) {
    //下滑到最底部
    if (notification.metrics.extentAfter == 0.0) {
      print('======下滑到最底部======');
      loadData();
    }
    //滑动到最顶部
    if (notification.metrics.extentBefore == 0.0) {
      print('======滑动到最顶部======');
      lastFileID = '0';
      rowNumber = 0;
      dataItems.clear();
      loadData();
    }
  }
  return true;
}
二:监听的整个过程滑动中多次调用接口?

      小菜在测试过程中每次滑动一下列表都会调用一次接口,因为在监听过程中若不做任何处理只要列表滑动便会进行监听,小菜的解决的方式有两种;

  1. 监听滑动到底部再进行业务操作调用接口,如问题一中的判断;
bool dataNotification(ScrollNotification notification) {
  if (notification is ScrollEndNotification) {
    //下滑到最底部
    if (notification.metrics.extentAfter == 0.0) {
      print('======下滑到最底部======');
      loadData();
    }
  }
  return true;
}
  1. 尝试使用 TrackingScrollController,对滑动进行监听,这个类可用于同步两个或更多个共享单个 TrackingScrollController 的惰性创建的滚动视图的滚动偏移。它跟踪最近更新的滚动位置,并将其报告为其初始滚动偏移量。且在非底部时 maxScrollExtent 和 offset 值会相等。使用该类监听时更灵活,有些操作并非到底部才会进行处理等。
bool dataNotification(ScrollNotification notification) {
  if (notification is ScrollUpdateNotification) {
    if (_scrollController.mostRecentlyUpdatedPosition.maxScrollExtent >
            _scrollController.offset &&
        _scrollController.mostRecentlyUpdatedPosition.maxScrollExtent -
                _scrollController.offset <= 50) {
        loadData();
    }
  }
  return true;
}
三:异常情况处理?

      小菜以前对列表的处理只包括列表数据为 0 时展示 Loading 等待页,有数据时展示数据列表,但是对于其他异常情况没有处理,这次特意添加上异常页面,这仅仅是业务方面的添加,没有新的技术点。

主要源码

class LoadMoreState extends State<LoadMorePage> {
  var lastFileID = '0';
  var rowNumber = 0;
  var cid = '29338';
  var newsListBean = null;
  List<ListBean> dataItems = <ListBean>[];

  @override
  void initState() {
    super.initState();
    loadData();
  }

  // 请求首页数据
  Future<Null> loadData() {
    this.isLoading = true;
    final Completer<Null> completer = new Completer<Null>();
    getNewsData();
    completer.complete(null);
    return completer.future;
  }

  getNewsData() async {
    await http
        .get(
            'https://XXX...&lastFileID=${lastFileID}&rowNumber=${rowNumber}')
        .then((response) {
      if (response.statusCode == 200) {
        var jsonRes = json.decode(response.body);
        newsListBean = NewsListBean(jsonRes);
        setState(() {
          if (newsListBean != null && newsListBean.list != null && newsListBean.list.length > 0) {
            for (int i = 0; i < newsListBean.list.length; i++) {
              dataItems.add(newsListBean.list[i]);
            }
            lastFileID = newsListBean.list[newsListBean.list.length - 1].fileID.toString();
            rowNumber += newsListBean.list.length;
          } else {}
        });
      }
    });
  }

  bool dataNotification(ScrollNotification notification) {
    if (notification is ScrollEndNotification) {
      //下滑到最底部
      if (notification.metrics.extentAfter == 0.0) {
        print('======下滑到最底部======');
        loadData();
      }
      //滑动到最顶部
      if (notification.metrics.extentBefore == 0.0) {
        print('======滑动到最顶部======');
        lastFileID = '0';
        rowNumber = 0;
        dataItems.clear();
        loadData();
      }
    }
    return true;
  }

  // 处理列表中是否有数据,对应展示相应页面
  Widget childWidget() {
    Widget childWidget;
    if (newsListBean != null &&
        (newsListBean.success != null && !newsListBean.success)) {
      childWidget = new Stack(
        children: <Widget>[
          new Padding(
            padding: new EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 100.0),
            child: new Center(
              child: Image.asset( 'images/icon_wrong.jpg', width: 120.0, height: 120.0, ),
            ),
          ),
          new Padding(
            padding: new EdgeInsets.fromLTRB(0.0, 100.0, 0.0, 0.0),
            child: new Center(
              child: new Text( '抱歉!暂无内容哦~', style: new TextStyle(fontSize: 18.0, color: Colors.blue), ),
            ),
          ),
        ],
      );
    } else if (dataItems != null && dataItems.length != 0) {
      childWidget = new Padding(
        padding: EdgeInsets.all(2.0),
        child: new ListView.builder(
            controller: _scrollController,
            physics: const AlwaysScrollableScrollPhysics(),
            padding: const EdgeInsets.all(6.0),
            itemCount: dataItems == null ? 0 : dataItems.length,
            itemBuilder: (context, item) {
               return buildListData(context, dataItems[item]);
            }),);
    } else {
      childWidget = new Center(
        child: new Card(
          child: new Stack(
            children: <Widget>[
              new Padding(
                padding: new EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 35.0),
                child: new Center( child: SpinKitFadingCircle( color: Colors.blueAccent, size: 30.0, ), ),
              ),
              new Padding(
                padding: new EdgeInsets.fromLTRB(0.0, 35.0, 0.0, 0.0),
                child: new Center(  child: new Text('正在加载中,莫着急哦~'), ),
              ),
            ])),);
    }
    return childWidget;
  }
}

      小菜刚接触 Flutter 时间不长,还有很多不清楚和不理解的地方,如果又不对的地方还希望多多指出。

目录
相关文章
|
4月前
Flutter 小技巧之 ListView 和 PageView 的各种花式嵌套
Flutter 小技巧之 ListView 和 PageView 的各种花式嵌套 在 Flutter 中,ListView 和 PageView 是两个常用的控件,它们可以用于滑动展示大量内容的场景,且支持各种嵌套方式,本文将介绍其中的一些花式嵌套方式。
Flutter ListView懒加载(滑动不加载,停止滑动加载)
前言:为了更好的减小网络的带宽,使得列表更加流畅,我们需要了解懒加载,也称延迟加载。关于上一章的登录界面,各位属实难为我了,我也在求ui小姐姐,各位点点赞给我点动力吧~
Flutter ScrollView嵌套ListView滑动冲突
Flutter ScrollView嵌套ListView滑动冲突
1082 0
|
3月前
Flutter.源码分析.flutter/packages/flutter/lib/src/widgets/scroll_view.dart/ListView
Flutter.源码分析.flutter/packages/flutter/lib/src/widgets/scroll_view.dart/ListView
28 0
flutter系列之:flutter中listview的高级用法
一般情况下,我们使用Listview的方式是构建要展示的item,然后将这些item传入ListView的构造函数即可,通常情况下这样做是够用了,但是不排除我们会有一些其他的特殊需求。 今天我们会来讲解一下ListView的一些高级用法。
|
缓存
【布局 widget】Flutter ListView
ListView 是最常用的滚动 widget,也是布局 widget。它在滚动方向上一个接一个地显示它的 child。
207 0
【布局 widget】Flutter ListView
|
Java Android开发 iOS开发
Flutter(六)——多子元素组件:ListView,Scaffold,AppBar,Row,Column
Flutter(六)——多子元素组件:ListView,Scaffold,AppBar,Row,Column
249 1
Flutter(六)——多子元素组件:ListView,Scaffold,AppBar,Row,Column
flutter系列之:flutter中常用的ListView layout详解
ListView是包含多个child组件的widget,在ListView中所有的child widget都是以list的形式来呈现的,你可以自定义List的方向,但是和GridView不同的是ListView中的每一个List里面都只包含一个widget。 今天我们来详细了解一下ListView的底层实现和具体的应用。
flutter系列之:flutter中常用的ListView layout详解
|
前端开发
Flutter 之列表下拉刷新和上拉加载
在实际的 App 中,下拉刷新和上滑加载更多是非常常见的交互形式。在 Flutter 中,有 flutter_easyrefresh开源插件用于实现下拉刷新和上滑加载更多。本篇介绍了有状态组件和 flutter_easyrefresh 的基本应用,同时使用模拟的方式完成了异步数据加载。
605 0
Flutter 之列表下拉刷新和上拉加载