强化版的ListView,可以对item进行动画处理。比如在添加、删除是item的动画。
相关组件
AnimatedList基本使用
<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()),
),
),
);
}
}