Building a Dynamic List with Animated Insertions and Removals using Flutter's AnimatedList Widget
Published: 2023-02-07 19:56:38

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.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4y2kyqomh8tyqrs8v5nc.gif)



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