Skip to content

Commit 2a24bab

Browse files
update
1 parent 371bc46 commit 2a24bab

File tree

7 files changed

+738
-0
lines changed

7 files changed

+738
-0
lines changed
Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
// Copyright 2017 The Chromium Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
import 'package:flutter/material.dart';
5+
6+
/// 本示例来源于官方文档
7+
8+
/// 这里显示了一个与应用程序特定的 ListModel 保持同步的卡片列表,
9+
/// 当条item被添加到model或从model中删除时,相应的卡片会以动画的方式在UI中插入或移除。
10+
/// 点击一个条目选择它,再次点击取消选择。点击’+’在选定条目插入一个条目,点击’-‘移除选定条目。
11+
/// tap 处理器添加或删除 ListModel<E> 中的条目,这是 List<E> 的简单封装,可使 AnimatedList 保持同步。
12+
/// 列表模型为其动画列表提供了一个 GlobalKey ,它使用该键来调用由 AnimatedListState 定义的 insertItem 和 removeItem 方法。
13+
void main() {
14+
runApp(new AnimatedListSample());
15+
}
16+
17+
class AnimatedListSample extends StatefulWidget {
18+
@override
19+
_AnimatedListSampleState createState() => new _AnimatedListSampleState();
20+
}
21+
22+
class _AnimatedListSampleState extends State<AnimatedListSample> {
23+
final GlobalKey<AnimatedListState> _listKey =
24+
new GlobalKey<AnimatedListState>();
25+
ListModel<int> _list;
26+
int _selectedItem;
27+
int _nextItem; // 当用户按下“+”按钮时插入的下一个item。
28+
29+
@override
30+
void initState() {
31+
super.initState();
32+
_list = new ListModel<int>(
33+
listKey: _listKey,
34+
initialItems: <int>[0, 1, 2],
35+
removedItemBuilder: _buildRemovedItem,
36+
);
37+
_nextItem = 3;
38+
}
39+
40+
/// 在从列表中删除后,用于构建项目。
41+
/// 这个方法是必需的,因为移除的item在动画完成之前是可见的(尽管它已经在这个列表模型中消失了)。
42+
/// 控件被 [AnimatedListState.removeItem] 方法的 [AnimatedListRemovedItemBuilder] 参数所调用。
43+
Widget _buildRemovedItem(
44+
int item, BuildContext context, Animation<double> animation) {
45+
return new CardItem(
46+
animation: animation,
47+
item: item,
48+
selected: false,
49+
//这里没有手势检测:我们不希望删除的项目是交互式的。
50+
);
51+
}
52+
53+
@override
54+
Widget build(BuildContext context) {
55+
return new MaterialApp(
56+
home: new Scaffold(
57+
appBar: new AppBar(
58+
title: const Text('AnimatedList'),
59+
actions: <Widget>[
60+
new IconButton(
61+
icon: const Icon(Icons.add_circle),
62+
onPressed: _insert,
63+
tooltip: 'insert a new item',
64+
),
65+
new IconButton(
66+
icon: const Icon(Icons.remove_circle),
67+
onPressed: _remove,
68+
tooltip: 'remove the selected item',
69+
),
70+
],
71+
),
72+
body: new Padding(
73+
padding: const EdgeInsets.all(16.0),
74+
child: new AnimatedList(
75+
key: _listKey,
76+
initialItemCount: _list.length,
77+
itemBuilder: _buildItem,
78+
),
79+
),
80+
),
81+
);
82+
}
83+
84+
// 插入下一个item到列表model中
85+
void _insert() {
86+
final int index =
87+
_selectedItem == null ? _list.length : _list.indexOf(_selectedItem);
88+
_list.insert(index, _nextItem++);
89+
}
90+
91+
// 从列表model中移除选中的item
92+
void _remove() {
93+
if (_selectedItem != null) {
94+
_list.removeAt(_list.indexOf(_selectedItem));
95+
setState(() {
96+
_selectedItem = null;
97+
});
98+
}
99+
}
100+
101+
/// 用于构建未被删除的item。
102+
Widget _buildItem(
103+
BuildContext context, int index, Animation<double> animation) {
104+
return new CardItem(
105+
animation: animation,
106+
item: _list[index],
107+
selected: _selectedItem == _list[index],
108+
onTap: () {
109+
setState(() {
110+
_selectedItem = _selectedItem == _list[index] ? null : _list[index];
111+
});
112+
},
113+
);
114+
}
115+
}
116+
117+
/// 将Dart的List与动画列表保持同步。
118+
/// [insert][removeAt]方法应用于内部列表和属于[listKey]的动画列表。
119+
/// 这个类只公开示例应用程序所需的Dart列表API。很容易添加更多的列表方法,
120+
/// 但是修改列表的方法必须按照[AnimatedListState.insertItem][AnimatedList.removeItem]对动画列表进行相同的更改。
121+
class ListModel<E> {
122+
final GlobalKey<AnimatedListState> listKey;
123+
final dynamic removedItemBuilder;
124+
final List<E> _items;
125+
126+
ListModel({
127+
@required this.listKey,
128+
@required this.removedItemBuilder,
129+
Iterable<E> initialItems,
130+
}) : assert(listKey != null),
131+
assert(removedItemBuilder != null),
132+
_items = new List<E>.from(initialItems ?? <E>[]);
133+
134+
AnimatedListState get _animatedList => listKey.currentState;
135+
136+
int get length => _items.length;
137+
138+
E operator [](int index) => _items[index];
139+
140+
int indexOf(E item) => _items.indexOf(item);
141+
142+
void insert(int index, E item) {
143+
_items.insert(index, item);
144+
_animatedList.insertItem(index);
145+
}
146+
147+
E removeAt(int index) {
148+
final E removedItem = _items.removeAt(index);
149+
if (removedItem != null) {
150+
// 匿名函数,省去参数类型也是可以的。
151+
// 相当于 _animatedList.removeItem(index, (BuildContext context, Animation<double> animation) {
152+
_animatedList.removeItem(index, (context, animation) {
153+
return removedItemBuilder(removedItem, context, animation);
154+
});
155+
}
156+
return removedItem;
157+
}
158+
}
159+
160+
/// 将其整数项显示为“item N”,其颜色基于该item的值。如果选择为true,文本将显示为teal颜色。
161+
/// 这个控件的高度(高度是128,我们传入的)基于动画参数(这个值介于0到128之间),因为动画从0.0到1.0不等。
162+
class CardItem extends StatelessWidget {
163+
// 动画
164+
final Animation<double> animation;
165+
166+
// 监听事件
167+
final VoidCallback onTap;
168+
169+
// item
170+
final int item;
171+
172+
// 是否选中
173+
final bool selected;
174+
175+
const CardItem(
176+
{Key key,
177+
@required this.animation,
178+
this.onTap,
179+
@required this.item,
180+
this.selected: false})
181+
: assert(animation != null),
182+
assert(item != null && item >= 0),
183+
assert(selected != null),
184+
super(key: key);
185+
186+
@override
187+
Widget build(BuildContext context) {
188+
TextStyle textStyle = Theme.of(context).textTheme.display1;
189+
if (selected) {
190+
textStyle = textStyle.copyWith(color: Colors.teal);
191+
}
192+
return new Padding(
193+
padding: const EdgeInsets.all(2.0),
194+
child: new SizeTransition(
195+
sizeFactor: animation,
196+
axis: Axis.vertical,
197+
child: new GestureDetector(
198+
behavior: HitTestBehavior.opaque,
199+
onTap: onTap,
200+
child: new SizedBox(
201+
height: 128.0,
202+
child: new Card(
203+
color: Colors.primaries[item % Colors.primaries.length],
204+
child: new Center(
205+
child: new Text('Item $item', style: textStyle),
206+
),
207+
),
208+
),
209+
),
210+
),
211+
);
212+
}
213+
}
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
import 'package:flutter/material.dart';
2+
3+
/// 手势demo
4+
/// 这里主要是测试一下手势事件的执行顺序
5+
void main() {
6+
runApp(
7+
new MaterialApp(
8+
title: 'ShouShiDemo',
9+
theme: new ThemeData(
10+
primarySwatch: Colors.blue, //设置全局主题
11+
),
12+
home: new ShouShiDemo(),
13+
),
14+
);
15+
}
16+
17+
class ShouShiDemo extends StatelessWidget {
18+
@override
19+
Widget build(BuildContext context) {
20+
return new Scaffold(
21+
appBar: new AppBar(
22+
title: new Text('ShouShiDemo'),
23+
),
24+
body: new GestureDetector(
25+
//单击
26+
onTap: () {
27+
print('GestureDetector onTap =====》 单击');
28+
},
29+
onTapDown: (TapDownDetails details) {
30+
print('GestureDetector onTapDown =====》 按下');
31+
},
32+
onTapUp: (TapUpDetails details) {
33+
print('GestureDetector onTapUp =====》 手指抬起');
34+
},
35+
onTapCancel: () {
36+
print('GestureDetector onTapCancel =====》 取消');
37+
},
38+
//双击
39+
onDoubleTap: () {
40+
print('GestureDetector onDoubleTap =====》 双击');
41+
},
42+
//长按
43+
onLongPress: () {
44+
print('GestureDetector onLongPress =====》 长按');
45+
},
46+
//垂直拖动
47+
onVerticalDragDown: (DragDownDetails details) {},
48+
onVerticalDragStart: (DragStartDetails details) {},
49+
onVerticalDragUpdate: (DragUpdateDetails details) {},
50+
onVerticalDragEnd: (DragEndDetails details) {},
51+
onVerticalDragCancel: () {},
52+
//水平拖动
53+
onHorizontalDragDown: (DragDownDetails details) {},
54+
onHorizontalDragStart: (DragStartDetails details) {},
55+
onHorizontalDragUpdate: (DragUpdateDetails details) {},
56+
onHorizontalDragEnd: (DragEndDetails details) {},
57+
onHorizontalDragCancel: () {},
58+
// // 这几个不常用 就不介绍了
59+
// onPanDown: ,
60+
// onPanStart: ,
61+
// onPanUpdate: ,
62+
// onPanEnd: ,
63+
// onPanCancel: ,
64+
// onScaleStart: ,
65+
// onScaleUpdate: ,
66+
// onScaleEnd: ,
67+
child: new Container(
68+
padding: new EdgeInsets.all(5.0),
69+
margin: new EdgeInsets.all(5.0),
70+
decoration: new BoxDecoration(
71+
color: Colors.teal,
72+
borderRadius: new BorderRadius.only(),
73+
border: new Border.all(color: Colors.red),
74+
),
75+
//InkWell水波纹带有效果,同时带有点击事件
76+
child: new InkWell(
77+
child: new Text('手势动画'),
78+
onTap: () {
79+
print('InkWell onTap ==》 单击');
80+
},
81+
onTapDown: (TapDownDetails details) {
82+
print('InkWell onTapDown ==》 按下');
83+
},
84+
onTapCancel: () {
85+
print('InkWell onTapCancel ==》 取消');
86+
},
87+
onDoubleTap: () {
88+
print('InkWell onDoubleTap ==》 双击');
89+
},
90+
onLongPress: () {
91+
print('InkWell onLongPress ==》 长按');
92+
},
93+
),
94+
// child: new Text('手势动画'),
95+
),
96+
),
97+
);
98+
}
99+
}
100+
101+
102+
// 经测试发现,手势的顺序如下:
103+
104+
// 单独使用GestureDetector:
105+
106+
// 单击会依次触发:
107+
// onTapDown //按下
108+
// onTapUp //手指抬起
109+
// onTap //单击
110+
111+
// 双击会依次触发:
112+
// onTapCancel //手势取消
113+
// onDoubleTap //双击
114+
115+
// 长按会依次触发:
116+
// onTapDown //按下
117+
// onTapCancel //手势取消
118+
// onLongPress //长按
119+
120+
// --------------------
121+
122+
// 单独使用InkWell:
123+
124+
// 单击会依次触发:
125+
// onTapDown //按下
126+
// onTap //单击
127+
128+
// 双击会依次触发:
129+
// onTapCancel //手势取消
130+
// onDoubleTap //双击
131+
132+
// 长按会依次触发:
133+
// onTapDown //按下
134+
// onTapCancel //手势取消
135+
// onLongPress //长按
136+
137+
138+
// ---------------------
139+
140+
// GestureDetector和InkWell同时使用,看看效果:
141+
142+
// 单击会依次触发:
143+
// InkWell onTapDown ==》 按下
144+
// GestureDetector onTapDown =====》 按下
145+
// InkWell onTap ==》 单击
146+
// GestureDetector onTapCancel =====》 取消
147+
148+
// 双击会依次触发:
149+
// InkWell onTapCancel ==》 取消
150+
// GestureDetector onTapCancel =====》 取消
151+
// InkWell onDoubleTap ==》 双击
152+
153+
// 长按会依次触发:
154+
// InkWell onTapDown ==》 按下
155+
// GestureDetector onTapDown =====》 按下
156+
// InkWell onTapCancel ==》 取消
157+
// GestureDetector onTapCancel =====》 取消
158+
// InkWell onLongPress ==》 长按

0 commit comments

Comments
 (0)