Flutter 17: 圖解 ListView 下拉刷新與上拉加載 (二)【NotificationListener】

??????小菜上次嘗試 ListView 異步加載列表數(shù)據(jù)時,用了三方庫 flutter_refresh,這種方式使用很簡單。但列表數(shù)據(jù)的加載也絕非一種,小菜這次準備用原生嘗試一下。因為種種原因,小菜這次的整理距離上次時間很長,還是應該加強自控力。
??????小菜這次的列表并沒有單獨處理動畫效果,只是對數(shù)據(jù)的刷新與加載更多進行正常加載進行處理,還需要進一步的學習研究。

ListView + NotificationListener

??????小菜參考了很多大神的實現(xiàn)方式,發(fā)現(xiàn) NotificationListener 很像 Android 的滑動監(jiān)聽事件,再頂部和底部添加事件處理,集成方式也很簡單。

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

問題小結(jié)

一:如何區(qū)分列表滑動到頂部或底部?

??????NotificationListener 中可以根據(jù)如下狀態(tài)進行判斷,并在相應的狀態(tài)下進行需要的處理:

  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;
}
二:監(jiān)聽的整個過程滑動中多次調(diào)用接口?

??????小菜在測試過程中每次滑動一下列表都會調(diào)用一次接口,因為在監(jiān)聽過程中若不做任何處理只要列表滑動便會進行監(jiān)聽,小菜的解決的方式有兩種;

  1. 監(jiān)聽滑動到底部再進行業(yè)務操作調(diào)用接口,如問題一中的判斷;
bool dataNotification(ScrollNotification notification) {
  if (notification is ScrollEndNotification) {
    //下滑到最底部
    if (notification.metrics.extentAfter == 0.0) {
      print('======下滑到最底部======');
      loadData();
    }
  }
  return true;
}
  1. 嘗試使用 TrackingScrollController,對滑動進行監(jiān)聽,這個類可用于同步兩個或更多個共享單個 TrackingScrollController 的惰性創(chuàng)建的滾動視圖的滾動偏移。它跟蹤最近更新的滾動位置,并將其報告為其初始滾動偏移量。且在非底部時 maxScrollExtent 和 offset 值會相等。使用該類監(jiān)聽時更靈活,有些操作并非到底部才會進行處理等。
bool dataNotification(ScrollNotification notification) {
  if (notification is ScrollUpdateNotification) {
    if (_scrollController.mostRecentlyUpdatedPosition.maxScrollExtent >
            _scrollController.offset &&
        _scrollController.mostRecentlyUpdatedPosition.maxScrollExtent -
                _scrollController.offset <= 50) {
        loadData();
    }
  }
  return true;
}
三:異常情況處理?

??????小菜以前對列表的處理只包括列表數(shù)據(jù)為 0 時展示 Loading 等待頁,有數(shù)據(jù)時展示數(shù)據(jù)列表,但是對于其他異常情況沒有處理,這次特意添加上異常頁面,這僅僅是業(yè)務方面的添加,沒有新的技術點。

主要源碼

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();
  }

  // 請求首頁數(shù)據(jù)
  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;
  }

  // 處理列表中是否有數(shù)據(jù),對應展示相應頁面
  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( '抱歉!暫無內(nèi)容哦~', 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 時間不長,還有很多不清楚和不理解的地方,如果又不對的地方還希望多多指出。

來源:阿策小和尚

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • 1、通過CocoaPods安裝項目名稱項目信息 AFNetworking網(wǎng)絡請求組件 FMDB本地數(shù)據(jù)庫組件 SD...
    陽明AI閱讀 16,228評論 3 119
  • 小菜上次學 ListView 時,只學習了一下異步請求數(shù)據(jù)加載新聞和 Loading 等待的小知識點,但對于新聞列...
    阿策神奇閱讀 5,124評論 2 14
  • 我坐在地上的時候覺得耳邊還有風聲,兩眼木呆呆的腦子直接當機了,因為我發(fā)現(xiàn)自己站不起來了。接著眼淚直接流出來都...
    MAXWELL96閱讀 122評論 0 0
  • 今天一些老同事聚會,其中一個同事組織一堆小朋友們表演節(jié)目,有表演節(jié)目就可以獎勵薯條吃,不知道原來薯條的魅力原來有這...
    云沐媽媽閱讀 119評論 0 0
  • 來打針了,嗓子疼,疼了六天了,疼的讓我有點抓狂。 一天前還是這個門診,當問我拿藥還是打針時,我猶豫的說拿藥吧。一是...
    假如愛若心飛閱讀 1,057評論 0 0

友情鏈接更多精彩內(nèi)容