AnimatedList in Flutter is a dynamic list component that enables us to insert and remove elements dynamically. AnimatedList is a list that animates the items when they are removed or inserted, enhancing the user experience.
As we all use ListView in Flutter lists are used for displaying a collection of items in a scrollable list format. In Real-Life scenarios, we sometimes need to add or remove items from the list. This sudden change in the list may confuse the user and lead to a bad user experience. The potential solution for this is to use AnimatedList in Flutter.
Prerequisites
- Globalkey: GlobalKey is a type of key that is unique across the entire app.
- ListView: It is a commonly used scrollable list of widgets in Flutter. It displays its children one after another in a scroll direction. It's especially useful when you're not sure how many widgets you'll be displaying, or if you're loading more data in real time.
- Animations: You need a basic understanding of animations. Animations like SizeTransition, FadeTransition, SlideTransition, etc.
Constructor of the AnimatedList Class
AnimatedList AnimatedList({
Key? key,
required Widget Function(BuildContext, int, Animation<double>) itemBuilder,
int initialItemCount = 0,
Axis scrollDirection = Axis.vertical,
bool reverse = false,
ScrollController? controller,
bool? primary,
ScrollPhysics? physics,
bool shrinkWrap = false,
EdgeInsetsGeometry? padding,
Clip clipBehavior = Clip.hardEdge,
})
Properties of AnimatedList Widget
Property | Description |
|---|---|
itemBuilder | It is Required Function that builds each items in list. It is called once for each item and during any animation affecting the item. |
initialItemCount | It is used to specify the initial length of the list. It indicates that the number of items that list will start. |
scrollDirection | It is used to specify the axis along with the scrolling occurs. It takes a value of Axis type, which can be either Axis.horizontal or Axis.vertical. |
reverse | It is Boolean that specify whether the list should display its items in reverse order. |
controller | It is used to control position to which scroll view is scrolled. |
primary | It is Boolean that determines whether the widget is primary scroll view associated with parent PrimaryScrollController. |
physics | It is used to determine how list should respond to user input and how it should over scroll. It takes Object of class that extends ScrollPhysics. |
shrinkWrap | Is Boolean that determines whether the scroll view should shrink-wrap its content. |
padding | It is used to provide padding to the AnimatedList. Takes EdgeInsets Object which defines padding. |
clipBehavior | It is used to control how the widget should clip it's children. It takes a value from the Clip enum. |
Simple Example of AnimatedList
In this example we are going to display list of persons using AnimatedList. Person will be having Name and Designation. we will first create a model and then using this create list of persons. Later we will be using this to display to user. user also can delete the items in list.
Step 1: Create a new Flutter Application
Create a new Flutter application using the command Prompt. To create a new app, write the below command and run it.
flutter create app_nameTo know more about it refer this article: Creating a Simple Application in Flutter.
Step 2: Folder Structure
Follow below folder structure for better code organization.

Step 3: Create a model
Create Person Model for creating list of persons. This list is further used to diplay infomation in the form of List. Person Model is simple class with properties and Constructor.
Person.dart:
class Person {
final String name;
final String designation;
Person(this.name, this.designation);
}
Step 4: Modify the main() function in codebase
we are modifying the main() function as per our need .
// Importing the Flutter Material package for UI components
import 'package:flutter/material.dart';
// Importing a custom model class for Person data structure
import 'package:flutter_geeks/models/Person.dart';
// Entry point of the Flutter application
void main() {
runApp(const MyApp()); // Runs the app starting from MyApp widget
}
// Root widget of the application, which doesn't hold state
class MyApp extends StatelessWidget {
const MyApp({super.key}); // Constructor with optional key
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Geeks', // App title
theme: ThemeData(primarySwatch: Colors.green), // Global theme
home: const MainListScreen(), // Main screen widget
debugShowCheckedModeBanner: false, // Removes debug banner
);
}
}
In the main() function, runApp() function is called which is taking the home as MyApp(). debugShowCheckedModeBanner is set to false , it will disable the debug banner from the app.
MyApp is a stateless widget which is returning MainListScreen widget.
Step 5: Create the MainListScreen Widget
This stateful widget that returns a new instance of _MainListScreenState when its createState mathod is called.
import 'package:animated_list_example/models/Person.dart';
import 'package:flutter/material.dart';
class MainListScreen extends StatefulWidget {
const MainListScreen({super.key});
@override
State<MainListScreen> createState() => _MainListScreenState();
}
class _MainListScreenState extends State<MainListScreen> {
@override
Widget build(BuildContext context) {
return Scaffold();
}
}
Step 6: Defining the GlobalKey and person list
final GlobalKey<AnimatedListState> _listKey = GlobalKey<AnimatedListState>();
List<Person> people = [];
GlobalKey<AnimatedListState> is defined to keep reference to AnimatedListState and List<Person> to store the list of people.
Step 7: Define Add Person Method
// Function to add a new Person to the list
void _addPerson(personName, personDesignation) {
people.add(Person(personName, personDesignation)); // Add to list
_listKey.currentState!.insertItem(
people.length - 1, // Insert at end
duration: const Duration(milliseconds: 1000), // Animation duration
);
}
This method takes a person's name and designation as arguments, creates a new Person object, adds it to the people list, and inserts it into the AnimatedList.
Step 8: Define remove person method
// Function to remove a Person from the list by index
void _removePerson(int index) {
_listKey.currentState!.removeItem(
index, // Remove from list at index
(context, animation) => SizeTransition(
sizeFactor: animation, // Animate shrinking
child: const Card(
margin: EdgeInsets.all(10),
color: Colors.red,
child: ListTile(
title: Text(
"Deleted",
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
),
),
),
duration: const Duration(milliseconds: 500), // Removal animation
);
people.removeAt(index); // Remove from data list
}
This method takes an index as an argument, removes the person at that index from the AnimatedList and the people list.
Step 9: Override the InitState() method
// Called when the widget is inserted into the widget tree
@override
void initState() {
WidgetsBinding.instance.addPostFrameCallback((_) {
// Pre-populating the list with dummy people
_addPerson('Ram', 'Web Developer');
_addPerson('Abhi', 'Software Engineer');
_addPerson('Anu', 'Software Engineer');
_addPerson('Sita', 'Data Scientist');
_addPerson('Raj', 'DevOps Engineer');
});
super.initState();
}
This method is called once when the State object is created. It adds a few people to the list after the first frame has been displayed.
Step 10: Implement AnimatedList
AnimatedList(
key: _listKey, // Key for controlling the list
initialItemCount: 0, // Start with empty list
itemBuilder: (context, index, animation) {
return SizeTransition(
key: UniqueKey(), // Unique key for each item
sizeFactor: animation, // Animation for list item entry
child: Card(
margin: const EdgeInsets.all(8.0), // Card margin
child: ListTile(
tileColor: Colors.grey[200], // Background color
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10), // Rounded edges
),
title: Text(
people[index].name, // Display name
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.green,
),
),
subtitle: Text(
people[index].designation, // Display designation
style: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.bold,
color: Colors.grey,
),
),
trailing: IconButton(
style: ButtonStyle(
backgroundColor: WidgetStateProperty.all<Color>(Colors.black12),
shape: WidgetStateProperty.all<RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
),
),
icon: const Icon(Icons.delete, color: Colors.red), // Delete icon
onPressed: () {
_removePerson(index); // Trigger remove
},
),
),
),
);
},
),
We are creating AnimatedList. The AnimatedList uses a GlobalKey to maintain state and animate items when they are inserted or removed. The itemBuilder function creates each item in the list, which is a Card containing a ListTile with person's details. The ListTile also includes a delete IconButton that removes the person from the list when pressed. The SizeTransition widget is used to animate the size of the items during insertion and removal.
Step 11: Add FloatingActionButton
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.green, // FAB color
foregroundColor: Colors.white,
onPressed: () {
_showDialog(); // Show dialog on FAB press
},
child: const Icon(Icons.add), // FAB icon
),
// pop up dialog for adding new person
TextEditingController nameController = TextEditingController();
TextEditingController designationController = TextEditingController();
// Function to show the add-person dialog
_showDialog() {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('Add Person'), // Dialog title
content: Column(
children: <Widget>[
// Input for Name
TextField(
controller: nameController,
decoration: InputDecoration(
border: OutlineInputBorder(),
hintText: "Enter Name",
),
),
SizedBox(height: 10),
// Input for Designation
TextField(
controller: designationController,
decoration: InputDecoration(
border: OutlineInputBorder(),
hintText: "Enter Designation",
),
),
],
),
actionsAlignment: MainAxisAlignment.spaceBetween, // Align buttons apart
actions: <Widget>[
// Cancel Button
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.grey,
foregroundColor: Colors.white,
),
onPressed: () {
Navigator.of(context).pop(); // Close dialog
},
child: Text('Cancel'),
),
// Add Button
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.green,
foregroundColor: Colors.white,
),
onPressed: () {
_addPerson(nameController.text, designationController.text); // Add person
nameController.clear(); // Clear text field
designationController.clear();
Navigator.of(context).pop(); // Close dialog
},
child: Text('Add'),
),
],
);
},
);
}
This button will show a dialog which will take Person name and person designation from the user once user clicked on add button _addPerson() method is called to add new person in the list.
Complete Source Code
// main.dart
// Importing the Flutter Material package for UI components
import 'package:flutter/material.dart';
// Importing a custom model class for Person data structure
import 'package:flutter_geeks/models/Person.dart';
// Entry point of the Flutter application
void main() {
runApp(const MyApp()); // Runs the app starting from MyApp widget
}
// Root widget of the application, which doesn't hold state
class MyApp extends StatelessWidget {
const MyApp({super.key}); // Constructor with optional key
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Geeks', // App title
theme: ThemeData(primarySwatch: Colors.green), // Global theme
home: const MainListScreen(), // Main screen widget
debugShowCheckedModeBanner: false, // Removes debug banner
);
}
}
// Main screen widget with mutable state
class MainListScreen extends StatefulWidget {
const MainListScreen({super.key}); // Constructor
@override
State<MainListScreen> createState() => _MainListScreenState(); // Creates state
}
// State class for MainListScreen
class _MainListScreenState extends State<MainListScreen> {
// Key to uniquely identify and control the AnimatedList
final GlobalKey<AnimatedListState> _listKey = GlobalKey<AnimatedListState>();
// List to hold Person objects
List<Person> people = [];
// Controllers for name and designation input fields
TextEditingController nameController = TextEditingController();
TextEditingController designationController = TextEditingController();
// Function to add a new Person to the list
void _addPerson(personName, personDesignation) {
people.add(Person(personName, personDesignation)); // Add to list
_listKey.currentState!.insertItem(
people.length - 1, // Insert at end
duration: const Duration(milliseconds: 1000), // Animation duration
);
}
// Function to remove a Person from the list by index
void _removePerson(int index) {
_listKey.currentState!.removeItem(
index, // Remove from list at index
(context, animation) => SizeTransition(
sizeFactor: animation, // Animate shrinking
child: const Card(
margin: EdgeInsets.all(10),
color: Colors.red,
child: ListTile(
title: Text(
"Deleted",
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
),
),
),
duration: const Duration(milliseconds: 500), // Removal animation
);
people.removeAt(index); // Remove from data list
}
// Called when the widget is inserted into the widget tree
@override
void initState() {
WidgetsBinding.instance.addPostFrameCallback((_) {
// Pre-populating the list with dummy people
_addPerson('Ram', 'Web Developer');
_addPerson('Abhi', 'Software Engineer');
_addPerson('Anu', 'Software Engineer');
_addPerson('Sita', 'Data Scientist');
_addPerson('Raj', 'DevOps Engineer');
});
super.initState();
}
// Building the UI
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('GeeksForGeeks'), // AppBar title
backgroundColor: Colors.green, // AppBar background
foregroundColor: Colors.white, // AppBar text/icon color
),
body: AnimatedList(
key: _listKey, // Key for controlling the list
initialItemCount: 0, // Start with empty list
itemBuilder: (context, index, animation) {
return SizeTransition(
key: UniqueKey(), // Unique key for each item
sizeFactor: animation, // Animation for list item entry
child: Card(
margin: const EdgeInsets.all(8.0), // Card margin
child: ListTile(
tileColor: Colors.grey[200], // Background color
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10), // Rounded edges
),
title: Text(
people[index].name, // Display name
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.green,
),
),
subtitle: Text(
people[index].designation, // Display designation
style: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.bold,
color: Colors.grey,
),
),
trailing: IconButton(
style: ButtonStyle(
backgroundColor: WidgetStateProperty.all<Color>(
Colors.black12,
),
shape: WidgetStateProperty.all<RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
),
),
icon: const Icon(
Icons.delete,
color: Colors.red,
), // Delete icon
onPressed: () {
_removePerson(index); // Trigger remove
},
),
),
),
);
},
),
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.green, // FAB color
foregroundColor: Colors.white,
onPressed: () {
_showDialog(); // Show dialog on FAB press
},
child: const Icon(Icons.add), // FAB icon
),
);
}
// Function to show the add-person dialog
_showDialog() {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('Add Person'), // Dialog title
content: Column(
children: <Widget>[
// Input for Name
TextField(
controller: nameController,
decoration: InputDecoration(
border: OutlineInputBorder(),
hintText: "Enter Name",
),
),
SizedBox(height: 10),
// Input for Designation
TextField(
controller: designationController,
decoration: InputDecoration(
border: OutlineInputBorder(),
hintText: "Enter Designation",
),
),
],
),
actionsAlignment:
MainAxisAlignment.spaceBetween, // Align buttons apart
actions: <Widget>[
// Cancel Button
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.grey,
foregroundColor: Colors.white,
),
onPressed: () {
Navigator.of(context).pop(); // Close dialog
},
child: Text('Cancel'),
),
// Add Button
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.green,
foregroundColor: Colors.white,
),
onPressed: () {
_addPerson(
nameController.text,
designationController.text,
); // Add person
nameController.clear(); // Clear text field
designationController.clear();
Navigator.of(context).pop(); // Close dialog
},
child: Text('Add'),
),
],
);
},
);
}
}
// models/Person.dart
class Person {
final String name;
final String designation;
Person(this.name, this.designation);
}
Output:
Conclusion
In this article we have created a simple app which displays list of persons having the name and designation using AnimatedList , we have used SizedTransition animation. We have also looked at the constructor and properties of AnimatedList in flutter.