RefreshIndicator+FutureBuilder 實現下拉刷新上滑加載數據

Flutter 中能夠經過 RefreshIndicator 和 FutrueBuilder 實現異步網絡請求,對於下拉刷新和上滑加載數據也能夠很方便的實現。這裏以知乎日報 API 爲例演示,僅供學習交流。git

說明

在 Flutter 中,異步 UI 更新能夠經過 FutureBuilder 和 StreamBuilder 這兩個組件來實現,對於網絡請求,一般使用 Futrue + FutureBuilder 組合來完成。而對於下拉刷新功能,Flutter 也單獨提供了一個 Widget —— RefreshIndicator 來實現,這個組件有一個 onRefresh 方法,這裏回調異步請求數據的操做就能實現下拉刷新的功能。github

const RefreshIndicator({
   Key key,
   @required this.child,
   this.displacement = 40.0,
   @required this.onRefresh,
   this.color,
   this.backgroundColor,
   this.notificationPredicate = defaultScrollNotificationPredicate,
   this.semanticsLabel,
   this.semanticsValue,
 }) : assert(child != null),
      assert(onRefresh != null),
      assert(notificationPredicate != null),
      super(key: key);
複製代碼

對於上滑加載數據,一般經過 ListView 或者其餘可滾動組件的 ScrollController 來完成,經過 ScrollController 能夠判斷滾動列表是否滾動到底部,若是是,就調用上滑加載的功能獲取數據便可。編程

效果:api

實現

1、網絡請求

這裏以知乎日報 API 爲例說明,兩個 api 以下bash

///最新消息
const String LAST_NEWS = "https://news-at.zhihu.com/api/4/news/latest";

///歷史消息
const String HISTORY_NEWS = "https://news-at.zhihu.com/api/4/news/before/";
複製代碼

使用 Dio 進行網絡請求:微信

Response     response = await dio.get(LAST_NEWS);
複製代碼

這裏返回的數據是 Map 類型的,能夠根據 key 直接拿到咱們想要的結果便可。 如,獲取消息中的日期字段:網絡

currentDate = response.data["date"].toString();
複製代碼

2、FutureBuilder + ListView 展現數據

FutureBuilder 須要結合 Future 使用,先定義一個 Future,異步網絡請求。異步

///先定義一個 Future
  Future getDataFuture;
  ...
 
 @override
 void initState() {
   super.initState();
   getDataFuture = getItemNews();
 } 
 
   ///獲取最新消息
 Future<List<dynamic> > getItemNews() async{
   items.clear();

   print("開始獲取最新消息.");
   response = await dio.get(LAST_NEWS);
   currentDate = response.data["date"].toString();

   if(response.data["stories"] != null){
     items.addAll(response.data["stories"]);
   }

   return items;
 }

 
複製代碼

FutureBuilder + ListView 展現數據async

FutureBuilder<List<dynamic>>(
         ///指定刷新數據的 Future
         future: getDataFuture,
         builder: (context,AsyncSnapshot<List<dynamic>> async){
           ///正在請求時的視圖
           if (async.connectionState == ConnectionState.active || async.connectionState == ConnectionState.waiting) {
             return Center(
               child: Text("loading..."),
             );
           }
           ///發生錯誤時的視圖
           if (async.connectionState == ConnectionState.done) {
             if (async.hasError) {
               return Center(
                 child: Text("error"),
               );
             } else if (async.hasData && async.data != null && async.data.length > 0) {

               List resultList = async.data;

               return  ListView.builder(
                   controller: _scrollController,
                   itemCount: resultList.length + 1,
                   itemExtent: 100.0,
                   itemBuilder: (BuildContext context, int index) {
                     return index < async.data.length  ?  Container(
                       child:  Card(
                         child: Row(
                           children: <Widget>[
                             Expanded(
                               child: Container(
                                 child: Text(resultList[index]["title"].toString(),style: TextStyle(fontSize:20),),
                                 padding: EdgeInsets.symmetric(horizontal: 10),
                               ),
                               flex: 2,
                             ),

                             Expanded(
                               child: Container(
                                 child:Image.network(resultList[index]["images"][0].toString()),
                                 padding: EdgeInsets.all(5),
                               ),
                               flex: 1,
                             ),

                           ],
                         ),
                       ),
                       height: 50,
                     ): Center(
                       child: isShowProgress? CircularProgressIndicator(
                         strokeWidth: 2.0,
                       ):Container(),
                     );

                   }
               );



             }else{
               return Center(
                 child: Text("error"),
               );
             }
           }
           return Center(
             child: Text("error"),
           );
         },
       ),
複製代碼

3、RefreshIndicator + ScrollController 實現下拉刷新和上滑加載

上面的代碼已經實現了異步加載和顯示數據的功能,對於下拉刷新,只要在 FutureBuilder 外面嵌套 RefreshIndicator 並指定 onRefresh 便可。ide

RefreshIndicator(
       onRefresh: getItemNews,
       child:   FutureBuilder<List<dynamic>>(
         future: getDataFuture,
         ...
         )
   )      
複製代碼

上滑加載數據經過 ScrollController 來實現:

ScrollController _scrollController = ScrollController();

 @override
 void initState() {
   super.initState();
   getDataFuture = getItemNews();
   _scrollController.addListener(() {
     if (_scrollController.position.pixels ==
         _scrollController.position.maxScrollExtent) {
       print("get more");
       _getMore(currentDate);
     }
   });

 }
 
 
   _getMore(String date) async{
   if(date == "")
     return;

   setState(() {
     isShowProgress = true;
   });

   Map<String, dynamic> historyMap ;
   response = await dio.get(HISTORY_NEWS + date);
   historyMap = response.data;

   if(historyMap != null && historyMap.length > 0){
     List<dynamic> stories = historyMap["stories"];
     if(stories != null && stories.length > 0){
       currentDate = historyMap["date"].toString();
     }


     if(response.data["stories"] != null){
       items.addAll(response.data["stories"]);
     }


   }

   setState(() {
     isShowProgress = false;
   });

 }


複製代碼

在 LisbView 裏面,我指定了數據的長度爲返回的數據長度 + 1,目的就是爲了如今最下面的 CircularProgressIndicator 對話框,而上面的變量 isShowProgress 可控制是否顯示這個組件的。

github

最後

歡迎關注「Flutter 編程開發」微信公衆號 。

相關文章
相關標籤/搜索