Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 30f9377

Browse files
author
Hans Muller
committed
Merge pull request #122 from HansMuller/shrinking-card
Card Collection dismiss animation
2 parents ced1d96 + 344ee7c commit 30f9377

File tree

6 files changed

+363
-200
lines changed

6 files changed

+363
-200
lines changed

sky/sdk/example/widgets/block_viewport.dart

+3-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import 'package:sky/widgets/widget.dart';
1515

1616
class BlockViewportApp extends App {
1717

18+
BlockViewportLayoutState layoutState = new BlockViewportLayoutState();
1819
List<double> lengths = <double>[];
1920
double offset = 0.0;
2021

@@ -96,7 +97,8 @@ class BlockViewportApp extends App {
9697
child: new BlockViewport(
9798
builder: builder,
9899
startOffset: offset,
99-
token: lengths.length
100+
token: lengths.length,
101+
layoutState: layoutState
100102
)
101103
)
102104
),

sky/sdk/example/widgets/card_collection.dart

+111-27
Original file line numberDiff line numberDiff line change
@@ -2,62 +2,145 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5+
import 'package:sky/animation/animation_performance.dart';
6+
import 'package:sky/animation/curves.dart';
57
import 'package:sky/base/lerp.dart';
68
import 'package:sky/painting/text_style.dart';
79
import 'package:sky/theme/colors.dart';
10+
import 'package:sky/widgets/animated_component.dart';
811
import 'package:sky/widgets/basic.dart';
12+
import 'package:sky/widgets/block_viewport.dart';
913
import 'package:sky/widgets/card.dart';
1014
import 'package:sky/widgets/dismissable.dart';
11-
import 'package:sky/widgets/scaffold.dart';
1215
import 'package:sky/widgets/variable_height_scrollable.dart';
16+
import 'package:sky/widgets/scaffold.dart';
1317
import 'package:sky/widgets/theme.dart';
1418
import 'package:sky/widgets/tool_bar.dart';
1519
import 'package:sky/widgets/widget.dart';
16-
import 'package:sky/theme/colors.dart' as colors;
1720
import 'package:sky/widgets/task_description.dart';
1821

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+
}
1973

2074
class CardCollectionApp extends App {
2175

2276
final TextStyle cardLabelStyle =
2377
new TextStyle(color: white, fontSize: 18.0, fontWeight: bold);
2478

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;
3381

3482
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+
});
3692
super.initState();
3793
}
3894

39-
void dismissCard(int cardIndex) {
95+
void shrinkCard(CardModel card, int index) {
96+
if (card.performance != null)
97+
return;
98+
layoutState.invalidate([index]);
4099
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();
42110
});
43111
}
44112

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)
47123
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+
}
48134

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);
52135
return new Dismissable(
53-
key: cardIndex.toString(),
54-
onDismissed: () { dismissCard(cardIndex); },
136+
key: card.key,
137+
onDismissed: () { shrinkCard(card, index); },
55138
child: new Card(
56-
color: color,
139+
color: card.color,
57140
child: new Container(
58-
height: cardHeights[cardIndex],
141+
height: card.height,
59142
padding: const EdgeDims.all(8.0),
60-
child: new Center(child: label)
143+
child: new Center(child: new Text(card.label, style: cardLabelStyle))
61144
)
62145
)
63146
);
@@ -68,16 +151,17 @@ class CardCollectionApp extends App {
68151
padding: const EdgeDims.symmetric(vertical: 12.0, horizontal: 8.0),
69152
decoration: new BoxDecoration(backgroundColor: Theme.of(this).primarySwatch[50]),
70153
child: new VariableHeightScrollable(
71-
builder: _builder,
72-
token: visibleCardIndices.length
154+
builder: builder,
155+
token: cardModels.length,
156+
layoutState: layoutState
73157
)
74158
);
75159

76160
return new Theme(
77161
data: new ThemeData(
78162
brightness: ThemeBrightness.light,
79-
primarySwatch: colors.Blue,
80-
accentColor: colors.RedAccent[200]
163+
primarySwatch: Blue,
164+
accentColor: RedAccent[200]
81165
),
82166
child: new TaskDescription(
83167
label: 'Cards',

0 commit comments

Comments
 (0)