【Flutter 16】圖解 ListView 異步加載數據與 Loading 等待

      和尚前兩天再學 ListView 時,整理了一下在列表中展現多種不一樣 item 樣式,今天繼續深刻學習異步請求數據並加載新聞列表以及初始進入頁面的 loading 等小知識點。暫時尚未學習下拉刷新與上劃加載更多。json

一. 異步請求數據 async + wait

      和尚在前一篇關於網絡請求小博客中整理過基本的異步使用方法;和尚在學習中發現有兩個小地方須要注意一下:微信

  1. 使用 StatefulWidget 時,必定必定不要忘記 setState(() {})網絡

  2. 和尚準備在剛進入頁面時,開啓異步請求數據,能夠在 initState() 中進行操做,以下:app

@override
void initState(
{
    getNewsData();
}

二. json 數據解析

      請求到數據以後必然得須要 json 解析,首先須要引入 import 'dart:convert' show json; 以後,和尚主要是使用 response.body 中數據進行處理,json.decode(response.body); 將 json 轉爲標準的 key-value 格式;最讓和尚頭疼的是實體類轉換,實體類的定義必定要全面且字段格式正確,否則解析出問題不容易定位。(請諒解:和尚的測試 url 沒法公佈)異步

getNewsData() async {
  await http
      .get('https://...?sid=xkycs&cid=${cid}&rowNumber=${rowNumber}')
      .then((response) {
    if (response.statusCode == 200) {
      var jsonRes = json.decode(response.body);
      newsListBean = NewsListBean(jsonRes);
      setState(() {
        for (int i = 0; i < newsListBean.list.length; i++) {
          print("==77==${newsListBean.list[i].title}");
          dataItems.add(newsListBean.list[i]);
        }
      });
    }
  });
}

      和尚單獨爲實體類區分爲一個新的 .dart 文件,須要注意的是,若實體類中有列表,必定要注意判空,以下:async

class NewsListBean {
  List<ListBean> list;
  int rowNumber;
  bool success;
  String msg;

  NewsListBean(jsonRes) {
    rowNumber = jsonRes['rowNumber'];
    success = jsonRes['success'];
    msg = jsonRes['msg'];
    list = [];
    if (jsonRes['list'] != null) {
      for (var dataItem in jsonRes['list']) {
        list.add(new ListBean(dataItem));
      }
    }
  }
}

class ListBean {
  int fileID;
  String title;
  int version;
  String abstractX;
  String publishTime;
  String realPublishTime;
  int articleType;
  String pic3;
  String pic2;
  String pic1;
  int bigPic;
  String tag;
  String contentUrl;
  String videoImgUrl;

  ListBean(jsonRes) {
    fileID = jsonRes['fileID'];
    title = jsonRes['title'];
    version = jsonRes['version'];
    abstractX = jsonRes['abstract'];
    publishTime = jsonRes['publishTime'];
    realPublishTime = jsonRes['realPublishTime'];
    articleType = jsonRes['articleType'];
    pic3 = jsonRes['pic3'];
    pic2 = jsonRes['pic2'];
    pic1 = jsonRes['pic1'];
    bigPic = jsonRes['bigPic'];
    tag = jsonRes['tag'];
    contentUrl = jsonRes['contentUrl'];
    videoImgUrl = jsonRes['videoImgUrl'];
  }
}

三. 列表加載數據

      和尚每次寫 item 時都會想到 Flutter 中一切都是 Widget 的重要性,和尚建議不少公共的或重複的 Widget 徹底能夠提取成統一的 Widget,即方便管理也會大幅度減小代碼量。ide

Widget buildListData(BuildContext context, ListBean listBean{
  Widget itemWidget;
  if (listBean != null) {
    switch (listBean.articleType) {
      case 1:
        itemWidget = new Card(
          child: new Container(
            child: new Column(
              children: <Widget>[
                new Row(
                  children: <Widget>[
                    new Expanded(
                      child: new Container(
                        padding: const EdgeInsets.fromLTRB(3.06.03.00.0),
                        child: new Image.network( listBean.pic1, fit: BoxFit.cover, ),
                        height: 100.0,
                      ),
                      flex: 1,
                    ),
                    new Expanded(
                      child: new Container(
                        padding: const EdgeInsets.fromLTRB(3.06.03.00.0),
                        child: new Image.network( listBean.pic2, fit: BoxFit.cover, ),
                        height: 100.0,
                      ),
                      flex: 1,
                    ),
                    new Expanded(
                      child: new Container(
                        padding: const EdgeInsets.fromLTRB(3.06.03.00.0),
                        child: new Image.network( listBean.pic3, fit: BoxFit.cover, ),
                        height: 100.0,
                      ),
                      flex: 1,
                    ),
                  ],
                ),
                new Padding( padding: new EdgeInsets.fromLTRB(8.06.00.06.0), child: botRow(listBean), ),
              ],
            ),
          ),
        );
        break;
      case 2:
        itemWidget = new Card(
          child: new Column(
            children: <Widget>[
              new Row(
                children: <Widget>[
                  new Expanded(
                    child: new Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      mainAxisSize: MainAxisSize.min,
                      children: <Widget>[
                        new Padding( padding: new EdgeInsets.fromLTRB(8.06.00.03.0), child: new Text(listBean.title), ),
                        new Padding( padding: new EdgeInsets.fromLTRB(8.06.00.03.0), child: new Text( listBean.abstractX, style: new TextStyle(fontSize: 14.0), ), ),
                        new Padding( padding: new EdgeInsets.fromLTRB(8.06.00.03.0), child: botRow(listBean), ),
                      ],
                    ),
                    flex: 2,
                  ),
                  new Expanded(
                    child: new Container(
                      padding: const EdgeInsets.fromLTRB(3.06.03.06.0),
                      child: new Image.network( listBean.pic1, fit: BoxFit.cover, ),
                      height: 100.0,
                    ),
                    flex: 1,
                  ),
                ],
              ),
            ],
          ),
        );
        break;
      default:
        Widget absWi;
        if (listBean.abstractX == null || listBean.abstractX.length == 0) {
          absWi = new Container( width: 0.0, height: 0.0, );
        } else {
          absWi = new Padding( padding: new EdgeInsets.fromLTRB(0.08.00.00.0),child: new Text( listBean.abstractX, style: new TextStyle(fontSize: 14.0), ), );
        }
        itemWidget = new Card(
          child: new Padding(
            padding: new EdgeInsets.all(10.0),
            child: new Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              mainAxisSize: MainAxisSize.min,
              children: <Widget>[
                new Text( listBean.title, style: new TextStyle(fontSize: 17.0), ),
                absWi,
                new Padding( padding: new EdgeInsets.fromLTRB(0.08.00.00.0), child: botRow(listBean), ),
              ],
            ),
          ),
        );
        break;
    }
    return itemWidget;
  }
}

// 底部時間和音視頻顯隱性
Widget botRow(ListBean listBean{
  Widget videoWi;
  if (listBean.videoImgUrl == null || listBean.videoImgUrl.length == 0) {
    videoWi = new Container( width: 0.0, height: 0.0, );
  } else {
    videoWi = new Padding(
      padding: new EdgeInsets.fromLTRB(0.00.06.00.0),
      child: new Row(
        children: <Widget>[
          new Padding(
            padding: new EdgeInsets.fromLTRB(0.02.06.00.0),
            child: new Center( child: new Icon( Icons.queue_music, size: 13.0, color: Colors.blueAccent, ), ),
          ),
          new Text( '音頻', style: new TextStyle(fontSize: 13.0), ),
        ],
      ),
    );
  }
  return new Padding(
    padding: new EdgeInsets.fromLTRB(0.08.00.00.0),
    child: new Row(
      children: <Widget>[
        videoWi,
        new Padding(
          padding: new EdgeInsets.fromLTRB(0.00.06.00.0),
          child: new Icon( Icons.access_time, size: 13.0, color: Colors.blueAccent,),
        ),
        new Text( listBean.publishTime, style: new TextStyle(fontSize: 13.0),),
      ],
    ),
  );
}

      和尚處理成在沒有加載出列表數據以前添加一個 loading 提醒,以下:學習

Widget childWidget({
  Widget childWidget;
  if (dataItems != null && dataItems.length != 0) {
    childWidget = new Padding(
      padding: EdgeInsets.all(6.0),
      child: new ListView.builder(
        itemCount: dataItems.length,
        itemBuilder: (context, item) {
          return buildListData(context, dataItems[item]);
        },
      ),
    );
  } else {
    childWidget = new Stack(
      children: <Widget>[
        new Padding(
          padding: new EdgeInsets.fromLTRB(0.00.00.035.0),
          child: new Center(
            child: SpinKitFadingCircle(
              color: Colors.blueAccent,
              size: 30.0,
            ),
          ),
        ),
        new Padding(
          padding: new EdgeInsets.fromLTRB(0.035.00.00.0),
          child: new Center(
            child: new Text('正在加載中,莫着急哦~'),
          ),
        ),
      ],
    );
  }
  return childWidget;
}

四. loading 提醒

      和尚在加載數據以後發現,網絡情況不佳或數據量大時都應有 loading 提醒,儘可能給用戶一個良好的體驗。
      和尚偷了個懶,借用一個三方庫 flutter_spinkit,這個 loading 庫集成簡單並且效果多樣,基本包含平常中常見的樣式。測試

集成步驟:

  1. pubspec.yaml 中添加 flutter_spinkit: "^2.1.0"flex

  2. 在相應的 .dart 文件中添加引用 import 'package:flutter_spinkit/flutter_spinkit.dart';

  3. 添加須要展現的樣式:SpinKit + Wave() 方式,同時與官網的使用有點區別,官網中用 width 和 height 來設置寬高,可是和尚在測試過程當中,源碼中提供了 size 方法,一個屬性便可。

new Column(
  children: <Widget>[
    new SpinKitRotatingPlain(color: Colors.blueAccent, size: 30.0,),
    new SpinKitRotatingCircle(color: Colors.blueAccent, size: 30.0,),
    new SpinKitDoubleBounce(color: Colors.blueAccent, size: 30.0,),
    new SpinKitRing(color: Colors.blueAccent, size: 30.0,),
    new SpinKitWave(color: Colors.blueAccent, size: 30.0,),
    new SpinKitWanderingCubes(color: Colors.blueAccent, size: 30.0,),
    new SpinKitFadingCube(color: Colors.blueAccent, size: 30.0,),
    new SpinKitFadingFour(color: Colors.blueAccent, size: 30.0,),
    new SpinKitPulse(color: Colors.blueAccent, size: 30.0,),
    new SpinKitChasingDots(color: Colors.blueAccent, size: 30.0,),
    new SpinKitHourGlass(color: Colors.blueAccent, size: 30.0,),
    new SpinKitSpinningCircle(color: Colors.blueAccent, size: 30.0,),
  ],
)


      和尚剛接觸 Flutter 時間不長,還有不少不清楚和不理解的地方,若是又不對的地方還但願多多指出。如下是和尚公衆號,歡迎閒來吐槽~

本文分享自微信公衆號 - 阿策小和尚(gh_8297e718c166)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索