A layer of encapsulation is added to flutter, which provides infinite loading components. This reusable infinite load list includes the following features:

  • Slide loading
  • The data does not meet the loading conditions, and the message reaches the bottom
  • Search and reload data based on changes in specific parameters (easily combined with filter and search)
  • The different lists simply need to pass in any RowItem defined
  • Load paging based on the limit and offset function

Trouble spots

  • How DART does generic instantiation.
  • The inheritWidget manages parameter state

code

  • RowItem parent class
import 'package:flutter/widgets.dart';

abstract  class ListItemWidget extends StatelessWidget {
    const ListItemWidget(
      {Key key, this.item}
    ):super(key:key);

  final Map item;
  call(Map item)=>this;
}
Copy the code
  • Wireless load list component
import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_easyrefresh/easy_refresh.dart';
import 'package:flutter_easyrefresh/material_header.dart';
import 'package:flutter_lim/common/http.dart';
import 'package:flutter_lim/widgets/listItem.dart'; typedef RequestCallback = Stream<dynamic> Function({Map paramObj}); typedef S ItemCreator<S extends ListItemWidget>(dynamic item); // New T() generic instantiation implementation // infinite load list // use InheritedWidget, Each list manages its own search parameter class ShareParams extends InheritedWidget {ShareParams({@required this.specialparams, this.data, this.count, Widget child}) : super(child: child); final Map<String, dynamic> specialParams; final List<dynamic> data; final int count; static ShareParams of(BuildContext context) {return context.dependOnInheritedWidgetOfExactType<ShareParams>();
  }

  @override
  bool updateShouldNotify(ShareParams oldWidget) {
    Map<String, dynamic> oldParams = oldWidget.specialParams;
    bool isUpdate = false;
    if(this.specialParams.keys.length ! = oldParams.length) { isUpdate =true;
    } else {
      for (String key in this.specialParams.keys) {
        if(oldParams[key] == null || oldParams[key] ! = this.specialParams[key]) { isUpdate =true;
          break; }}}returnisUpdate; } } @immutable class InfiniteListViewWidget<T extends ListItemWidget> extends StatefulWidget { final RequestCallback request; // Data request path Final List<dynamic> data; // Initialize data final int count; Final ItemCreator<T> Creator; // List Item generics instantiate final Map<String, dynamic> specialParam; // Final Function refresh; InfiniteListViewWidget(this.request, this.data, this.count, this.creator, {this.specialParam, this.refresh}); @override _InfiniteListViewWidgetState<T> createState() => new _InfiniteListViewWidgetState<T>( this.request, this.data, this.count, this.creator, specialParam: this.specialParam, refresh: this.refresh); } @override class _InfiniteListViewWidgetState<T extends ListItemWidget> extends State<InfiniteListViewWidget> { static const loadingTag = {"position": "bottom"}; // table end tag Http Http = new Http(); InfiniteListParamObj infiParams; RequestCallback request; // Data request int count; // List<dynamic> data; ItemCreator<T> creator; @optionalTypeArgs Map<String, dynamic> specialParam; // Function refresh; EasyRefreshController _refreshController = new EasyRefreshController(); _InfiniteListViewWidgetState( this.request, this.data, this.count, this.creator, {this.specialParam, this.refresh}); @override voidinitState() {
    super.initState();
  }

  void didChangeDependencies() {
    super.didChangeDependencies();
    if(ShareParams.of(context) ! = null) {print(
          'LISTEN PARAMS didChange=>${ShareParams.of(context).specialParams}');
      print('LISTEN PARAMS didChange=>${ShareParams.of(context).data}');
      this.specialParam = ShareParams.of(context).specialParams;
      this.data = ShareParams.of(context).data;
      this.count = ShareParams.of(context).count;
    }
    if(this.specialParam ! = null) { this._setInfiniteParams(this.specialParam); }} @override Widget build(BuildContext context) {//EasyRefresh is a pull-down refresh componentreturn EasyRefresh(
      header: MaterialHeader(),
      child: ListView.builder(
        itemCount: this.data.length,
        itemBuilder: (BuildContext context, int index) {
          var word = this.data[index];
          var nextWord;
          if (index == this.data.length - 1) {
            nextWord = null;
          } else {
            nextWord = this.data[index + 1];
          }
          ifNextWord == null && this.data.length == index + 1) {// Final curCount = this.data.length;if (curCount < this.count) {
              //offset + limitInfiparams. offset = this.data.length; // RetrieveData _retrieveData(this.resolvelistData); // Display loading while loadingreturnColumn(children: <Widget>[Creator (word), Container(padding: const EdgeInsets. All (16.0), alignment: Alignment. Center, child: SizedBox (width: 24.0, height: 24.0, child: CircularProgressIndicator (strokeWidth: 2.0)),)],); }else{// The total number of loaded data has exceeded countreturnColumn( children: <Widget>[ creator(word), Container( alignment: Alignment.center, padding: EdgeInsets. All (16.0), the child: the Text ("There is no more.", style: TextStyle(color: Colors.grey), )) ], ); }} // Since the Widget returned in the build method must be instantiated, implement the generic instantiation T item = creator(word);return item;
        },
      ),
      controller: _refreshController,
      onRefresh: () async {
        this.refresh == null
            ? _refreshController.finishRefresh(success: true) : this.refresh(); }); } void _setInfiniteParams(Map<String, dynamic> specialParam, {intlimit, int offset, String ordering}) {
    if (this.infiParams == null) {
      limit = limit! = null ?limit: 10; offset = offset ! = null ? offset : 0; ordering = ordering ! = null ? ordering :'-create_time';
      this.infiParams =
          new InfiniteListParamObj(limit, offset, ordering, specialParam);
    } else {
      this.infiParams.paramsObj = specialParam;
    }
  }

  void _retrieveData(Function resovle) {
    Map<String, dynamic> paramObj = this.infiParams.getParams();
    request(paramObj: paramObj).listen(resovle);
  }

  void resolveListData(dynamic data) {
    this.count = data['count'];
    setListData(data['results']);
  }

  void setListData(dynamic list) {
    this.data.addAll(list);
    setState(() {}); }}Copy the code
  • List the default parameters, which can be modified according to your design
@override
class InfiniteListParamObj {
  int limit = 10;
  int offset = 0;
  String ordering = ' ';
  Map<String, dynamic> paramsObj = {};
  InfiniteListParamObj(this.limit, this.offset, this.ordering, this.paramsObj);
  Map<String, dynamic> getParams() {
    Map<String, dynamic> basic = {
      "limit": this.limit,
      "offset": this.offset,
      "ordering": this.ordering
    };
    basic.addAll(this.paramsObj);
    return basic;
  }

  void resetBasicParams() { this.limit = 10; this.offset = 0; }}Copy the code
  • How to use
Expanded(Child: ShareParams(specialParams: this.paramsobj, data: this.datalist,// Data list count: This. count,// count child: New InfiniteListViewWidget<ProductItem>(//ProductItem is specifically integrated from ListItemWidget's list item getUniqueProdList,// data load URL this.datalist, This. count, (item) => new ProductItem(item), specialParam: this.paramsObj,// List search parameter refresh: This.onrefresh,// Custom list refresh methods),));Copy the code

limitations

  • Applies when rowitem is a stateless component
  • If Rowitem needs to interact with the list to control state, such as deleting items, this is not a good idea