I currently have a SliverList
whose items are loaded dynamically. The issue is that once these items are loaded, the SliverList
updates without animating the changes, making the transition between loading & loaded very jarring.
I see that AnimatedList
exists but it isn't a sliver so I can't place it directly in a CustomScrollView
.
You probably know about this now, but might as well mention it here to help people.
You can use SliverAnimatedList. It achieves the required result.
SliverAnimatedList Construction:
itemBuilder
defines the way new items are built. The builder should typically return a Transition
widget, or any widget that would use the animation
parameter.
SliverAnimatedList(
key: someKey,
initialItemCount: itemCount,
itemBuilder: (context, index, animation) => SizeTransition(
sizeFactor: animation,
child: SomeWidget()
)
)
Adding/removing dynamically
You do that by using insertItem
and removeItem
methods of SliverAnimatedListState
. You access the state by either:
Key
to the SliverAnimatedList
and use key.currentState
SliverAnimatedList.of(context)
static method.In cases where you need to make changes from outside of the list, you're always going to need to use the key.
Here's a full example to clarify things. Items are added by tapping the FloatingActionButton
and are removed by tapping the item itself. I used both the key
and of(context)
ways to access the SliverAnimatedListState
.
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class SliverAnimatedListTest extends StatefulWidget {
@override
_SliverAnimatedListTestState createState() => _SliverAnimatedListTestState();
}
class _SliverAnimatedListTestState extends State<SliverAnimatedListTest> {
int itemCount = 2;
// The key to be used when accessing SliverAnimatedListState
final GlobalKey<SliverAnimatedListState> _listKey =
GlobalKey<SliverAnimatedListState>();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Sliver Animated List Test")),
// fab will handle inserting a new item at the last index of the list.
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
_listKey.currentState.insertItem(itemCount);
itemCount++;
},
),
body: CustomScrollView(
slivers: <Widget>[
SliverAnimatedList(
key: _listKey,
initialItemCount: itemCount,
// Return a widget that is wrapped with a transition
itemBuilder: (context, index, animation) =>
SizeTransition(
sizeFactor: animation,
child: SomeWidget(
index: index,
// Handle removing an item using of(context) static method.
// Returned widget should also utilize the [animation] param
onPressed: () {
SliverAnimatedList.of(context).removeItem(
index,
(context, animation) => SizeTransition(
sizeFactor: animation,
child: SomeWidget(
index: index,
)));
itemCount--;
}),
))
],
),
);
}
}
class SomeWidget extends StatelessWidget {
final int index;
final Function() onPressed;
const SomeWidget({Key key, this.index, this.onPressed}) : super(key: key);
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(20.0),
child: Center(
child: FlatButton(
child: Text("item $index"),
onPressed: onPressed,
)));
}
}
You can use Implicitly Animated Reorderable List
import 'package:implicitly_animated_reorderable_list/implicitly_animated_reorderable_list.dart';
import 'package:implicitly_animated_reorderable_list/transitions.dart';
...
SliverImplicitlyAnimatedList<Comment>(
items: comments,
areItemsTheSame: (a, b) => a.id == b.id,
itemBuilder: (BuildContext context, Animation<double> animation, Comment item, int index) {
return SizeFadeTransition(
sizeFraction: 0.7,
curve: Curves.easeInOut,
animation: animation,
child: CommentSliver(
comment: item,
),
);
},
);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With