Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

equivalent incrementally_loading_listview for Slivers #13

Closed
wemped opened this issue Jun 18, 2019 · 7 comments
Closed

equivalent incrementally_loading_listview for Slivers #13

wemped opened this issue Jun 18, 2019 · 7 comments

Comments

@wemped
Copy link
Contributor

wemped commented Jun 18, 2019

Currently this library is not compatible w/ Slivers. Slivers are used often when you have complex scrolling views or even just a scrolling app bar.

I think it would be awesome to create a similar incrementally_loading_listview but for usages in Slivers. That way this library can be used in even more scenarios.

@MaikuB
Copy link
Owner

MaikuB commented Jun 18, 2019

If I'm not mistaken, you'd run into the same problem with the standard ListView (mentioning this since this package is providing a drop-in replacement for it). Though I may be partially misunderstanding what you're saying i.e. if it's about using the widget provided within a sliver or more around supporting a loading more items in a SliverList. You could check out this package that may be solving the problem you're describing

https://pub.dev/packages/flutter_pagewise

@wemped
Copy link
Contributor Author

wemped commented Jun 18, 2019

I'll take a look at that package.

I'm not sure what problem you're referring to when you say you'd run into the same problem with the standard ListView.
There's a SliverListView for when you want a ListView in a Sliver.

Trying to write out an example of how this package could incorporate it (mostly through copy/paste 😄)

import 'dart:async';
import 'package:flutter/widgets.dart';
import 'package:rxdart/subjects.dart';

typedef LoadMore = Future Function();

typedef OnLoadMore = void Function();

typedef ItemCount = int Function();

typedef HasMore = bool Function();

typedef OnLoadMoreFinished = void Function();

/// A list view that can be used for incrementally loading items when the user scrolls.
/// This is an extension of the ListView widget that uses the ListView.builder constructor.
class SliverIncrementallyLoadingListView extends StatefulWidget {
  /// A callback that indicates if the collection associated with the ListView has more items that should be loaded
  final HasMore hasMore;

  /// A callback to an asynchronous function that would load more items
  final LoadMore loadMore;

  /// Determines when the list view should attempt to load more items based on of the index of the item is scrolling into view
  /// This is relative to the bottom of the list and has a default value of 0 so that it loads when the last item within the list view scrolls into view.
  /// As an example, setting this to 1 would attempt to load more items when the second last item within the list view scrolls into view
  final int loadMoreOffsetFromBottom;
  final Key key;
  final IndexedWidgetBuilder itemBuilder;
  final ItemCount itemCount;
  final bool addAutomaticKeepAlives;
  final bool addRepaintBoundaries;
  final double cacheExtent;

  /// A callback that is triggered when more items are being loaded
  final OnLoadMore onLoadMore;

  /// A callback that is triggered when items have finished being loaded
  final OnLoadMoreFinished onLoadMoreFinished;

  SliverIncrementallyLoadingListView(
      {@required this.hasMore,
      @required this.loadMore,
      this.loadMoreOffsetFromBottom = 0,
      this.key,
      @required this.itemBuilder,
      @required this.itemCount,
      this.addAutomaticKeepAlives = true,
      this.addRepaintBoundaries = true,
      this.cacheExtent,
      this.onLoadMore,
      this.onLoadMoreFinished});

  @override
  SliverIncrementallyLoadingListViewState createState() {
    return SliverIncrementallyLoadingListViewState();
  }
}

class SliverIncrementallyLoadingListViewState
    extends State<SliverIncrementallyLoadingListView> {
  bool _loadingMore = false;
  final PublishSubject<bool> _loadingMoreSubject = PublishSubject<bool>();
  Stream<bool> _loadingMoreStream;

  IncrementallyLoadingListViewState() {
    _loadingMoreStream =
        _loadingMoreSubject.switchMap((shouldLoadMore) => loadMore());
  }

  @override
  Widget build(BuildContext context) {
    return StreamBuilder(
        stream: _loadingMoreStream,
        builder: (context, snapshot) {
          return SliverList(
            key: widget.key,
            delegate: SliverChildBuilderDelegate(
              (itemBuilderContext, index) {
                if (!_loadingMore &&
                    index ==
                        widget.itemCount() -
                            widget.loadMoreOffsetFromBottom -
                            1 &&
                    widget.hasMore()) {
                  _loadingMore = true;
                  _loadingMoreSubject.add(true);
                }
                return widget.itemBuilder(itemBuilderContext, index);
              },
              childCount: widget.itemCount(),
              addAutomaticKeepAlives: widget.addAutomaticKeepAlives,
              addRepaintBoundaries: widget.addRepaintBoundaries,
            ),
          );
        });
  }

  Stream<bool> loadMore() async* {
    yield _loadingMore;
    if (widget.onLoadMore != null) {
      widget.onLoadMore();
    }
    await widget.loadMore();
    _loadingMore = false;
    yield _loadingMore;
    if (widget.onLoadMoreFinished != null) {
      widget.onLoadMoreFinished();
    }
  }

  @override
  void dispose() {
    _loadingMoreSubject.close();
    super.dispose();
  }
}

As you can see it's 90% the same as your original, but just replaced the ListView.builder w/ a SliverList and SliverChildBuilderDelegate.

If you'd not like to include it in the library, because this is specifically about ListViews and not Slivers that's fine. Just thought I'd make the suggestion.

Would you happen to know why the above code never calls the loadMore function, though?

@MaikuB
Copy link
Owner

MaikuB commented Jun 18, 2019

i had edited my comment for a bit clarity before you replied in case you were referring to using the widget in this package within a sliver widget or if you were essentially after an extended SliverList with support for loading more items. In case, it looks like the package I've given a link to already seems to have a version of that

@wemped
Copy link
Contributor Author

wemped commented Jun 18, 2019

Yep,

you were essentially after an extended SliverList with support for loading more items

That's exactly what I'm looking for.
And the other library does support that, but I much prefer your implementation to that one. It doesn't force me to use their idea of where a loading indicator should show or how it should handle with no items. It does let you customize how it looks, but I'd like to be able to handle some of that logic on my end.

Either way, perhaps you'll have more insight into the reason loadMore isn't getting called when using the above implementation? I can get a breakpoint to stop within the itemBuilder and it calls _loadingMoreSubject.add(true); but for some reason loadMore isn't triggered after that.

@MaikuB
Copy link
Owner

MaikuB commented Jun 20, 2019

Yeah I'd prefer the loading indicator to be decoupled as well. Don't know what's causing the issue you're seeing but at this stage I'm not looking to do an implementation for a SliverList. I'd suggest trying to ask elsewhere like Stack Overflow. You could also ask the maintainers of the other package to see if they could change their implementation

@wemped
Copy link
Contributor Author

wemped commented Jun 20, 2019

Sounds good, thanks 👍

@wemped wemped closed this as completed Jun 20, 2019
@wiradikusuma
Copy link

@wemped what do you end up using?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants