TatsuUkraine / flutter_sticky_infinite_list

Multi directional infinite list with Sticky headers for Flutter applications
BSD 2-Clause "Simplified" License
341 stars 31 forks source link

Grouping? #39

Closed wiradikusuma closed 4 years ago

wiradikusuma commented 4 years ago

I might be missing something obvious, but if the builder has both header and content, how do I make grouping? E.g. the header would be "A" and the content would be "Abby", "Adam", "Ahmad".

TatsuUkraine commented 4 years ago

You can always create hash map of arrays from the original array. Where key will be your group label and value - array of items that belongs to this group. Or anything like this

TatsuUkraine commented 4 years ago

at least it's first stuff that came into my mind) For instance, you can have a lazy fetch with 10 items per stem and 25 items overall. Let's imagine you have 5 A's, 7 B's, 10 C's, and 3 E's.

What you can do. So you fetch the first 10. There you have 5 A's and 5 B's. You group it by the first letter. And get 2 groups with 5 items in each.

During the build of your list, you render group count + 1. +1 is needed to render spinner and queue next chunk fetch.

As soon as the next 10 fetched you group it again and rerender. Now you have 3 groups - A with 5, B with 7, and C with 8. Same as before you render the list with group count +1 since you know that it's not all.

You fetch the next 10. as soon as it fetched you group it and rerender, but this time with just total count equal to group count.

In all this story spinner will be rendered as soon as next (+1) item will be rendered. And it will be rendered only if this +1 item is satisfying Flutter's render rules (el position according to extent config)

Obviously it's just a raw thought how this can be done.

wiradikusuma commented 4 years ago

Thank you, that's what I ended up doing. I was just thinking if there's something baked in.

Tiger3030 commented 3 years ago

I also need grouping, but do not fully understand the explanation here.

I want to built something like this (no lazy fetching needed):

Screen(1)

For this example I have put a ListView inside contentBuilder of InfiniteListItem, which works as expected, but has very bad performance with a lot of data. Is there a better way to do this?

TatsuUkraine commented 3 years ago

Hard to say with the code. But since you're using ListView inside content builder, I assume that you are using shrinkWrap to make it work?

Tiger3030 commented 3 years ago

No, I wrapped it with a container. Here is the full code:

Map content = {
    "A": [1, 2, 3],
    "B": [1, 2, 3],
    "C": [1, 2, 3],
    "D": [1, 2, 3]
  };

Container(
  height: 180,
  child: InfiniteList(
      direction: InfiniteListDirection.single,
      posChildCount: content.length,
      scrollDirection: Axis.horizontal,
      builder: (BuildContext context, int index) {
        return InfiniteListItem(
            positionAxis: HeaderPositionAxis.crossAxis,
            headerBuilder: (BuildContext context) {
              return Text('Header ' +
                  content.keys.elementAt(index));
            },
            contentBuilder: (BuildContext context) {
              return Container(
                  width: (content.values
                              .elementAt(index)
                              .length *
                          95)
                      .toDouble(),
                  height: 150,
                  child: ListView.builder(
                    itemCount: content.values
                        .elementAt(index)
                        .length,
                    scrollDirection: Axis.horizontal,
                    itemBuilder: (context, index2) {
                      return Container(
                        width: 85,
                        color: Colors.teal,
                        padding: EdgeInsets.all(8),
                        margin:
                            EdgeInsets.only(right: 10),
                        child: Center(
                            child: Text('Content ' +
                                content.values
                                    .elementAt(index)
                                    .elementAt(index2)
                                    .toString())),
                      );
                    },
                  ));
            });
      }),
);

As far as I understand it, the problem with this approach is that as soon as the first element of the ListView enters the cache area, the whole ListView is rendered at once, which leads to the bad performance.

TatsuUkraine commented 3 years ago

case here that ListView in this case (not particularly with my package, but in general) doesn't give any benefit at all. Have you tried to use a simple Row instead?

Tiger3030 commented 3 years ago

The reason why I used ListView instead of Row is the ListView.builder, which Row does not have.

But I have tried it anyway and it has the same problem that the whole Row is rendered as soon as the beginning of the row enters the cache area. I think what I want to achieve is not possible with good performance with this package, because it has no grouping built in.

TatsuUkraine commented 3 years ago

The reason why I used ListView instead of Row is the ListView.builder, which Row does not have.

builder in a list view works in a little bit different way than you think. In order to get lazy build you need this block to be scrolled, since flutter defined what needs to be rendered based on viewport.

I think what I want to achieve is not possible with good performance with this package

hard to say anything regarding performance, since I've been using this package with big number of items in each Item content and never had any problems.

This package is focused on rendering a known number of items within a group, and an infinite number of groups. The opposite approach is used by flutter_sticky_header - with finite number of groups and potentially infinite number of items in each. You can follow conversation here #34 to get more info regarding differences

Tiger3030 commented 3 years ago

I have finally found a solution: I only use your package for the header and let the contentBuilder return an empty container with the right width for my content. Then I use ListView.builder for my content and sync the two scrollviews using https://pub.dev/packages/sync_scroll.

Thank you for your help.