Open sm2017 opened 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!
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
Yea any update with being able to remove items from the list in an animated way?
No updates. But let's reopen the issue in case anyone else has interest.
@EdsonBueno I am very interested in this, do you know if there is a temporary way to achieve this behavior?
Hi @basketball-ico Please, take a look at this. It might help
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
Would love to see this!
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(), ), )
Also interested
Also interested on this. I am having a very bad time deleting items from the list
This would have been a great feature to have. Has any one had luck with using custom PagedListView?
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: ...
}
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
Is there any
AnimatedList
equivalent?