AnimatedList
簡(jiǎn)述:一個(gè)滾動(dòng)容器,可以為item被插入或移除時(shí)添加動(dòng)畫效果。列表數(shù)據(jù)的插入和刪除有進(jìn)出場(chǎng)動(dòng)畫需要調(diào)用AnimatedListState指定的方法,只刪除原數(shù)據(jù)并調(diào)用setState方法是沒有動(dòng)畫效果的。nimatedListState.insertItem或AnimatedListState.removeItem并不會(huì)更新實(shí)際數(shù)據(jù),需要手動(dòng)處理。
獲取AnimatedListState的兩種方法:
- 通過AnimatedList.of(context)方法
AnimatedList.of(context).insertItem(index);
AnimatedList.of(context).removeItem(index, (context,animation)=>{});
- 通過設(shè)置key
final GlobalKey<AnimatedListState> _listKey = GlobalKey<AnimatedListState>();
AnimatedList(
key: _listKey,
initialItemCount: _list.length,
itemBuilder: (BuildContext context, int index, Animation animation) {
return _buildItem(_list[index].toString(), animation);
},
)
| 屬性 | 釋義 |
|---|---|
| controller → ScrollController? | 控制滾動(dòng)到的位置 |
| initialItemCount → int | 列表視圖初始化的條目數(shù)量 |
| itemBuilder → AnimatedListItemBuilder | 必須的,用于構(gòu)建視圖小部件 |
| padding → EdgeInsetsGeometry? | 滾動(dòng)視圖的內(nèi)邊距 |
| physics → ScrollPhysics? | 滾動(dòng)到邊界時(shí)的滾動(dòng)特性 |
| primary → bool? | 這是否是與父控件PrimaryScrollController相關(guān)聯(lián)的主滾動(dòng)視圖。 |
| reverse → bool | 滾動(dòng)視圖是否向閱讀方向滾動(dòng)。 |
| scrollDirection → Axis | 滾動(dòng)視圖的滾動(dòng)軸。 |
| shrinkWrap → bool | 滾動(dòng)視圖在滾動(dòng)方向上的范圍是否應(yīng)該由所查看的內(nèi)容決定。 |
| 靜態(tài)函數(shù) | 釋義 |
|---|---|
| maybeOf(BuildContext context) → AnimatedListState? | 包含給定上下文的這個(gè)類的最近實(shí)例的狀態(tài)。 |
| of(BuildContext context) → AnimatedListState | 包含給定上下文的這個(gè)類的最近實(shí)例的狀態(tài)。 |
示例1

animated_list_1.gif
實(shí)現(xiàn)一個(gè)添加item時(shí)呈現(xiàn)回彈效果動(dòng)畫,刪除item時(shí)呈現(xiàn)移動(dòng)效果的例子。
class AnimatedListExample1 extends StatefulWidget {
AnimatedListExample1({Key? key}) : super(key: key);
@override
_AnimatedListExample1State createState() {
return _AnimatedListExample1State();
}
}
class _AnimatedListExample1State extends State<AnimatedListExample1> {
//聲明列表數(shù)據(jù)
List<String> _list = ['Email','Email'];
//用來執(zhí)行添加/移除的動(dòng)畫,但是不會(huì)改變_list的數(shù)據(jù)
final GlobalKey<AnimatedListState> _listStateKey = GlobalKey<AnimatedListState>();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('AnimatedList示例1'),
),
body: AnimatedList(
key: _listStateKey,
initialItemCount: _list.length,
itemBuilder: (BuildContext context, int index, Animation<double> animation) {
return _buildItem(index, animation,true);
},
),
floatingActionButton: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
FloatingActionButton(
onPressed: () => _addItem(),
child: Icon(Icons.add),
),
SizedBox(
width: 60,
),
FloatingActionButton(
onPressed: () => _removeItem(),
child: Icon(Icons.remove),
),
],
),
);
}
//添加一條數(shù)據(jù)
void _addItem(){
_listStateKey.currentState!.insertItem(0,duration: Duration(seconds: 1));
_list.insert(0, 'Email');
}
//刪除一條數(shù)據(jù)
void _removeItem(){
_listStateKey.currentState!.removeItem(0, (context, animation) => _buildItem(0, animation,false),duration: Duration(seconds: 1));
_list.removeAt(0);
}
//返回列表ui
_buildItem(int index,Animation animation,bool slideIn){
return SlideTransition(
position: animation.drive(CurveTween(curve:slideIn? Curves.bounceOut:Curves.easeOutQuint)).drive(Tween<Offset>(begin: Offset(1,1),end: Offset(0,1))),
child: Container(
color: Colors.deepOrange[400],
margin: EdgeInsets.only(left: 16,right: 16,top: 5),
child: ListTile(
title: Text('${_list[index]}',style: TextStyle(color: Colors.white),),
trailing: IconButton(
onPressed: (){
},
icon: Icon(Icons.mail_outline,color: Colors.white,),
),
),
),
);
}
}
示例2

animated_list_2.gif
class AnimatedListExample2 extends StatefulWidget {
AnimatedListExample2({Key? key}) : super(key: key);
@override
_AnimatedListExample2State createState() {
return _AnimatedListExample2State();
}
}
class _AnimatedListExample2State extends State<AnimatedListExample2> {
//列表
final _list = [];
//列表的key
final GlobalKey<AnimatedListState> _animatedListKey = GlobalKey();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('AnimatedList示例2'),
),
body: AnimatedList(
key: _animatedListKey,
initialItemCount: 0,
padding: EdgeInsets.all(10),
itemBuilder: (context,index,animation){
return _buildItem(index,animation);
}
),
floatingActionButton: FloatingActionButton(onPressed: _addItem,child: Icon(Icons.add,color: Colors.white,),),
);
}
Widget _buildItem(int index, Animation<double> animation) {
return SizeTransition(
sizeFactor: animation,
child: Card(
margin: EdgeInsets.all(10),
color: Colors.deepOrange[400],
elevation: 10,
child: ListTile(
contentPadding: EdgeInsets.all(15),
title: Text(_list[index], style: TextStyle(fontSize: 24,color: Colors.white)),
trailing: IconButton(
onPressed: (){
_removeItem(index);
},
icon: Icon(Icons.delete,color: Colors.white,),
),
),
),
);
}
//添加新的item到列表
void _addItem(){
_list.insert(0, "Item ${_list.length + 1}");
_animatedListKey.currentState!.insertItem(0, duration: Duration(seconds: 1));
}
//刪除一條數(shù)據(jù)
void _removeItem(int index){
var temp = _list[index];
_animatedListKey.currentState!.removeItem(index,
(context, animation) {
return SizeTransition(
sizeFactor: animation,
child: Card(
margin: EdgeInsets.all(10),
color: Colors.deepOrange[400],
elevation: 10,
child: ListTile(
contentPadding: EdgeInsets.all(15),
title: Text(temp, style: TextStyle(fontSize: 24,color: Colors.white)),
trailing: IconButton(
onPressed: (){
},
icon: Icon(Icons.delete,color: Colors.white,),
),
),
),
);
},
duration: Duration(seconds: 1)
);
_list.removeAt(index);
}
}
示例3

animated_list_3.gif
class AnimatedListExample3 extends StatefulWidget {
AnimatedListExample3({Key? key}) : super(key: key);
@override
_AnimatedListExample3State createState() {
return _AnimatedListExample3State();
}
}
class _AnimatedListExample3State extends State<AnimatedListExample3> {
final _items = ['Item 0'];
final GlobalKey<AnimatedListState> _key = GlobalKey();
void _addItem() {
_items.insert(0, "Item ${_items.length + 1}");
_key.currentState!.insertItem(0, duration: Duration(seconds: 1));
}
void _removeItem(int index, BuildContext context) {
var temp = _items[index];
AnimatedList.of(context).removeItem(index, (_, animation) {
return FadeTransition(
opacity: animation,
child: SizeTransition(
sizeFactor: animation,
child: SizedBox(
height: 150,
child: Card(
margin: const EdgeInsets.symmetric(vertical: 20),
elevation: 10,
color: Colors.deepOrange[400],
child: Center(
child: Text(temp, style: TextStyle(fontSize: 28,color: Colors.white)),
),
),
),
),
);
}, duration: Duration(seconds: 1));
_items.removeAt(index);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('AnimatedList示例3'),
),
body: AnimatedList(
key: _key,
initialItemCount: _items.length,
padding: EdgeInsets.all(10),
itemBuilder: (context,index,animation){
return _buildItem(context,index,animation);
},
),
floatingActionButton: FloatingActionButton(onPressed: _addItem,child: Icon(Icons.add,color: Colors.white,),),
);
}
Widget _buildItem(BuildContext context, int index, Animation<double> animation) {
print('animation:${animation.value}');
return SlideTransition(
position: Tween<Offset>(
begin: const Offset(-1, -0.5),
end: Offset(0,0)
).animate(animation),
child: RotationTransition(
turns: animation,
child: SizeTransition(
axis: Axis.vertical,
sizeFactor: animation,
child: SizedBox(
height: 150,
child: InkWell(
onTap: ()=>_removeItem(index, context),
child: Card(
margin: EdgeInsets.symmetric(vertical: 20),
elevation: 10,
color: Colors.deepOrange[400],
child: Center(child: Text(_items[index],style: TextStyle(fontSize: 28,color: Colors.white)),),
) ,
),
),
),
),
);
}
}