1. 程式人生 > >Flutter EasyRefresh+ListView+Scoped Model 實現上拉重新整理和分頁載入

Flutter EasyRefresh+ListView+Scoped Model 實現上拉重新整理和分頁載入

前言:

       Flutter專案需要實現“上拉重新整理和分頁載入“的功能,分頁可以把大量資料分割成一個個小段,分次載入。這樣可以有效避免因為一次load全部資料而導致客戶端變慢的問題。在這裡我使用EasyRefresh第三方外掛來實現下拉重新整理和分頁載入的操作。

用到的知識點:

  1. dio實現網路請求的資料
  2. model層封裝“上拉重新整理和分頁載入“的方法
  3. ui層使用easyrefresh實現下拉重新整理,分頁載入
  4. ui層使用listview展示列表資訊

實現的步驟:

1.在pubspec.yaml新增sdk

dependencies:
  ...
  cupertino_icons: ^0.1.0
  dio: ^2.1.9
  scoped_model: ^1.0.1
  flutter_easyrefresh: ^1.2.7

 2.請求資料

//請求資料的方法
Future getListData(BuildContext context) async {
    String url = IHttpService.baseUrl +"&offset=$currentPage&limit=$limit"; //介面
    DioUtil.getInstance().get(context, url).then((res) {  //DioUtil是自定義封裝網路請求的工具類
      if (res.statusCode == Response.ok) {
        var responseList = res.data;
        if (responseList != null) {
          _listData = responseList["loans"];
        }
      } else {
        _listData= [];
      }
    }).catchError((onError) {     
    }).whenComplete(this.notifyListeners);
  }

3.上拉重新整理資料

//上拉重新整理資料的方法
Future refreshData(BuildContext context) async {
    currentPage = 0;
    _listData.clear();
    getListData(context);
    return null;
  }

4.載入更多資料

//載入更多資料的方法
Future loadMoreData(BuildContext context) async {
    String url = IHttpService.baseUrl +"&offset=$currentPage&limit=$limit"; //介面
    DioUtil.getInstance().get(context, url).then((res) {
      if (res.statusCode == Response.ok) {        
        var responseList = res.data;
        if (responseList != null) {
          var listData = responseList["loans"];
          currentPage += limit; //每次載入的條數
          if (currentPage > 0) {
            List list1 = List();
            list1.addAll(_listData);
            list1.addAll(listData);
            _listData= list1;
          }
        }
      } else {       
        _listData= [];
      }
    }).catchError((onError) {     
    }).whenComplete(this.notifyListeners);
  }

5.easyrefresh實現下拉重新整理,分頁載入

Widget _buildListView(BuildContext context, CommonStateModel model) {
    return new EasyRefresh(
      onRefresh: () => _onListRefresh(context), //下拉重新整理
      loadMore: () => _onListLoadMore(context), //分頁載入更多
      key: _easyRefreshKey,
      refreshHeader: MaterialHeader( //Material風格的頭部重新整理
        key: _headerKey,
      ),
      refreshFooter: MaterialFooter( //Material風格的底部重新整理
        key: _footerKey,
      ),
      child: _buildListViewContent(context, model),
    );
  }

6.listview展示列表資訊

Widget _buildListViewContent(BuildContext context, CommonStateModel model) {
    return ListView.builder(
      shrinkWrap: true,
      physics: NeverScrollableScrollPhysics(),
      itemBuilder: (context, index) {
        return renderRow(context, model, index);
      },
      itemCount: model.listData.length,
      controller: _scrollController,
    );
  }

  //自定義的列表項
  Widget renderRow(BuildContext context, CommonStateModel model, int i) {
    var itemData = model.listData[i];
    return new InkWell(
      child: new Container(
        decoration: new BoxDecoration(color: Colors.white),
        margin: const EdgeInsets.fromLTRB(10.0, 10.0, 10.0, 0.0),
        child: new Row(
          children: <Widget>[
            new Expanded(
                flex: 1,
                child: new Padding(
                  padding: const EdgeInsets.all(5.0),
                  child: new Container(
                    height: 120.0,
                    child: new Center(
                      child: Image.asset(images/logo.png),
                    ),
                  ),
                )),
            new Expanded(
              flex: 2,
              child: new Padding(
                padding: const EdgeInsets.all(5.0),
                child: new Column(
                  children: <Widget>[
                   new Text(itemData["name"])
                   ...
                  ],
                ),
              ),
            ),
            new Image.asset("images/arrow_right_icon.png",
                height: 25.0, width: 25.0)
          ],
        ),
      ),
      onTap: () {
       //To do something
      },
    );
  }

7.Model層封裝“上拉重新整理和分頁載入“的完整程式碼

import 'package:scoped_model/scoped_model.dart';

class CommonStateModel extends Model {
  int currentPage = 0;
  int limit = 4; //每次分頁載入的條數

  var _listData;

  get listData => _listData;

  //請求資料的方法
  Future getListData(BuildContext context) async {
    //介面
    String url = IHttpService.baseUrl +"&offset=$currentPage&limit=$limit"; //介面
    DioUtil.getInstance().get(context, url).then((res) { //DioUtil是自定義封裝網路請求的工具類
      if (res.statusCode == Response.ok) {
        var responseList = res.data;
        if (responseList != null) {
          _listData = responseList["loans"];
        }
      } else {
        _listData= [];
      }
    }).catchError((onError) {     
    }).whenComplete(this.notifyListeners);
  }

  //上拉重新整理資料的方法
  Future refreshData(BuildContext context) async {
    currentPage = 0;
    _listData.clear();
    getListData(context);
    return null;
  }

  //載入更多資料的方法
  Future loadMoreData(BuildContext context) async {
     String url = IHttpService.baseUrl +"&offset=$currentPage&limit=$limit"; //介面
     DioUtil.getInstance().get(context, url).then((res) {
       if (res.statusCode == Response.ok) {        
         var responseList = res.data;
         if (responseList != null) {
           var listData = responseList["loans"];
           currentPage += limit; //每次分頁載入的條數
           if (currentPage > 0) {
             List list1 = List();
             list1.addAll(_listData);
             list1.addAll(listData);
             _listData= list1;
          }
        }
      } else {       
        _listData= [];
      }
    }).catchError((onError) {     
    }).whenComplete(this.notifyListeners);
  }

  static CommonStateModel of(context) =>
      ScopedModel.of<CommonStateModel>(context, rebuildOnChange: true);
}

8.UI層實現上拉重新整理和分頁載入的完整程式碼

import 'package:flutter_easyrefresh/easy_refresh.dart';
import 'package:flutter_easyrefresh/material_header.dart';
import 'package:flutter_easyrefresh/material_footer.dart';
import 'package:scoped_model/scoped_model.dart';
...

class HomePage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => HomePageState();
}

class HomePageState extends State<HomePage>
    with AutomaticKeepAliveClientMixin {
  CommonStateModel commonStateModel;
  GlobalKey<EasyRefreshState> _easyRefreshKey =
      new GlobalKey<EasyRefreshState>();
  GlobalKey<RefreshHeaderState> _headerKey =
      new GlobalKey<RefreshHeaderState>();
  GlobalKey<RefreshFooterState> _footerKey =
      new GlobalKey<RefreshFooterState>();

  ScrollController _scrollController = ScrollController();

  _getModel() {
    if (commonStateModel == null) {
      commonStateModel = CommonStateModel();
    }
    return commonStateModel;
  }

  //請求資料的方法
  void _initListData(BuildContext context) async {
    await commonStateModel.getListData(context);
  }

  //上拉重新整理的方法
  Future<Null> _onListRefresh(BuildContext context) async {
    await commonStateModel.refreshData(context);
  }

  //分頁載入更多的方法
  Future<Null> _onListLoadMore(BuildContext context) async {
    await commonStateModel.loadMoreData(context);
  }

  @override
  void initState() {
    super.initState();
    _getModel();
    _initListData(context);
  }

  @override
  Widget build(BuildContext context) {
    super.build(context);
    return ScopedModel<CommonStateModel>(
      model: commonStateModel,
      child: ScopedModelDescendant<CommonStateModel>(
        builder: (context, child, model) {
          return Scaffold(
            body: Container(
              decoration: new BoxDecoration(color: Color(0xFFECECEB)),
              child: _buildListView(context, model),
            ),
          );
        },
      ),
    );
  }

  //使用easyrefresh實現下拉重新整理,分頁載入的方法
  Widget _buildListView(BuildContext context, CommonStateModel model) {
    return new EasyRefresh(
      onRefresh: () => _onListRefresh(context),
      loadMore: () => _onListLoadMore(context),
      key: _easyRefreshKey,
      refreshHeader: MaterialHeader( //Material風格的頭部重新整理
        key: _headerKey,
      ),
      refreshFooter: MaterialFooter( //Material風格的底部重新整理
        key: _footerKey,
      ),
      child: _buildListViewContent(context, model),
    );
  }

  //listview展示列表資訊的方法
  Widget _buildListViewContent(BuildContext context, CommonStateModel model) {
    return ListView.builder(
      shrinkWrap: true,
      physics: NeverScrollableScrollPhysics(),
      itemBuilder: (context, index) {
        return renderRow(context, model, index);
      },
      itemCount: model.listData.length,
      controller: _scrollController,
    );
  }

  Widget renderRow(BuildContext context, CommonStateModel model, int i) {
    var itemData = model.listData[i];
    
    return new InkWell(
      child: new Container(
        decoration: new BoxDecoration(color: Colors.white),
        margin: const EdgeInsets.fromLTRB(10.0, 10.0, 10.0, 0.0),
        child: new Row(
          children: <Widget>[
            new Expanded(
                flex: 1,
                child: new Padding(
                  padding: const EdgeInsets.all(5.0),
                  child: new Container(
                    height: 120.0,
                    child: new Center(
                      child: Image.asset(Image.asset(images/logo.png)),
                    ),
                  ),
                )),
            new Expanded(
              flex: 2,
              child: new Padding(
                padding: const EdgeInsets.all(5.0),
                child: new Column(
                  children: <Widget>[
                   new Text[itemData["name"]]
                   ...
                  ],
                ),
              ),
            ),
            new Image.asset("images/arrow_right_icon.png",
                height: 25.0, width: 25.0)
          ],
        ),
      ),
      onTap: () {
       //To do something
      },
    );
  }

  @override
  void dispose() {
    super.dispose();
    _scrollController.dispose();
  }

  @protected
  bool get wantKeepAlive => true;
}

9.總結:

 在Flutter專案已經實現”上拉重新整理和分頁載入“的功能, 如果有什麼疑問的話,可以