Flutter 20: 圖解 ListView 下拉刷新與上拉加載 (三)【RefreshIndicator】

??????小菜前段時(shí)間整理了兩種 ListView 的異步加載數(shù)據(jù)時(shí)焕檬,下拉刷新與上滑加載更多的方式,每種方式都有自己的優(yōu)勢(shì)澳泵,網(wǎng)上也有很多大神講解過(guò) ListView 數(shù)據(jù)流的種種處理方式实愚,小菜根據(jù)實(shí)際遇到的情況整理一下嘗試的第三種方案。

RefreshIndicator 下拉刷新

??????Flutter 提供了自帶刷新效果的 RefreshIndicator兔辅,這也是網(wǎng)上大神們用的最多的 Widget 之一腊敲,使用方式也很簡(jiǎn)單,RefreshIndicator 中提供了一個(gè)刷新的回調(diào)入口 onRefresh维苔,僅需在該回調(diào)接口中處理數(shù)據(jù)請(qǐng)求即可碰辅,如下:

// 刷新時(shí)數(shù)據(jù)請(qǐng)求
Future<Null> _loadRefresh() async {
  await Future.delayed(Duration(seconds: 2), () {
    setState(() {
      dataItems.clear();
      lastFileID = '0';
      rowNumber = 0;
      _getNewsData(lastFileID, rowNumber);
      return null;
    });
  });
}

// 請(qǐng)求接口整合數(shù)據(jù)
_getNewsData(var lastID, var rowNum) async {
  await http
      .get(
      'https://XXX.../getArticles?...&lastFileID=${lastID}&rowNumber=${rowNum}')
      .then((response) {
    if (response.statusCode == 200) {
      var jsonRes = json.decode(response.body);
      newsListBean = NewsListBean(jsonRes);
      if (lastID == '0' && rowNum == 0 && dataItems != null) {
        dataItems.clear();
      }
      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 {}
      });
    }
  });
}

// 綁定列表數(shù)據(jù)
@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: Text("第三種加載方式"),
    ),
    body: new RefreshIndicator(
      child: ListView.builder(
        itemCount: items.length,
        itemBuilder: buildListData(context, dataItems[index])
      ),
      onRefresh: _loadRefresh,  // 刷新回調(diào)
    ));
}

ScrollController 上滑動(dòng)加載更多

??????至此,列表的下拉刷新就完成了介时,接下來(lái)處理【上滑加載更多】没宾,這時(shí)我們可以借助 ScrollController,用來(lái)監(jiān)聽列表是否滑動(dòng)到底部沸柔,主要分兩步:

  1. 初始化時(shí)添加監(jiān)聽事件,判斷是否滑動(dòng)到最底部会钝;
  2. ListView 中添加監(jiān)聽方法乱凿。
ScrollController _scrollController = new ScrollController();

@override
void initState() {
  super.initState();
  _scrollController.addListener(() {
    if (_scrollController.position.pixels ==
        _scrollController.position.maxScrollExtent) {
      _getMoreData();  // 當(dāng)滑到最底部時(shí)調(diào)用
    }
  });
  _getMoreData();  // 數(shù)據(jù)初始化
}

@override
Widget build(BuildContext context) {
  return Scaffold(
      appBar: AppBar(
        title: Text("第三種加載方式"),
      ),
      body: ListView.builder(
        itemCount: items.length,
        itemBuilder: buildListData(context, dataItems[index]),
        controller: _scrollController,
      ));
}

??????至此顽素,列表的下拉刷新與上滑加載更多就基本完成了;接下來(lái)需要將兩種合并使用徒蟆,也很簡(jiǎn)單,如下:

body: new Padding(
  padding: EdgeInsets.all(2.0),
  child: RefreshIndicator(
      onRefresh: _loadRefresh,
      child: ListView.builder(
        itemCount: dataItems.length,
        physics: const AlwaysScrollableScrollPhysics(),
        itemBuilder: (context, index) {
           return buildListData(context, dataItems[index]);
        },
        controller: _scrollController,
      )));

??????Tips: 注意處理好數(shù)據(jù)接口請(qǐng)求內(nèi)容全蝶。

小優(yōu)化

優(yōu)化一:【上滑加載更多】添加動(dòng)畫效果
  1. 添加一個(gè)加載更多的布局 Widget
  2. itemCount 中將 item 個(gè)數(shù) +1寺枉;
  3. 添加監(jiān)聽判斷抑淫,當(dāng)滑到最后一個(gè) item 時(shí)展示加載更多到布局 Widget
body: new Padding(
  padding: EdgeInsets.all(2.0),
  child: RefreshIndicator(
      onRefresh: _loadRefresh,
      child: ListView.builder(
        itemCount: dataItems.length + 1,
        physics: const AlwaysScrollableScrollPhysics(),
        itemBuilder: (context, index) {
          if (index == dataItems.length) {
            return _buildProgressIndicator();
          } else {
            return buildListData(context, dataItems[index]);
          }
        },
        controller: _scrollController,
      )));

// 加載更多 Widget
Widget _buildProgressIndicator() {
  return new Padding(
      padding: EdgeInsets.fromLTRB(0.0, 14.0, 0.0, 14.0),
      child: new Opacity(
          opacity: isShowLoading ? 1.0 : 0.0,
          child: new Row(
            mainAxisAlignment: MainAxisAlignment.center,
            mainAxisSize: MainAxisSize.max,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: <Widget>[
              new SpinKitChasingDots(color: Colors.blueAccent, size: 26.0),
              new Padding(
                  padding: EdgeInsets.fromLTRB(10.0, 0.0, 0.0, 0.0),
                  child: new Text('正在加載中...'))
            ],
          )));
}
優(yōu)化二:第一次初始化加載數(shù)據(jù)時(shí)添加 loading 動(dòng)畫

??????RefreshIndicator 中自帶刷新的動(dòng)畫始苇,所以小菜只是在第一次加載數(shù)據(jù)時(shí)添加一個(gè) loading 動(dòng)畫筐喳,小菜只是填了一個(gè)小小的狀態(tài)判斷函喉,如下包括異常情況下的失敗頁(yè)荣月。

Widget childWidget() {
  Widget childWidget;
  if (newsListBean != null &&
      (newsListBean.success != null && !newsListBean.success)) {
    isFirstLoading = false;
    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(
            '抱歉!暫無(wú)內(nèi)容哦~',
            style: new TextStyle(fontSize: 18.0, color: Colors.blue),
          )))
    ]);
  } else if (dataItems != null && dataItems.length != 0) {
    isFirstLoading = false;
    childWidget = new Padding(
        padding: EdgeInsets.all(2.0),
        child: RefreshIndicator(
            onRefresh: _loadRefresh,
            child: ListView.builder(
              itemCount: dataItems.length + 1,
              physics: const AlwaysScrollableScrollPhysics(),
              itemBuilder: (context, index) {
                if (index == dataItems.length) {
                  return _buildProgressIndicator();
                } else {
                  return buildListData(context, dataItems[index]);
                }
              },
              controller: _scrollController,
            )));
  } else {
    if (isFirstLoading) {  // 只有在第一次加載數(shù)據(jù)時(shí)才會(huì)展示自定義 loading
      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('正在加載中捐下,莫著急哦~'),
              ))
        ])),
      );
    } else {}
  }
  return childWidget;
}
優(yōu)化三:借助 Future.delayed() 進(jìn)行延遲加載坷襟,使數(shù)據(jù)請(qǐng)求銜接性更好咽白。
_getMoreData() async {
  if (!isShowLoading) {
    setState(() {
      isShowLoading = true;
    });
    await Future.delayed(Duration(seconds: 2), () {
      setState(() {
        _getNewsData(lastFileID, rowNumber);
        isShowLoading = false;
        return null;
      });
    });
  }
}

??????小菜剛接觸 Flutter 時(shí)間不長(zhǎng),還有很多不清楚和不理解的地方晶框,如果有不對(duì)的地方還希望多多指教。

來(lái)源:阿策小和尚

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蹲蒲,一起剝皮案震驚了整個(gè)濱河市侵贵,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌卡睦,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,744評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件漱抓,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡瞬逊,警方通過(guò)查閱死者的電腦和手機(jī)仪或,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)蕾域,“玉大人到旦,你說(shuō)我怎么就攤上這事廓块∑跄” “怎么了昔汉?”我有些...
    開封第一講書人閱讀 163,105評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)会通。 經(jīng)常有香客問我娄周,道長(zhǎng)涕侈,這世上最難降的妖魔是什么煤辨? 我笑而不...
    開封第一講書人閱讀 58,242評(píng)論 1 292
  • 正文 為了忘掉前任众辨,我火速辦了婚禮,結(jié)果婚禮上鹃彻,老公的妹妹穿的比我還像新娘。我一直安慰自己团赁,他們只是感情好谨履,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,269評(píng)論 6 389
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著剧浸,像睡著了一般矗钟。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上吨艇,一...
    開封第一講書人閱讀 51,215評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音冯吓,去河邊找鬼。 笑死组贺,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的啊奄。 我是一名探鬼主播掀潮,決...
    沈念sama閱讀 40,096評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼庄新!你這毒婦竟也來(lái)了薯鼠?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,939評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤人断,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后涩金,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體暇仲,經(jīng)...
    沈念sama閱讀 45,354評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,573評(píng)論 2 333
  • 正文 我和宋清朗相戀三年全度,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了斥滤。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,745評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡顶掉,死狀恐怖挑胸,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤移袍,帶...
    沈念sama閱讀 35,448評(píng)論 5 344
  • 正文 年R本政府宣布老充,位于F島的核電站,受9級(jí)特大地震影響啡浊,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,048評(píng)論 3 327
  • 文/蒙蒙 一涂籽、第九天 我趴在偏房一處隱蔽的房頂上張望砸抛。 院中可真熱鬧评雌,春花似錦直焙、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,683評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至蜕煌,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間贫母,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,838評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工腺劣, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留伪冰,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,776評(píng)論 2 369
  • 正文 我出身青樓靠柑,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親歼冰。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,652評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容