AnimatedList
is a stateful widget, and its corresponding state type is AnimatedListState
.
The structure of AnimatedList is as follows:
const AnimatedList({
Key? key,
required Widget Function(BuildContext, int, Animation<double>) itemBuilder, // Data builder
int initialItemCount = 0, // Total length of initial data
Axis scrollDirection = Axis.vertical, // Scroll direction
bool reverse = false, // Whether to display from bottom to top
ScrollController? controller, // Scroll controller, optional, controls the scroll position of the list
bool? primary, // Whether to associate with the parent scroll
ScrollPhysics? physics, // How to respond to user actions
bool shrinkWrap = false,
EdgeInsetsGeometry? padding, // Padding
Clip clipBehavior = Clip.hardEdge,
})
There are two methods in the state to add and remove elements in the list:
Note that if the list is bound to a data source list, you cannot directly achieve the effect of updating the list by removing or adding elements. You also need to execute the following methods:
// Insert element
void insertItem(
int index,
{Duration duration = _kDuration}
)
// Remove element
void removeItem(
int index,
Widget Function(BuildContext, Animation<double>) builder, {
Duration duration = _kDuration,
})
When adding or removing, the specified animation will be displayed.
When using insertItem and removeItem, you usually need to use a globalKey, but the globalKey needs to specify the generic type in order to call the methods to add and remove list elements:
final globalKey = GlobalKey<AnimatedListState>();
To add an element, you only need to:
list.add("New data");
globalKey.currentState!.insertItem(list.length - 1);
Removing an element is more complicated because you need to display the animation when the element leaves and then delete the corresponding entry in the data source:
Usually, we need to create a
_buildItem
method to dynamically create child elements. When executingremoveItem
, first create an identical child element in the callback, fade it out using animation, and finally delete the data in the data array.
// Remove element
_delItem(index) {
globalKey.currentState!.removeItem(index, (context, animation) {
// Get the element to be removed and fade it out
var removeItem = _buildItem(index);
return FadeTransition(
// Execute animation
opacity: animation,
child: removeItem,
);
});
list.removeAt(index); // Delete data from the array
}
Complete code#
// AnimatedList
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
// Define a globalKey
final globalKey = GlobalKey<AnimatedListState>();
List<String> list = ["First data", "Second data"];
bool flag = true;
Widget _buildItem(index) {
return ListTile(
title: Text(list[index]),
trailing: IconButton(
icon: const Icon(Icons.delete),
onPressed: () {
// Execute deletion
_delItem(index);
},
),
);
}
// Remove element
_delItem(index) {
globalKey.currentState!.removeItem(index, (context, animation) {
// Get the element to be removed and fade it out
var removeItem = _buildItem(index);
list.removeAt(index); // Delete data from the array
return FadeTransition(
// Execute animation
opacity: animation,
child: removeItem,
);
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("kano")),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.add),
// This will not work on AnimatedList by directly manipulating the list
onPressed: () {
// setState(() {
// list.add("New data");
// });
/// Use the insertItem method in AnimatedList
list.add("New data");
globalKey.currentState!.insertItem(list.length - 1);
},
),
// Animated list with animation
body: AnimatedList(
key: globalKey,
initialItemCount: list.length,
itemBuilder: (context, index, animation) {
// Fade transition opacity enters from 0 to 1 and exits in reverse
return FadeTransition(opacity: animation);
// Scale transition
// return ScaleTransition(scale: animation);
},
),
);
}
}