EdsonBueno / infinite_scroll_pagination

Flutter package to help you lazily load and display pages of items as the user scrolls down your screen.
https://pub.dev/packages/infinite_scroll_pagination
MIT License
624 stars 212 forks source link

[Feature Request] AnimatedList equivalent #21

Open sm2017 opened 3 years ago

sm2017 commented 3 years ago

Is there any AnimatedList equivalent?

EdsonBueno commented 3 years ago

Hi @sm2017. First of all, I'm sorry for the delay. I was on a trip for the past few days and just got back today. The package doesn't have an AnimatedList equivalent right now. I'll definitely consider adding one in the future. Thank you for your suggestion!

sachin052 commented 3 years ago

Hi @sm2017. First of all, I'm sorry for the delay. I was on a trip for the past few days and just got back today. The package doesn't have an AnimatedList equivalent right now. I'll definitely consider adding one in the future. Thank you for your suggestion!

Hey there any update of animated list equivalent? Thanks

brianschardt commented 3 years ago

Yea any update with being able to remove items from the list in an animated way?

EdsonBueno commented 3 years ago

No updates. But let's reopen the issue in case anyone else has interest.

basketball-ico commented 3 years ago

@EdsonBueno I am very interested in this, do you know if there is a temporary way to achieve this behavior?

EdsonBueno commented 3 years ago

Hi @basketball-ico Please, take a look at this. It might help

basketball-ico commented 3 years ago

Hi, I have not managed to use an animated list. If you have time, can you please make a small example, surely someone else will also find this useful. Thank you so much

allenzhou101 commented 3 years ago

Would love to see this!

martipello commented 2 years ago

I had a play with this today and took @EdsonBueno advice I tried to implement the bare bones PagedSliverBuilder as per his link here I didn't manage to make it work and I think the problem is here

completedListingBuilder: ( context, itemBuilder, itemCount, noMoreItemsIndicatorBuilder, ) => SliverAnimatedList( initialItemCount: itemCount, itemBuilder: (context, index, animation,) { final finished = itemCount = index; return SlideTransition( position: animation.drive(offset), child: itemBuilder.call( context, index, ), ); }, ),

in the above example the variable finished points to the problem, I don't claim to be an expert so please correct me if I'm wrong, I think we can't return a single item here as we need the full list before we can start to animate it, and by the time its finished I will have a list of widgets not a single widget so this would need a new builder that accepted a list of widgets, happy to learn otherwise, here is my full widget

PagedSliverBuilder<int, InvoiceItem>( pagingController: _invoicesViewModel.getPagingController(), builderDelegate: PagedChildBuilderDelegate<InvoiceItem>( animateTransitions: false, itemBuilder: (context, invoiceItem, index) { return _invoiceItem( invoiceItem: invoiceItem, invoiceItemTileSelectState: null, ); }, firstPageErrorIndicatorBuilder: (context) => ew.ErrorWidget( showImage: true, error: _invoicesViewModel.getPagingController().error as ApiResponse, onTryAgain: () => _invoicesViewModel.getPagingController().refresh(), ), noItemsFoundIndicatorBuilder: _emptyListIndicator, newPageErrorIndicatorBuilder: (context) => _errorListItemWidget(onTryAgain: _invoicesViewModel.retryLastRequest), firstPageProgressIndicatorBuilder: (context) => const Center( child: LoadingWidget(), ), newPageProgressIndicatorBuilder: (context) => _loadingListItemWidget(), ), completedListingBuilder: ( context, itemBuilder, itemCount, noMoreItemsIndicatorBuilder, ) => SliverAnimatedList( initialItemCount: itemCount, itemBuilder: (context, index, animation,) { final finished = itemCount == index; return SlideTransition( position: animation.drive(offset), child: itemBuilder.call( context, index, ), ); }, ), loadingListingBuilder: ( context, itemBuilder, itemCount, progressIndicatorBuilder, ) => _loadingListItemWidget(), errorListingBuilder: ( context, itemBuilder, itemCount, errorIndicatorBuilder, ) => ew.ErrorWidget( showImage: true, error: _invoicesViewModel.getPagingController().error as ApiResponse, onTryAgain: () => _invoicesViewModel.getPagingController().refresh(), ), )

hermannpoilpre commented 2 years ago

Also interested

carrasc0 commented 9 months ago

Also interested on this. I am having a very bad time deleting items from the list

AnwarTuha commented 8 months ago

This would have been a great feature to have. Has any one had luck with using custom PagedListView?

Zvnimir commented 2 months ago

Here is my implementation of SliverAnimatedPagedList and an example of how I use it in my code. Im not sure if this is the most optimal way to implement it but I hope that it might help someone. I would appreciate feedback on how to improve it.

Keep in mind that I am using Flutter Hooks.

SliverAnimatedPagedList implementation:

class SliverAnimatedPagedList<T> extends StatelessWidget {
  final PagingController<int, T> pagingController;
  final PagedChildBuilderDelegate<T> builderDelegate;
  final GlobalKey<SliverAnimatedListState> animatedListKey;

  const SliverAnimatedPagedList({
    super.key,
    required this.animatedListKey,
    required this.pagingController,
    required this.builderDelegate,
  });

  @override
  Widget build(BuildContext context) {
    return PagedLayoutBuilder<int, T>(
      pagingController: pagingController,
      builderDelegate: builderDelegate,
      completedListingBuilder: (
        BuildContext context,
        Widget Function(BuildContext, int) itemWidgetBuilder,
        int itemCount,
        Widget Function(BuildContext)? noMoreItemsIndicatorBuilder,
      ) {
        return MultiSliver(
          children: [
            SliverAnimatedList(
              key: animatedListKey,
              initialItemCount: itemCount,
              itemBuilder: (
                BuildContext context,
                int index,
                Animation<double> animation,
              ) =>
                  itemWidgetBuilder(
                context,
                index,
              ),
            ),
            if (noMoreItemsIndicatorBuilder != null)
              SliverToBoxAdapter(
                child: noMoreItemsIndicatorBuilder(context),
              )
          ],
        );
      },
      loadingListingBuilder: (
        BuildContext context,
        Widget Function(BuildContext, int) itemWidgetBuilder,
        int itemCount,
        Widget Function(BuildContext) newPageProgressIndicatorBuilder,
      ) {
        return MultiSliver(
          children: [
            SliverAnimatedList(
              key: animatedListKey,
              initialItemCount: itemCount,
              itemBuilder: (
                BuildContext context,
                int index,
                Animation<double> animation,
              ) =>
                  itemWidgetBuilder(
                context,
                index,
              ),
            ),
            SliverToBoxAdapter(
              child: newPageProgressIndicatorBuilder(context),
            ),
          ],
        );
      },
      errorListingBuilder: (
        BuildContext context,
        Widget Function(BuildContext, int) itemWidgetBuilder,
        int itemCount,
        Widget Function(BuildContext) newPageErrorIndicatorBuilder,
      ) {
        return MultiSliver(
          children: [
            SliverAnimatedList(
              key: animatedListKey,
              initialItemCount: itemCount,
              itemBuilder: (
                BuildContext context,
                int index,
                Animation<double> animation,
              ) =>
                  itemWidgetBuilder(
                context,
                index,
              ),
            ),
            SliverToBoxAdapter(
              child: newPageErrorIndicatorBuilder(context),
            ),
          ],
        );
      },
      layoutProtocol: PagedLayoutProtocol.sliver,
    );
  }
}

An example of how I use it for animating the deletion of items in a notification list: Place the SliverAnimatedPagedList in a CustomScrollView

CustomScrollView(
      slivers: [
        SliverAnimatedPagedList(
          animatedListKey: animatedListKey.value,
          pagingController: pagingController,
          builderDelegate: PagedChildBuilderDelegate<Notification>(
            itemBuilder: (context, notification, index) {
              final notificationHelper = NotificationHelper.fromType(
                type: notification.type,
                l: l,
              );
              return NotificationsCard(
                title: notificationHelper.title,
                subtitle: notificationHelper.subtitle,
                timeStamp: notification.createdAt,
                isRead: notification.isRead,
                onCardPressed: () => handleNotificationPressed(
                  notification,
                ),
                onDeletePressed: () => handleNotificationDelete(
                  notification.id,
                ),
              );
            },
            firstPageProgressIndicatorBuilder: (context) => const AppLoader(),
            newPageProgressIndicatorBuilder: (context) => const AppLoader(
              size: 30,
            ),
            noItemsFoundIndicatorBuilder: (context) => NotificationsEmpty(),
          ),
        ),
      ],
    );

Create an instance of the GlobalKey which is going to be used to perform actions on the SliverAnimatedList

final animatedListKey = useState(GlobalKey<SliverAnimatedListState>());

Inside of your function which performs the deletion update the SliverAnimatedList and the PagingController

          animatedListKey.value.currentState!.removeItem(
            duration: const Duration(milliseconds: 200),
            index,
            (context, animation) {
              final notificationHelper = NotificationHelper.fromType(
                type: notification.type,
                l: l,
              );
              return NotificationsCard(
                animation: animation,
                title: notificationHelper.title,
                subtitle: notificationHelper.subtitle,
                timeStamp: notification.createdAt,
                isRead: notification.isRead,
                onCardPressed: () {},
                onDeletePressed: () {},
              );
            },
          );
          pagingController.itemList = pagingController.itemList
              ?.where((element) => element.id != args['id'])
              .toList();

Don't forget to add the transition of your liking to the item widget

Widget build(BuildContext context, WidgetRef ref) {
    return SizeTransition(
      sizeFactor: animation ?? const AlwaysStoppedAnimation(1),
      child: FadeTransition(
        opacity: animation ?? const AlwaysStoppedAnimation(1),
        child: ...
  }
adarshwebcastle commented 1 month ago

I found a simple way by using AnimatedSwitcher for the item tile, Consider if it's useful

         PagedListView.separated(
            builderDelegate: ....
                  child: AnimatedSwitcher(
                      duration: const Duration(milliseconds: 300),
                      transitionBuilder: (Widget child, Animation<double> animation) {
                        final offsetAnimation = Tween<Offset>(
                          begin: Offset((item.isBeingRemoved) ? 1 : 0, 0), // Start  from current position
                          end: const Offset(0, 0), // Slide out to the right
                        ).animate(animation);
                        return SlideTransition(
                          position: offsetAnimation,
                          child: child,
                        );
                      },
                      child:
                          (item.isBeingRemoved) ? SizedBox.shrink() : OrderItem(item: item)));
            },

When remove item

       void onRemove() {
                      item.isBeingRemoved = true;
                      notifyListeners();

                     await Future.delayed(const Duration(milliseconds: 500));
                     //update order list
                    pagingController.itemList?.remove(item);
                    pagingController.notifyListeners();
       }

Example

https://github.com/user-attachments/assets/61cfdc1b-eacf-4237-96d5-0ebe1331ad44