loading_more_list 3.1.1

Flutter Android iOS web

A loading more list which supports ListView,GridView,WaterfallFlow and Slivers.

loading_more_list #

A loading more list which supports ListView,GridView,WaterfallFlow and Slivers.

pub package GitHub stars GitHub forks GitHub license GitHub issues flutter-candies

Language: English | 中文简体

Web demo for LoadingMoreList

Use #

  • add library to your pubspec.yaml

dependencies:
  loading_more_list: any

  • import library in dart file

  import 'package:loading_more_list/loading_more_list.dart';

Prepare Data Collection #

LoadingMoreBase

class TuChongRepository extends LoadingMoreBase<TuChongItem> {
  int pageindex = 1;
  bool _hasMore = true;
  bool forceRefresh = false;
  @override
  bool get hasMore => (_hasMore && length < 30) || forceRefresh;

  @override
  Future<bool> refresh([bool clearBeforeRequest = false]) async {
    _hasMore = true;
    pageindex = 1;
    //force to refresh list when you don't want clear list before request
    //for the case, if your list already has 20 items.
    forceRefresh = !clearBeforeRequest;
    var result = await super.refresh(clearBeforeRequest);
    forceRefresh = false;
    return result;
  }

  @override
  Future<bool> loadData([bool isloadMoreAction = false]) async {
    String url = "";
    if (this.length == 0) {
      url = "https://api.tuchong.com/feed-app";
    } else {
      int lastPostId = this[this.length - 1].postId;
      url =
          "https://api.tuchong.com/feed-app?post_id=$lastPostId&page=$pageindex&type=loadmore";
    }
    bool isSuccess = false;
    try {
      //to show loading more clearly, in your app,remove this
      await Future.delayed(Duration(milliseconds: 500));

      var result = await HttpClientHelper.get(url);

      var source = TuChongSource.fromJson(json.decode(result.body));
      if (pageindex == 1) {
        this.clear();
      }
      for (var item in source.feedList) {
        if (item.hasImage && !this.contains(item) && hasMore) this.add(item);
      }

      _hasMore = source.feedList.length != 0;
      pageindex++;
      isSuccess = true;
    } catch (exception, stack) {
      isSuccess = false;
      print(exception);
      print(stack);
    }
    return isSuccess;
  }
}

Argument #

almost of arguments are the same as official.

following arguments are made for loading more.

ListConfig

argumentdescriptiondefault
itemBuilderThe item builder of list.required
sourceListThe source list of data which extends LoadingMoreBaserequired
showGlowLeadingWhether to show the overscroll glow on the side with negative scroll offsets.0.0
showGlowTrailingWhether to show the overscroll glow on the side with positive scroll offsets.-
lastChildLayoutTypeLayout type of last child(loadmore/no more item).LastChildLayoutType.foot
extendedListDelegateThe delegate for WaterfallFlow or ExtendedList.-
gridDelegateThe delegate for GridView.-
indicatorBuilderwidget builder for different loading state.IndicatorWidget
paddingThe amount of space by which to inset the child sliver for SliverListConfig-

Widget #

LoadingMoreList

argumentdescriptiondefault
listConfigListConfigrequired
onScrollNotificationCalled when a ScrollNotification of the appropriate type arrives at this location in the tree.-

LoadingMoreSliverList

argumentdescriptiondefault
sliverListConfigSliverListConfigrequired

LoadingMoreCustomScrollView

argumentdescriptiondefault
onScrollNotificationCalled when a ScrollNotification of the appropriate type arrives at this location in the tree.-
rebuildCustomScrollViewin NestedScrollView, rebuild CustomScrollView, viewport can be computed again.false

ListView #

            LoadingMoreList(
              ListConfig<TuChongItem>(
                itemBuilder: ItemBuilder.itemBuilder,
                sourceList: listSourceRepository,
                padding: EdgeInsets.all(0.0),
              ),
            ),

GridView #

define GridView with gridDelegate argument.

            LoadingMoreList(
              ListConfig<TuChongItem>(
                itemBuilder: ItemBuilder.itemBuilder,
                sourceList: listSourceRepository,
                padding: EdgeInsets.all(0.0),
                gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                  crossAxisCount: 2,
                  crossAxisSpacing: 3.0,
                  mainAxisSpacing: 3.0,
                ),
              ),
            ),

WaterfallFlow #

define WaterfallFlow with waterfallFlowDelegate argument.

            LoadingMoreList(
              ListConfig<TuChongItem>(
                extendedListDelegate: SliverWaterfallFlowDelegateWithFixedCrossAxisCount(
                  crossAxisCount: 2,
                  crossAxisSpacing: 5,
                  mainAxisSpacing: 5,
                ),
                itemBuilder: _buildItem,
                sourceList: listSourceRepository,
                padding: EdgeInsets.all(5.0),
              ),
            ),

Sliver/CustomScrollView #

following codes are show how to build loading more list within CustomScrollView.

      LoadingMoreCustomScrollView(
        slivers: <Widget>[
          SliverAppBar(
            pinned: true,
            title: Text("MultipleSliverDemo"),
          ),
          ///SliverList
          LoadingMoreSliverList(SliverListConfig<TuChongItem>(
            itemBuilder: ItemBuilder.itemBuilder,
            sourceList: listSourceRepository,
          )),
          SliverToBoxAdapter(
            child: Container(
              alignment: Alignment.center,
              child: Text("Next list"),
              color: Colors.blue,
              height: 100.0,
            ),
          ),
          ///SliverGrid
          LoadingMoreSliverList(
            SliverListConfig<TuChongItem>(
              itemBuilder: ItemBuilder.itemBuilder,
              sourceList: listSourceRepository1,
              gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                crossAxisCount: 2,
                crossAxisSpacing: 3.0,
                mainAxisSpacing: 3.0,
              ),
            ),
          ),
          SliverPersistentHeader(
            delegate: CommonExtentSliverPersistentHeaderDelegate(
                Container(
                  alignment: Alignment.center,
                  child: Text("Pinned Content"),
                  color: Colors.red,
                ),
                100.0),
            pinned: true,
          ),
          ///SliverWaterfallFlow
          LoadingMoreSliverList(
            SliverListConfig<TuChongItem>(
              itemBuilder: buildWaterfallFlowItem,
              sourceList: listSourceRepository2,
              padding: EdgeInsets.symmetric(horizontal: 5.0),
              extendedListDelegate: SliverWaterfallFlowDelegateWithFixedCrossAxisCount(
                crossAxisCount: 2,
                crossAxisSpacing: 5,
                mainAxisSpacing: 5,
              ),
            ),
          ),
        ],
      ),

IndicatorStatus #

define loading status with indicatorBuilder argument.

        enum IndicatorStatus {
          None,
          LoadingMoreBusying,
          FullScreenBusying,
          Error,
          FullScreenError,
          NoMoreLoad,
          Empty
        }
      LoadingMoreList(
        ListConfig<TuChongItem>(
          itemBuilder: ItemBuilder.itemBuilder,
          sourceList: listSourceRepository,
          indicatorBuilder: _buildIndicator,
          padding: EdgeInsets.all(0.0),
        ),
      ),

  //you can use IndicatorWidget or build yourself widget
  //in this demo, we define all status.
  Widget _buildIndicator(BuildContext context, IndicatorStatus status) {
    //if your list is sliver list ,you should build sliver indicator for it
    //isSliver=true, when use it in sliver list
    bool isSliver = false;

    Widget widget;
    switch (status) {
      case IndicatorStatus.None:
        widget = Container(height: 0.0);
        break;
      case IndicatorStatus.LoadingMoreBusying:
        widget = Row(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: <Widget>[
            Container(
              margin: EdgeInsets.only(right: 5.0),
              height: 15.0,
              width: 15.0,
              child: getIndicator(context),
            ),
            Text("正在加载...不要着急")
          ],
        );
        widget = _setbackground(false, widget, 35.0);
        break;
      case IndicatorStatus.FullScreenBusying:
        widget = Row(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: <Widget>[
            Container(
              margin: EdgeInsets.only(right: 0.0),
              height: 30.0,
              width: 30.0,
              child: getIndicator(context),
            ),
            Text("正在加载...不要着急")
          ],
        );
        widget = _setbackground(true, widget, double.infinity);
        if (isSliver) {
          widget = SliverFillRemaining(
            child: widget,
          );
        } else {
          widget = CustomScrollView(
            slivers: <Widget>[
              SliverFillRemaining(
                child: widget,
              )
            ],
          );
        }
        break;
      case IndicatorStatus.Error:
        widget = Text(
          "好像出现了问题呢?",
        );
        widget = _setbackground(false, widget, 35.0);

        widget = GestureDetector(
          onTap: () {
            listSourceRepository.errorRefresh();
          },
          child: widget,
        );

        break;
      case IndicatorStatus.FullScreenError:
        widget = Text(
          "好像出现了问题呢?",
        );
        widget = _setbackground(true, widget, double.infinity);
        widget = GestureDetector(
          onTap: () {
            listSourceRepository.errorRefresh();
          },
          child: widget,
        );
        if (isSliver) {
          widget = SliverFillRemaining(
            child: widget,
          );
        } else {
          widget = CustomScrollView(
            slivers: <Widget>[
              SliverFillRemaining(
                child: widget,
              )
            ],
          );
        }
        break;
      case IndicatorStatus.NoMoreLoad:
        widget = Text("没有更多的了。。不要拖了");
        widget = _setbackground(false, widget, 35.0);
        break;
      case IndicatorStatus.Empty:
        widget = EmptyWidget(
          "这里是空气!",
        );
        widget = _setbackground(true, widget, double.infinity);
        if (isSliver) {
          widget = SliverToBoxAdapter(
            child: widget,
          );
        } else {
          widget = CustomScrollView(
            slivers: <Widget>[
              SliverFillRemaining(
                child: widget,
              )
            ],
          );
        }
        break;
    }
    return widget;
  }

CollectGarbage #

track the indexes are collect, you can collect garbage at that monment(for example Image cache)

more detail

        LoadingMoreList(
          ListConfig<TuChongItem>(
            extendedListDelegate: ExtendedListDelegate(
              collectGarbage: (List<int> indexes) {
                ///collectGarbage
              },
            ),
          ),
        ),

ViewportBuilder #

track the indexes go into the viewport, it's not include cache extent.

        LoadingMoreList(
          ListConfig<TuChongItem>(
            extendedListDelegate: ExtendedListDelegate(
              viewportBuilder: (int firstIndex, int lastIndex) {
                print('viewport : [$firstIndex,$lastIndex]');
              },
            ),
          ),
        ),

LastChildLayoutType #

build lastChild as special child in the case that it is loadmore/no more item.

        enum LastChildLayoutType {
        /// as default child
        none,

        /// follow max child trailing layout offset and layout with full cross axis extent
        /// last child as loadmore item/no more item in [ExtendedGridView] and [WaterfallFlow]
        /// with full cross axis extend
        fullCrossAxisExtent,

        /// as foot at trailing and layout with full cross axis extend
        /// show no more item at trailing when children are not full of viewport
        /// if children is full of viewport, it's the same as fullCrossAxisExtent
        foot,
        }

CloseToTrailing #

when reverse property of List is true, layout is as following. it likes chat list, and new session will insert to zero index. but it's not right when items are not full of viewport.

     trailing
-----------------
|               |
|               |
|     item2     |
|     item1     |
|     item0     |
-----------------
     leading

to solve it, you could set closeToTrailing to true, layout is as following. support [ExtendedGridView],[ExtendedList],[WaterfallFlow]. and it also works when reverse is flase, layout will close to trailing.

     trailing
-----------------
|     item2     |
|     item1     |
|     item0     |
|               |
|               |
-----------------
     leading
      LoadingMoreList(
        ListConfig<TuChongItem>(
          extendedListDelegate: ExtendedListDelegate(
            closeToTrailing: true
          ),
        ),
      ),
33
likes
110
pub points
89%
popularity

Publisher

fluttercandies.com

A loading more list which supports ListView,GridView,WaterfallFlow and Slivers.

Repository (GitHub)
View/report issues

Documentation

API reference

License

MIT (LICENSE)

Dependencies

extended_list, extended_list_library, flutter, loading_more_list_library, waterfall_flow

More

Packages that depend on loading_more_list