2
2
// Use of this source code is governed by a BSD-style license that can be
3
3
// found in the LICENSE file.
4
4
5
+ import 'package:sky/animation/animation_performance.dart' ;
6
+ import 'package:sky/animation/curves.dart' ;
5
7
import 'package:sky/base/lerp.dart' ;
6
8
import 'package:sky/painting/text_style.dart' ;
7
9
import 'package:sky/theme/colors.dart' ;
10
+ import 'package:sky/widgets/animated_component.dart' ;
8
11
import 'package:sky/widgets/basic.dart' ;
12
+ import 'package:sky/widgets/block_viewport.dart' ;
9
13
import 'package:sky/widgets/card.dart' ;
10
14
import 'package:sky/widgets/dismissable.dart' ;
11
- import 'package:sky/widgets/scaffold.dart' ;
12
15
import 'package:sky/widgets/variable_height_scrollable.dart' ;
16
+ import 'package:sky/widgets/scaffold.dart' ;
13
17
import 'package:sky/widgets/theme.dart' ;
14
18
import 'package:sky/widgets/tool_bar.dart' ;
15
19
import 'package:sky/widgets/widget.dart' ;
16
- import 'package:sky/theme/colors.dart' as colors;
17
20
import 'package:sky/widgets/task_description.dart' ;
18
21
22
+ class CardModel {
23
+ CardModel (this .value, this .height, this .color);
24
+ int value;
25
+ double height;
26
+ Color color;
27
+ AnimationPerformance performance;
28
+ String get label => "Item $value " ;
29
+ String get key => value.toString ();
30
+ bool operator == (other) => other is CardModel && other.value == value;
31
+ int get hashCode => 373 * 37 * value.hashCode;
32
+ }
33
+
34
+ class ShrinkingCard extends AnimatedComponent {
35
+
36
+ ShrinkingCard ({
37
+ String key,
38
+ CardModel this .card,
39
+ Function this .onUpdated,
40
+ Function this .onCompleted
41
+ }) : super (key: key);
42
+
43
+ CardModel card;
44
+ Function onUpdated;
45
+ Function onCompleted;
46
+
47
+ double get currentHeight => card.performance.variable.value;
48
+
49
+ void initState () {
50
+ assert (card.performance != null );
51
+ card.performance.addListener (handleAnimationProgress);
52
+ watch (card.performance);
53
+ }
54
+
55
+ void handleAnimationProgress () {
56
+ if (card.performance.isCompleted) {
57
+ if (onCompleted != null )
58
+ onCompleted ();
59
+ } else if (onUpdated != null ) {
60
+ onUpdated ();
61
+ }
62
+ }
63
+
64
+ void syncFields (ShrinkingCard source) {
65
+ card = source.card;
66
+ onCompleted = source.onCompleted;
67
+ onUpdated = source.onUpdated;
68
+ super .syncFields (source);
69
+ }
70
+
71
+ Widget build () => new Container (height: currentHeight);
72
+ }
19
73
20
74
class CardCollectionApp extends App {
21
75
22
76
final TextStyle cardLabelStyle =
23
77
new TextStyle (color: white, fontSize: 18.0 , fontWeight: bold);
24
78
25
- final List <double > cardHeights = [
26
- 48.0 , 64.0 , 82.0 , 46.0 , 60.0 , 55.0 , 84.0 , 96.0 , 50.0 ,
27
- 48.0 , 64.0 , 82.0 , 46.0 , 60.0 , 55.0 , 84.0 , 96.0 , 50.0 ,
28
- 48.0 , 64.0 , 82.0 , 46.0 , 60.0 , 55.0 , 84.0 , 96.0 , 50.0 ,
29
- 48.0 , 64.0 , 82.0 , 46.0 , 60.0 , 55.0 , 84.0 , 96.0 , 50.0
30
- ];
31
-
32
- List <int > visibleCardIndices;
79
+ BlockViewportLayoutState layoutState = new BlockViewportLayoutState ();
80
+ List <CardModel > cardModels;
33
81
34
82
void initState () {
35
- visibleCardIndices = new List .generate (cardHeights.length, (i) => i);
83
+ List <double > cardHeights = < double > [
84
+ 48.0 , 63.0 , 82.0 , 146.0 , 60.0 , 55.0 , 84.0 , 96.0 , 50.0 ,
85
+ 48.0 , 63.0 , 82.0 , 146.0 , 60.0 , 55.0 , 84.0 , 96.0 , 50.0 ,
86
+ 48.0 , 63.0 , 82.0 , 146.0 , 60.0 , 55.0 , 84.0 , 96.0 , 50.0
87
+ ];
88
+ cardModels = new List .generate (cardHeights.length, (i) {
89
+ Color color = lerpColor (Red [300 ], Blue [900 ], i / cardHeights.length);
90
+ return new CardModel (i, cardHeights[i], color);
91
+ });
36
92
super .initState ();
37
93
}
38
94
39
- void dismissCard (int cardIndex) {
95
+ void shrinkCard (CardModel card, int index) {
96
+ if (card.performance != null )
97
+ return ;
98
+ layoutState.invalidate ([index]);
40
99
setState (() {
41
- visibleCardIndices.remove (cardIndex);
100
+ assert (card.performance == null );
101
+ card.performance = new AnimationPerformance ()
102
+ ..duration = const Duration (milliseconds: 300 )
103
+ ..variable = new AnimatedType <double >(
104
+ card.height + kCardMargins.top + kCardMargins.bottom,
105
+ end: 0.0 ,
106
+ curve: ease,
107
+ interval: new Interval (0.5 , 1.0 )
108
+ )
109
+ ..play ();
42
110
});
43
111
}
44
112
45
- Widget _builder (int index) {
46
- if (index >= visibleCardIndices.length)
113
+ void dismissCard (CardModel card) {
114
+ if (cardModels.contains (card)) {
115
+ setState (() {
116
+ cardModels.remove (card);
117
+ });
118
+ }
119
+ }
120
+
121
+ Widget builder (int index) {
122
+ if (index >= cardModels.length)
47
123
return null ;
124
+ CardModel card = cardModels[index];
125
+
126
+ if (card.performance != null ) {
127
+ return new ShrinkingCard (
128
+ key: card.key,
129
+ card: card,
130
+ onUpdated: () { layoutState.invalidate ([index]); },
131
+ onCompleted: () { dismissCard (card); }
132
+ );
133
+ }
48
134
49
- int cardIndex = visibleCardIndices[index];
50
- Color color = lerpColor (Red [500 ], Blue [500 ], cardIndex / cardHeights.length);
51
- Widget label = new Text ("Item ${cardIndex }" , style: cardLabelStyle);
52
135
return new Dismissable (
53
- key: cardIndex. toString () ,
54
- onDismissed: () { dismissCard (cardIndex ); },
136
+ key: card.key ,
137
+ onDismissed: () { shrinkCard (card, index ); },
55
138
child: new Card (
56
- color: color,
139
+ color: card. color,
57
140
child: new Container (
58
- height: cardHeights[cardIndex] ,
141
+ height: card.height ,
59
142
padding: const EdgeDims .all (8.0 ),
60
- child: new Center (child: label)
143
+ child: new Center (child: new Text (card. label, style : cardLabelStyle) )
61
144
)
62
145
)
63
146
);
@@ -68,16 +151,17 @@ class CardCollectionApp extends App {
68
151
padding: const EdgeDims .symmetric (vertical: 12.0 , horizontal: 8.0 ),
69
152
decoration: new BoxDecoration (backgroundColor: Theme .of (this ).primarySwatch[50 ]),
70
153
child: new VariableHeightScrollable (
71
- builder: _builder,
72
- token: visibleCardIndices.length
154
+ builder: builder,
155
+ token: cardModels.length,
156
+ layoutState: layoutState
73
157
)
74
158
);
75
159
76
160
return new Theme (
77
161
data: new ThemeData (
78
162
brightness: ThemeBrightness .light,
79
- primarySwatch: colors. Blue ,
80
- accentColor: colors. RedAccent [200 ]
163
+ primarySwatch: Blue ,
164
+ accentColor: RedAccent [200 ]
81
165
),
82
166
child: new TaskDescription (
83
167
label: 'Cards' ,
0 commit comments