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

[Feature Request] AnimatedList equivalent #21

Open
sm2017 opened this issue Nov 14, 2020 · 14 comments
Open

[Feature Request] AnimatedList equivalent #21

sm2017 opened this issue Nov 14, 2020 · 14 comments
Labels
feature New feature or request

Comments

@sm2017
Copy link

sm2017 commented Nov 14, 2020

Is there any AnimatedList equivalent?

@EdsonBueno
Copy link
Owner

EdsonBueno commented Nov 21, 2020

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
Copy link

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
Copy link

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

@EdsonBueno EdsonBueno reopened this Mar 2, 2021
@EdsonBueno
Copy link
Owner

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

@EdsonBueno EdsonBueno changed the title AnimatedList equivalent [Feature Request] AnimatedList equivalent Mar 3, 2021
@basketball-ico
Copy link

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

@EdsonBueno
Copy link
Owner

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

@basketball-ico
Copy link

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
Copy link

Would love to see this!

@martipello
Copy link

martipello commented Nov 21, 2021

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
Copy link

Also interested

@carrasc0
Copy link

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

@AnwarTuha
Copy link

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

@Zvnimir
Copy link

Zvnimir commented Jul 18, 2024

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
Copy link

adarshwebcastle commented Aug 20, 2024

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

EXAMPLE.mov

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

No branches or pull requests