强化版的ListView,可以对item进行动画处理。比如在添加、删除是item的动画。

相关组件

ListView

AnimatedList基本使用

  1. <br />【itemBuilder】 : 组件构造器 【AnimatedListItemBuilder】<br />【initialItemCount】 : 子组件数量 【int】<br />【scrollDirection】 : 滑动方向 【Axis】<br />【controller】 : 滑动控制器 【ScrollController】<br />【reverse】 : 数据是否反向 【bool】<br />【padding】 : 内边距 【EdgeInsetsGeometry】<br />![142.gif](https://cdn.nlark.com/yuque/0/2020/gif/326147/1589457469005-e1ba9daf-de98-49fd-ae8f-2ac6f363e78f.gif#align=left&display=inline&height=746&margin=%5Bobject%20Object%5D&name=142.gif&originHeight=746&originWidth=450&size=751331&status=done&style=none&width=450)
import 'package:flutter/material.dart';
class CustomAnimatedList extends StatefulWidget {
  @override
  _CustomAnimatedListState createState() => _CustomAnimatedListState();
}

class _CustomAnimatedListState extends State<CustomAnimatedList> {
  final GlobalKey<AnimatedListState> _listKey = GlobalKey<AnimatedListState>();
  ListModel<int> _list;
  int _selectedItem;
  int _nextItem;

  @override
  void initState() {
    super.initState();
    _list = ListModel<int>(
      listKey: _listKey,
      initialItems: <int>[0, 1, 2, 3],
      removedItemBuilder: _buildRemovedItem,
    );
    _nextItem = 4;
  }

  Widget _buildItem(
      BuildContext context, int index, Animation<double> animation) {
    return CardItem(
      animation: animation,
      item: _list[index],
      selected: _selectedItem == _list[index],
      onTap: () {
        setState(() {
          _selectedItem = _selectedItem == _list[index] ? null : _list[index];
        });
      },
    );
  }

  Widget _buildRemovedItem(
      int item, BuildContext context, Animation<double> animation) {
    return CardItem(
      animation: animation,
      item: item,
      selected: false,
    );
  }

  void _insert() {
    final int index =
        _selectedItem == null ? _list.length : _list.indexOf(_selectedItem);
    _list.insert(index, _nextItem++);
  }

  void _remove() {
    if (_selectedItem != null) {
      _list.removeAt(_list.indexOf(_selectedItem));
      setState(() {
        _selectedItem = null;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Container(
        color: Colors.grey.withAlpha(33),
        width: MediaQuery.of(context).size.width/2,
        child: Column(
          children: <Widget>[
            _buildBtn(),
            Container(
              width: MediaQuery.of(context).size.width/2,
              height: 300,
              child: AnimatedList(
                padding: EdgeInsets.all(10.0),
                key: _listKey,
                initialItemCount: _list.length,
                itemBuilder: _buildItem,
              ),
            )
          ],
        ));
  }

  Widget _buildBtn() => Row(
        children: <Widget>[
          IconButton(
            icon: const Icon(
              Icons.add_circle,
              color: Colors.blue,
            ),
            onPressed: _insert,
          ),
          IconButton(
            icon: const Icon(Icons.remove_circle, color: Colors.blue),
            onPressed: _remove,
          ),
        ],
      );
}

class ListModel<E> {
  ListModel({
    @required this.listKey,
    @required this.removedItemBuilder,
    Iterable<E> initialItems,
  })  : assert(listKey != null),
        assert(removedItemBuilder != null),
        _items = List<E>.from(initialItems ?? <E>[]);
  final GlobalKey<AnimatedListState> listKey;
  final dynamic removedItemBuilder;
  final List<E> _items;

  AnimatedListState get _animatedList => listKey.currentState;

  void insert(int index, E item) {
    _items.insert(index, item);
    _animatedList.insertItem(index);
  }

  E removeAt(int index) {
    final E removedItem = _items.removeAt(index);
    if (removedItem != null) {
      _animatedList.removeItem(index,
        (BuildContext context, Animation<double> animation) =>
            removedItemBuilder(removedItem, context, animation),
      );
    }
    return removedItem;
  }

  int get length => _items.length;

  E operator [](int index) => _items[index];

  int indexOf(E item) => _items.indexOf(item);
}

class CardItem extends StatelessWidget {
  const CardItem(
      {Key key,
      @required this.animation,
      this.onTap,
      @required this.item,
      this.selected: false})
      : assert(animation != null),
        assert(item != null && item >= 0),
        assert(selected != null),
        super(key: key);
  final Animation<double> animation;
  final VoidCallback onTap;
  final int item;
  final bool selected;

  @override
  Widget build(BuildContext context) {
    return SizeTransition(
      axis: Axis.vertical,
      sizeFactor: animation,
      child: Card(
        child: Container(
          color: Colors.primaries[item % Colors.primaries.length],
          child: CheckboxListTile(
            dense: true,
              title: Text(
                'Item $item',
                style: TextStyle(color: Colors.white, fontSize: 18),
              ),
              value: selected,
              onChanged: (v) => onTap()),
        ),
      ),
    );
  }
}