loading_more_list 3.0.0

  • Readme
  • Changelog
  • Example
  • Installing
  • 94

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

[3.0.0] #

  • breaking change: fix typo(fullCrossAxisExtend => fullCrossAxisExtent)
  • breaking change: use [extendedListDelegate] replace [viewportBuilder], [collectGarbage], [closeToTrailing] and [waterfallFlowDelegate]. You can use WaterfallFlow layout by set [extendedListDelegate] to [SliverWaterfallFlowDelegateWithFixedCrossAxisCount] or [SliverWaterfallFlowDelegateWithMaxCrossAxisExtent].

[2.0.0] #

  • Fix analysis_options
  • Support ScrollViewKeyboardDismissBehavior(breaking change 1.17.0)
  • Support DragStartBehavior

[1.0.5] #

  • Web support

[1.0.4] #

  • Issues: Fix issue about setState throw exception

  • Improve: Remove io.platform to support web

[1.0.3] #

  • Issues: Fix issue about empty sliver list throw exception

[1.0.2] #

  • Issues: Fix issue about LoadingMoreCustomScrollView miss slivers

[1.0.1] #

  • Features: Support SliverFixedExtentList

  • Breaking change: Change values of IndicatorStatus to lowercase

  • Improve: Extract core codes to loading_more_list_library

[1.0.0] #

  • Features: Support WaterfallFlow Support to layout no more item as foot Support to track collect garbage Support to track viewport indexes Support to layout close to trailing

  • Issues: Fix issue about reverse is true with LoadingMoreCustomScrollView

[0.3.7] #

  • set list StreamBuilder's initialData
  • index range check for LoadingMoreBase

[0.3.4] #

  • set notifyStateChanged default value: false clear list when notifyStateChanged is true.

[0.2.7] #

  • add notifyStateChanged parameter for LoadingMoreBase's refresh method to notify state changed or not (you can not rebuild ui before complete refresh)

[0.2.6] #

  • fix LoadingMoreList not listen onScrollNotification
  • add onScrollNotification listen call back for LoadingMoreList and LoadingMoreCustomScrollView

[0.1.9] #

  • update CustomIndicatorDemo in case sliver indicator.

[0.1.8] #

  • export 'src/glow_notification_widget.dart'.

[0.1.7] #

  • add GlowNotificationWidget for overscroll glow.

[0.1.6] #

  • add isloadMoreAction parameter for LoadData method.

[0.1.5] #

  • fix issue about full screen status in IndicatorWidget

[0.1.4] #

  • make all loading_more_list darts as library loading_more_list

[0.1.3] #

  • fix issue about IndicatorWidget.

[0.1.2] #

  • Add FullScreenError status.
  • Fix issues for sliver list in NestedScrollView.
  • Add NestedScrollView demo for loading more list

[0.1.1] #

  • Correct try again when it has error.

[0.1.0] #

  • Upgrade Some Commments.

[0.0.1] #

  • Initial Open Source release.

example/lib/main.dart

import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_candies_demo_library/flutter_candies_demo_library.dart';
import 'example_route.dart';
import 'example_route_helper.dart';
import 'example_routes.dart';


void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      builder: (BuildContext c, Widget w) {
        ScreenUtil.init(width: 750, height: 1334, allowFontScaling: true);
        // ScreenUtil.instance =
        //     ScreenUtil(width: 750, height: 1334, allowFontScaling: true)
        //       ..init(c);
        if (!kIsWeb) {
          final MediaQueryData data = MediaQuery.of(c);
          return MediaQuery(
            data: data.copyWith(textScaleFactor: 1.0),
            child: w,
          );
        }
        return w;
      },
      initialRoute: Routes.fluttercandiesMainpage,
      onGenerateRoute: (RouteSettings settings) {
        //when refresh web, route will as following
        //   /
        //   /fluttercandies:
        //   /fluttercandies:/
        //   /fluttercandies://mainpage
        if (kIsWeb && settings.name.startsWith('/')) {
          return onGenerateRouteHelper(
            settings.copyWith(name: settings.name.replaceFirst('/', '')),
            notFoundFallback:
                getRouteResult(name: Routes.fluttercandiesMainpage).widget,
          );
        }
        return onGenerateRouteHelper(settings);
      },
    );
  }
}

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  loading_more_list: ^3.0.0

2. Install it

You can install packages from the command line:

with Flutter:


$ flutter pub get

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:loading_more_list/loading_more_list.dart';
  
Popularity:
Describes how popular the package is relative to other packages. [more]
88
Health:
Code health derived from static analysis. [more]
100
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
100
Overall:
Weighted score of the above. [more]
94
Learn more about scoring.

We analyzed this package on Jul 2, 2020, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.8.4
  • pana: 0.13.13
  • Flutter: 1.17.5

Analysis suggestions

Package not compatible with SDK dart

because of import path [loading_more_list] that is in a package requiring null.

Health suggestions

Format lib/loading_more_list.dart.

Run flutter format to format lib/loading_more_list.dart.

Format lib/src/glow_notification_widget.dart.

Run flutter format to format lib/src/glow_notification_widget.dart.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.6.0 <3.0.0
extended_list ^2.0.0 2.0.0
flutter 0.0.0
loading_more_list_library ^1.0.2 1.0.2
waterfall_flow ^2.0.0 2.0.2
Transitive dependencies
collection 1.14.12 1.14.13
extended_list_library 1.0.0
meta 1.1.8
sky_engine 0.0.99
typed_data 1.1.6 1.2.0
vector_math 2.0.8
Dev dependencies
flutter_test