Flutter is a modern and powerful UI toolkit that provides a lot of customization options for building beautiful and engaging user interfaces. One of the most important aspects of building a dynamic and interactive list is animating the insertion and removal of items. Flutter provides a handy widget called AnimatedList
that makes this task very easy and straightforward. In this article, we will demonstrate how to use the AnimatedList
widget in Flutter to build a dynamic list with beautiful and professional animations.
By the end of this project we will have the following output.
First, let's import the necessary libraries and define the basic structure of our app:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Animated List Demo',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold();
}
}
In this code, we first import the Material
library, which provides a set of visual, behavioral, and motion-rich widgets. Then, we define our main widget, MyApp
, which extends the StatelessWidget
class. The MyApp
widget is the root widget of our app and contains a MaterialApp
widget that acts as the host for our UI.
Next, we define the MyHomePage
widget, which extends the StatefulWidget
class. This widget is the main content of our app and contains the dynamic list that we will build using the AnimatedList
widget. The MyHomePage
widget is associated with a State
object, _MyHomePageState
, which holds the state of the MyHomePage
widget.
Now that we have the basic structure of our app in place, let's define the list of items that we will display in our AnimatedList
. We will also define a global key for our AnimatedList
:
class _MyHomePageState extends State<MyHomePage> {
final GlobalKey<AnimatedListState> _listKey = GlobalKey<AnimatedListState>();
List<String> items = ['Item 1', 'Item 2', 'Item 3'];
// ...
}
A global key is a unique identifier that allows us to retrieve the state of a widget from anywhere in the widget tree. In this case, we use a global key to access the AnimatedListState
object associated with our AnimatedList
widget. This will allow us to insert and remove items from our list dynamically.
Next, let's define two methods that will allow us to add and remove items from our list:
class _MyHomePageState extends State<MyHomePage> {
// ...
void _addItem() {
setState(() {
items.insert(0, 'Item ${items.length + 1}');
_listKey.currentState?.insertItem(0);
});
}
void _removeItem() {
if (items.isEmpty) return;
int index = items.length - 1;
String removedItem = items.removeAt(index);
_listKey.currentState?.removeItem(
index,
(context, animation) => _buildRemovedItem(removedItem, animation),
);
}
Widget _buildRemovedItem(String item, Animation<double> animation) {
return ScaleTransition(
scale: animation,
child: Card(
child: ListTile(
title: Text(item),
),
),
);
}
This code defines two methods _addItem
and _removeItem
and a custom widget _buildRemovedItem
.
The _addItem
method is called when the "Add Item" button is pressed. It updates the list of items by inserting a new item at the beginning of the list and updates the AnimatedList
by calling its insertItem
method with the index of the new item. The insertItem
method is called on the _listKey.currentState
object, which is a reference to the state of the AnimatedList
widget. The ?
operator is used to check if _listKey.currentState
is not null before calling insertItem
, to avoid a runtime error.
The _removeItem
method is called when the "Remove Item" button is pressed. If the list of items is not empty, it removes the last item from the list and updates the AnimatedList
by calling its removeItem
method with the index of the removed item and the _buildRemovedItem
widget. The _buildRemovedItem
widget is used to define the visual representation of the removed item during the removal animation.
The _buildRemovedItem
method returns a ScaleTransition
widget, which applies a scaling animation to its child widget. The scale
property of the ScaleTransition
widget is set to the animation
argument, which represents the animation that will be used to remove the item. The child of the ScaleTransition
widget is a Card
widget that displays the removed item as a ListTile
widget with a single title
property set to a Text
widget that displays the text of the item
argument.
Finally, let's define the build
method of the _MyHomePageState
class, which will build the UI of our app:
class _MyHomePageState extends State<MyHomePage> {
// ...
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('AnimatedList Example'),
),
body: AnimatedList(
key: _listKey,
initialItemCount: items.length,
itemBuilder: (context, index, animation) {
return ScaleTransition(
scale: animation,
child: Card(
child: ListTile(
title: Text(items[index]),
),
),
);
},
),
floatingActionButton: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
FloatingActionButton(
onPressed: _addItem,
child: const Icon(Icons.add),
),
const SizedBox(height: 10),
FloatingActionButton(
onPressed: _removeItem,
child: const Icon(Icons.remove),
),
],
),
);
}
}
In the build
method, we first use the Scaffold
widget to define the basic structure of our app, including the AppBar
and the main body of the app. In the main body, we use a Container
widget to provide some padding around the contents of the body, and a Column
widget to stack the contents vertically.
Within the AnimatedList
widget, we specify the initialItemCount
, which is the number of items that the list will initially display. We also specify the itemBuilder
callback, which is called for each item in the list. The itemBuilder callback takes three arguments: the BuildContext
object, the index of the item, and an Animation object that represents the animation that will be used to insert or remove the item. In this case, we use a ScaleTransition
widget to create the animation.
Then we defines a FloatingActionButton
widget inside a Column
widget. The Column widget has two children: two FloatingActionButton
widgets. The first FloatingActionButton
has an onPressed
callback that triggers the _addItem
function, and its icon is an "add" symbol represented by the Icons.add
constant. The second FloatingActionButton has an onPressed callback that triggers the _removeItem
function, and its icon is a "remove" symbol represented by the Icons.remove
constant. A SizedBox
widget with a height of 10 is also included between the two FloatingActionButton
widgets.
With this, we have successfully demonstrated the use of the AnimatedList
widget in Flutter to create beautiful animations when inserting or removing items from a list. By using the AnimatedList
widgets, we can create dynamic and engaging user interfaces with ease
Top comments (0)