DEV Community

Flutter Tanzania
Flutter Tanzania

Posted on

Building a Dynamic List with Animated Insertions and Removals using Flutter's AnimatedList Widget

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

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();
  }
}
Enter fullscreen mode Exit fullscreen mode

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'];

  // ...
}
Enter fullscreen mode Exit fullscreen mode

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),
        ),
      ),
    );
  }
Enter fullscreen mode Exit fullscreen mode

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),
          ),
        ],
      ),
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

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)