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

How to use dismissable widget with HugeListView #7

Open
3rpse-ai opened this issue Jul 18, 2022 · 10 comments
Open

How to use dismissable widget with HugeListView #7

3rpse-ai opened this issue Jul 18, 2022 · 10 comments

Comments

@3rpse-ai
Copy link

First of all - thx for the nice package!

I have following use case:
Similar to you I have a huge list consisting not of a dictionary but names.
What I want to provide to users is the possibility to either like or dislike a name entry. For that I am using a dismissible widget.
Now on swiping the dismissible widget I need to remove the respective entry from the list, however I have not found a solution for that yet (on standard listviews with a builder I would simply remove the respective item from the underlying list and that's it).

I could use a regular Listview.builder approach for that but your package was the only one which allowed for having a draggable scrollbar without completely freezing once I start dragging.

Is there any (easy) solution to achieving this?

@deakjahn
Copy link
Owner

That might be a tough question, I never needed modification in the list, just display the fixed items. I would first think about invalidating and reloading the current page but I doubt it will look nice. Do you also need the nice animation?

@3rpse-ai
Copy link
Author

The animation is handled by the dismissible widget itself so that is given by default. The tricky part is that the respective item needs to be removed from the underlying list. I already tried to modify the HugeListView widget to allow for that but I got only halfway there yet :)

@deakjahn
Copy link
Owner

The widget has a ScrollablePositionedList inside that, in turn, has a PositionedList. I have a hunch that this is where you would need to implement your removing behavior, just that this means you can no longer simply use those as external packages but copy their relevant code into your own app instead for modification. Kinda what I needed to do with the DraggableScrollbar in the first place. I needed to modify that, so I couldn't just use it out-of-the-box any more.

@3rpse-ai
Copy link
Author

So I managed to implement what I need. In order to do so I needed following features:
-> access to the internal state of HugeListView (forcing a reload)
-> access to the cached map
-> access to the items within a page

For that I introduced a simple controller class:

class HugeListViewController {
  void Function()? onReload;
  void Function(bool reloadPage)? onInvalidateList;
  void reload() {
    if (onReload != null) {
      onReload!();
    }
  }

  void invalidateList(bool reloadPage) {
    if (onInvalidateList != null) {
      onInvalidateList!(reloadPage);
    }
  }
}

controller.reload() is aimed at forcing a setState call in HugeListView.
controller.invalidateList() is aimed at forcing a full reload of the list.

Now in HugeListView I changed following:

added a method to invalidate the cache:

void _invalidateCache() {
    final keys = map.keys;
    for (final key in keys) {
      cache.invalidate(key);
    }
  }

Then made use of the new controller class

class MyHugeListView<T> extends StatefulWidget {
  final HugeListViewController? listViewController;
  ...

And set the callbacks in initState:

@override
void initState() {
  super.initState();

  _initCache();
  listener.itemPositions.addListener(_sendScroll);
  widget.listViewController?.onReload = () => _doReload(0);
  widget.listViewController?.onInvalidateList = (bool reloadPage) {
    _invalidateCache();
    if (reloadPage) {
      _doReload(0);
    }
  };
}

Now lastly in order to "silently" - so without a reload remove listView items I added the page to the builder callback:

typedef HugeListViewItemBuilder<T> = Widget Function(BuildContext context, int index, T entry, List<T> entries);

With that I can now do the following when passing a HugeListViewController controller to a hugelistview:
Remove items and invalidating the cache so that there is no rebuild, but on loading the items the indices are not messed up.
Remove item, reload and then invalidate the cache without reload (lets flutter optimize the building of the list meaning you never get to see any placeholders this way)
Completely reload the list with invalidateList(true) which allows me to e.g. apply filters to the pageFuture (which I actually needed).

Just wanted to share my solution here in case you are interested as it is actually just an extension of you implementation without changing any functionalities.

@3rpse-ai
Copy link
Author

3rpse-ai commented Jul 20, 2022

Ok actually I needed to do one more thing to make this work as now the totalItemCount can change.

I enabled setting the totalItemCount on via the controller, and added a property to the state:
ValueNotifier<int> _totalItemCount

I initialize the totalItemCount in initState and use a ValueListenableBuilder to do a targeted rebuild on the DraggableScrollbar. This way I can update the _totalItemCount without reloading the whole page.

return ValueListenableBuilder<int>(
    valueListenable: _totalItemCount,
    builder: (context, value, child) {
      return DraggableScrollbar(
        key: scrollKey,
        totalCount: value,
        initialScrollIndex: widget.startIndex,
        scrollDirection: widget.scrollDirection,
        onChange: (position) {
          widget.controller
              ?.jumpTo(index: (position * widget.totalCount).floor());
        },
        scrollThumbBuilder: widget.thumbBuilder,
        backgroundColor: widget.thumbBackgroundColor,
        drawColor: widget.thumbDrawColor,
        heightScrollThumb: widget.thumbHeight,
        currentFirstIndex: _currentFirst(),
        child: child!,
      );
    },
    child: ScrollablePositionedList.builder(
    ....

@deakjahn
Copy link
Owner

Nice, a controller is always a good solution. Could you wrap it up into a PR? Maybe even adding a few words to the README describing the possibilities.

@3rpse-ai
Copy link
Author

Sure thing! Will just clean up the implementation and test it a bit more 👍

@Xsims
Copy link

Xsims commented Sep 2, 2022

Sure thing! Will just clean up the implementation and test it a bit more 👍

Would love to use this new feature !

@Xsims
Copy link

Xsims commented Sep 2, 2022

Sure thing! Will just clean up the implementation and test it a bit more 👍

Would love to use this new feature !

Your explanation and code was very clear because I successfully manage to invalidate the cache after filtering my list 🤩 but still would love to see this feature in a PR if you've got the time :)

@JWambaugh
Copy link

JWambaugh commented Jul 20, 2023

I have created a PR that implements @3rpse-ai 's suggested changes here:
#13

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

4 participants