image

04 Dec 2025

9K

35K

Flutter List Animations with AnimatedList

Creating dynamic and engaging user interfaces is crucial for modern mobile applications. Flutter provides powerful tools to achieve this, and animating list changes is a common requirement. While a standard ListView can display data, it lacks built-in animations for item additions or removals. This is where Flutter's AnimatedList widget shines, offering a seamless and visually appealing experience when items are inserted, removed, or reordered within a list.

Understanding AnimatedList

The AnimatedList widget is a stateful widget designed to animate the insertion and removal of items. It works by providing a global key to its state, which allows you to programmatically trigger animations for list modifications. Instead of rebuilding the entire list, AnimatedList focuses on animating only the changed items, leading to smoother transitions and better performance.

Key Components of AnimatedList

  • itemBuilder: A required callback that builds the widget for each item in the list. It provides the BuildContext, the index of the item, and an Animation<double> that can be used to animate the item as it enters or exits the list.
  • initialItemCount: The number of items that should be present in the list when it's first built.
  • key: A GlobalKey<AnimatedListState> is essential. This key provides access to the AnimatedListState, which exposes methods like insertItem and removeItem to control the list animations.

Setting Up AnimatedList

To use AnimatedList, you typically need a StatefulWidget to manage the list's state and a GlobalKey to interact with the AnimatedListState. Let's look at a basic setup:


import 'package:flutter/material.dart';

class AnimatedListExample extends StatefulWidget {
  @override
  _AnimatedListExampleState createState() => _AnimatedListExampleState();
}

class _AnimatedListExampleState extends State {
  final GlobalKey _listKey = GlobalKey();
  List _data = ['Item 0', 'Item 1', 'Item 2']; // Our list data

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('AnimatedList Demo'),
        actions: [
          IconButton(
            icon: Icon(Icons.add),
            onPressed: () => _insertItem(),
          ),
        ],
      ),
      body: AnimatedList(
        key: _listKey,
        initialItemCount: _data.length,
        itemBuilder: (context, index, animation) {
          return _buildItem(_data[index], animation, index);
        },
      ),
    );
  }

  // Helper method to build each item
  Widget _buildItem(String item, Animation animation, int index) {
    return SizeTransition(
      sizeFactor: animation,
      child: Card(
        margin: EdgeInsets.symmetric(horizontal: 10, vertical: 5),
        child: ListTile(
          title: Text(item),
          trailing: IconButton(
            icon: Icon(Icons.delete),
            onPressed: () => _removeItem(index),
          ),
        ),
      ),
    );
  }

  void _insertItem() {
    // We'll implement this next
  }

  void _removeItem(int index) {
    // We'll implement this next
  }
}

In this initial setup, we define a _data list to hold our actual items. The AnimatedList uses a GlobalKey and initialItemCount. The itemBuilder then calls _buildItem, passing the item, the animation, and its index. We use a SizeTransition here as a simple animation example, but you can use any transition widget like SlideTransition, FadeTransition, or combine them.

Adding Items to AnimatedList

To add an item, you first update your underlying data list and then call _listKey.currentState!.insertItem(). The insertItem method takes the index where the new item should be inserted and an optional duration. This triggers the animation for the new item entering the list.


  // ... inside _AnimatedListExampleState

  void _insertItem() {
    final int newIndex = _data.length; // Add to the end
    _data.add('New Item ${newIndex}'); // Update data source
    _listKey.currentState!.insertItem(
      newIndex,
      duration: const Duration(milliseconds: 500),
    );
  }

  // ... rest of the code

When insertItem is called, the itemBuilder for the new item (and potentially subsequent items if they shift) is called with the animation, allowing the new item to smoothly slide into place.

Removing Items from AnimatedList

Removing items is slightly more involved because you need to animate the item *leaving* the list. The removeItem method requires a builder argument that is responsible for building the widget of the item being removed. This builder receives the same animation as itemBuilder, allowing you to animate the item's exit.


  // ... inside _AnimatedListExampleState

  void _removeItem(int index) {
    if (_data.isEmpty) return; // Prevent errors if list is empty or index invalid

    final String removedItem = _data[index]; // Get the item before removing
    
    // Remove from the data source immediately
    _data.removeAt(index);

    // Call removeItem on the AnimatedListState
    _listKey.currentState!.removeItem(
      index,
      (context, animation) {
        // This builder is for the item that is being removed
        // It's crucial to return the widget as it was before removal
        return _buildItem(removedItem, animation, index);
      },
      duration: const Duration(milliseconds: 500),
    );
  }

  // ... rest of the code

Notice that inside the removeItem's builder, we reuse our existing _buildItem method. This ensures that the item being removed looks identical to its state before removal, making the animation visually consistent. The animation provided to this builder will run in reverse or from 1.0 down to 0.0, allowing you to animate the item shrinking, fading out, or sliding away.

Putting It All Together (Full Example)

Here's the complete code for a basic AnimatedList demonstrating add and remove operations:


import 'package:flutter/material.dart';

class AnimatedListFullExample extends StatefulWidget {
  @override
  _AnimatedListFullExampleState createState() => _AnimatedListFullExampleState();
}

class _AnimatedListFullExampleState extends State {
  final GlobalKey _listKey = GlobalKey();
  List _data = ['Item 0', 'Item 1', 'Item 2'];
  int _nextItem = 3; // To keep track of unique item names

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('AnimatedList Full Demo'),
        actions: [
          IconButton(
            icon: Icon(Icons.add),
            onPressed: () => _insertItem(),
          ),
        ],
      ),
      body: AnimatedList(
        key: _listKey,
        initialItemCount: _data.length,
        itemBuilder: (context, index, animation) {
          return _buildItem(_data[index], animation, index);
        },
      ),
    );
  }

  Widget _buildItem(String item, Animation animation, int index) {
    return SizeTransition( // Can also use SlideTransition, FadeTransition etc.
      sizeFactor: animation,
      axisAlignment: -1.0, // Align top for size transition
      child: Card(
        margin: EdgeInsets.symmetric(horizontal: 10, vertical: 5),
        color: Colors.blueGrey[50],
        child: ListTile(
          title: Text(item, style: TextStyle(fontSize: 18)),
          trailing: IconButton(
            icon: Icon(Icons.delete, color: Colors.redAccent),
            onPressed: () => _removeItem(index),
          ),
        ),
      ),
    );
  }

  void _insertItem() {
    final int newIndex = _data.length;
    _data.add('Item ${_nextItem++}');
    _listKey.currentState!.insertItem(
      newIndex,
      duration: const Duration(milliseconds: 500),
    );
  }

  void _removeItem(int index) {
    if (_data.isEmpty) return; // Prevent removing from empty list

    final String removedItem = _data[index];
    
    _data.removeAt(index); // Remove from underlying data first

    _listKey.currentState!.removeItem(
      index,
      (context, animation) {
        // This builder is for the item being removed.
        // It must return the widget as it was before removal.
        return _buildItem(removedItem, animation, index);
      },
      duration: const Duration(milliseconds: 500),
    );
  }
}

Customizing Animations

The animation object provided to both itemBuilder and the removeItem builder is an Animation<double> typically ranging from 0.0 to 1.0 (for insertion) or 1.0 to 0.0 (for removal). You can wrap your item's widget in various transition widgets:

  • SizeTransition: Animates the size of the child widget.
  • SlideTransition: Animates the position of the child widget.
  • FadeTransition: Animates the opacity of the child widget.
  • ScaleTransition: Animates the scale of the child widget.

You can also combine these or create custom AnimatedBuilder widgets for more complex effects. The duration parameter in insertItem and removeItem controls how long these animations take.

Conclusion

AnimatedList is a powerful and flexible widget for adding sophisticated list animations to your Flutter applications. By managing your data source and using the GlobalKey to interact with the AnimatedListState, you can create smooth and engaging user experiences for item insertions and removals. Remember to provide a consistent widget for the item being removed in the removeItem builder to ensure a flawless exit animation.

Related Articles

Dec 19, 2025

Building a Widget List with Sticky

Building a Widget List with Sticky Header in Flutter Creating dynamic and engaging user interfaces is crucial for modern applications. One common UI pattern th

Dec 19, 2025

Mastering Transform Scale & Rotate Animations in Flutter

Mastering Transform Scale & Rotate Animations in Flutter Flutter's powerful animation framework allows developers to create visually stunning and highly intera

Dec 19, 2025

Building a Countdown Timer Widget in Flutter

Building a Countdown Timer Widget in Flutter Countdown timers are a fundamental component in many modern applications, ranging from e-commerce platforms indica