Flutter EasyRefresh+ListView+Scoped Model realizes pull-up refresh and paging load

Keywords: Mobile network SDK

Preface:

The Flutter project needs to realize the function of "pull-up refresh and page loading". The page can divide a large amount of data into small segments and load them in batches. This can effectively avoid the problem that the client slows down due to loading all the data at one time. Here I use the EasyRefresh third-party plug-in to implement the operations of pull-down refresh and paging load.

Knowledge points used:

  1. dio realizes the data of network request
  2. The model layer encapsulates the methods of pull-up refresh and paging load
  3. ui layer uses easyrefresh to realize pull-down refresh and page loading
  4. ui layer uses listview to display list information

Implementation steps:

1. Add sdk in pubspec.yaml

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

2. Request data

//How to request data
Future getListData(BuildContext context) async {
    String url = IHttpService.baseUrl +"&offset=$currentPage&limit=$limit"; //Interface
    DioUtil.getInstance().get(context, url).then((res) {  //DioUtil is a custom tool class to encapsulate network requests
      if (res.statusCode == Response.ok) {
        var responseList = res.data;
        if (responseList != null) {
          _listData = responseList["loans"];
        }
      } else {
        _listData= [];
      }
    }).catchError((onError) {     
    }).whenComplete(this.notifyListeners);
  }

3. Pull up to refresh data

//How to pull up and refresh data
Future refreshData(BuildContext context) async {
    currentPage = 0;
    _listData.clear();
    getListData(context);
    return null;
  }

4. Load more data

//How to load more data
Future loadMoreData(BuildContext context) async {
    String url = IHttpService.baseUrl +"&offset=$currentPage&limit=$limit"; //Interface
    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; //Number of entries per load
          if (currentPage > 0) {
            List list1 = List();
            list1.addAll(_listData);
            list1.addAll(listData);
            _listData= list1;
          }
        }
      } else {       
        _listData= [];
      }
    }).catchError((onError) {     
    }).whenComplete(this.notifyListeners);
  }

5.easyrefresh implements pull-down refresh and paging load

Widget _buildListView(BuildContext context, CommonStateModel model) {
    return new EasyRefresh(
      onRefresh: () => _onListRefresh(context), //Drop-down refresh
      loadMore: () => _onListLoadMore(context), //Page load more
      key: _easyRefreshKey,
      refreshHeader: MaterialHeader( //Material style head refresh
        key: _headerKey,
      ),
      refreshFooter: MaterialFooter( //Material style bottom refresh
        key: _footerKey,
      ),
      child: _buildListViewContent(context, model),
    );
  }

6.listview display list information

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

  //Custom list items
  Widget renderRow(BuildContext context, CommonStateModel model, int i) {
    var itemData = model.listData[i];
    return new ListTile(
            title: Text(itemData['name']),
            onTap: () {
             launchURL(itemData['url']);
            },
          );
  }

7. The model layer encapsulates the complete code of "pull-up refresh and page loading"

import 'package:scoped_model/scoped_model.dart';

class CommonStateModel extends Model {
  int currentPage = 0;
  int limit = 4; //Number of entries loaded per page

  var _listData;

  get listData => _listData;

  //How to request data
  Future getListData(BuildContext context) async {
    //Interface
    String url = IHttpService.baseUrl +"&offset=$currentPage&limit=$limit"; //Interface
    DioUtil.getInstance().get(context, url).then((res) { //DioUtil is a custom tool class to encapsulate network requests
      if (res.statusCode == Response.ok) {
        var responseList = res.data;
        if (responseList != null) {
          _listData = responseList["loans"];
        }
      } else {
        _listData= [];
      }
    }).catchError((onError) {     
    }).whenComplete(this.notifyListeners);
  }

  //How to pull up and refresh data
  Future refreshData(BuildContext context) async {
    currentPage = 0;
    _listData.clear();
    getListData(context);
    return null;
  }

  //How to load more data
  Future loadMoreData(BuildContext context) async {
     String url = IHttpService.baseUrl +"&offset=$currentPage&limit=$limit"; //Interface
     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; //Number of entries loaded per page
           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. The UI layer implements the complete code of pull-up refresh and paging load

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

  //How to request data
  void _initListData(BuildContext context) async {
    await commonStateModel.getListData(context);
  }

  //Pull up refresh method
  Future<Null> _onListRefresh(BuildContext context) async {
    await commonStateModel.refreshData(context);
  }

  //More ways to load pages
  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),
            ),
          );
        },
      ),
    );
  }

  //Using easyrefresh to realize pull-down refresh and page loading
  Widget _buildListView(BuildContext context, CommonStateModel model) {
    return new EasyRefresh(
      onRefresh: () => _onListRefresh(context),
      loadMore: () => _onListLoadMore(context),
      key: _easyRefreshKey,
      refreshHeader: MaterialHeader( //Material style head refresh
        key: _headerKey,
      ),
      refreshFooter: MaterialFooter( //Material style bottom refresh
        key: _footerKey,
      ),
      child: _buildListViewContent(context, model),
    );
  }

  //The method of displaying list information in 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(itemData['logo']),
                    ),
                  ),
                )),
            new Expanded(
              flex: 2,
              child: new Padding(
                padding: const EdgeInsets.all(5.0),
                child: new Column(
                  children: <Widget>[
                   ...
                  ],
                ),
              ),
            ),
            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. summary:

"Pull up refresh and page loading" has been implemented in the Flutter project. If you have any questions, please contact me with a message!

Posted by blade_922 on Tue, 22 Oct 2019 08:08:51 -0700