Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(253)

Side by Side Diff: sky/sdk/example/widgets/card_collection.dart

Issue 1227963003: Card "swipe-away" dismiss version 3: Uses BlockViewport (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: Created 5 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 import 'dart:sky' as sky; 5 import 'dart:sky' as sky;
6 6
7 import 'package:vector_math/vector_math.dart'; 7 import 'package:vector_math/vector_math.dart';
8 import 'package:sky/animation/animation_performance.dart'; 8 import 'package:sky/animation/animation_performance.dart';
9 import 'package:sky/animation/scroll_behavior.dart';
9 import 'package:sky/base/lerp.dart'; 10 import 'package:sky/base/lerp.dart';
10 import 'package:sky/painting/text_style.dart'; 11 import 'package:sky/painting/text_style.dart';
11 import 'package:sky/theme/colors.dart'; 12 import 'package:sky/theme/colors.dart';
12 import 'package:sky/widgets/animated_container.dart'; 13 import 'package:sky/widgets/animated_container.dart';
13 import 'package:sky/widgets/basic.dart'; 14 import 'package:sky/widgets/basic.dart';
15 import 'package:sky/widgets/block_viewport.dart';
14 import 'package:sky/widgets/card.dart'; 16 import 'package:sky/widgets/card.dart';
15 import 'package:sky/widgets/scaffold.dart'; 17 import 'package:sky/widgets/scaffold.dart';
18 import 'package:sky/widgets/scrollable.dart';
16 import 'package:sky/widgets/theme.dart'; 19 import 'package:sky/widgets/theme.dart';
17 import 'package:sky/widgets/tool_bar.dart'; 20 import 'package:sky/widgets/tool_bar.dart';
18 import 'package:sky/widgets/widget.dart'; 21 import 'package:sky/widgets/widget.dart';
19 22
20 23
21 const int _kCardDismissFadeoutMS = 300; 24 const int _kCardDismissFadeoutMS = 300;
22 const double _kMinCardFlingVelocity = 0.4; 25 const double _kMinFlingVelocity = 700.0;
23 const double _kDismissCardThreshold = 0.70; 26 const double _kMinFlingVelocityDelta = 400.0;
27 const double _kDismissCardThreshold = 0.6;
28
29 class CardCollection extends Scrollable {
Hixie 2015/07/10 00:08:15 I think this should just be a generic ValiableHeig
hansmuller 2015/07/10 00:18:50 Done.
30 CardCollection({
31 String key,
32 IndexedBuilder this.builder,
33 int this.cardCount
Hixie 2015/07/10 00:08:15 take a token, not a count. If an item in the list
hansmuller 2015/07/10 00:18:50 Done.
34 }) : super(key: key);
35
36 IndexedBuilder builder;
37 int cardCount;
38
39 void syncFields(CardCollection source) {
40 builder = source.builder;
41 cardCount = source.cardCount;
42 super.syncFields(source);
43 }
44
45 ScrollBehavior createScrollBehavior() => new OverscrollBehavior();
46 OverscrollBehavior get scrollBehavior => super.scrollBehavior;
47
48 void _handleSizeChanged(Size newSize) {
49 setState(() {
50 scrollBehavior.containerHeight = newSize.height;
51 scrollBehavior.contentsHeight = 5000.0;
52 });
53 }
54
55 Widget buildContent() {
56 Widget viewport = new BlockViewport(
57 builder: builder,
58 startOffset: scrollOffset,
59 token: cardCount
60 );
61 return new Container(
62 child: new SizeObserver(child: viewport, callback: _handleSizeChanged),
63 padding: const EdgeDims.symmetric(vertical: 12.0, horizontal: 8.0),
64 decoration: new BoxDecoration(backgroundColor: Theme.of(this).primarySwatc h[50])
65 );
Hixie 2015/07/10 00:08:15 the padding and decoration should be done by the p
hansmuller 2015/07/10 00:18:50 Done.
66 }
67 }
24 68
25 class CardCollectionApp extends App { 69 class CardCollectionApp extends App {
26 70
27 final TextStyle cardLabelStyle = 71 final TextStyle cardLabelStyle =
28 new TextStyle(color: White, fontSize: 18.0, fontWeight: bold); 72 new TextStyle(color: White, fontSize: 18.0, fontWeight: bold);
29 73
74 final List<double> cardHeights = [
75 48.0, 64.0, 82.0, 46.0, 60.0, 55.0, 84.0, 96.0, 50.0,
76 48.0, 64.0, 82.0, 46.0, 60.0, 55.0, 84.0, 96.0, 50.0,
77 48.0, 64.0, 82.0, 46.0, 60.0, 55.0, 84.0, 96.0, 50.0
78 ];
79
80 List<int> visibleCardIndices;
81
30 CardCollectionApp() { 82 CardCollectionApp() {
31 _activeCardTransform = new AnimatedContainer() 83 _activeCardTransform = new AnimatedContainer()
32 ..position = new AnimatedType<Point>(Point.origin) 84 ..position = new AnimatedType<Point>(Point.origin)
33 ..opacity = new AnimatedType<double>(1.0, end: 0.0); 85 ..opacity = new AnimatedType<double>(1.0, end: 0.0);
86
34 _activeCardAnimation = _activeCardTransform.createPerformance( 87 _activeCardAnimation = _activeCardTransform.createPerformance(
35 [_activeCardTransform.position, _activeCardTransform.opacity], 88 [_activeCardTransform.position, _activeCardTransform.opacity],
36 duration: new Duration(milliseconds: _kCardDismissFadeoutMS)); 89 duration: new Duration(milliseconds: _kCardDismissFadeoutMS));
37 _activeCardAnimation.addListener(_handleAnimationProgressChanged); 90 _activeCardAnimation.addListener(_handleAnimationProgressChanged);
91
92 visibleCardIndices = new List.generate(cardHeights.length, (i) => i);
38 } 93 }
39 94
40 int _activeCardIndex = -1; 95 int _activeCardIndex = -1;
41 AnimatedContainer _activeCardTransform; 96 AnimatedContainer _activeCardTransform;
42 AnimationPerformance _activeCardAnimation; 97 AnimationPerformance _activeCardAnimation;
43 double _activeCardWidth; 98 double _activeCardWidth;
44 double _activeCardDragX = 0.0; 99 double _activeCardDragX = 0.0;
45 bool _activeCardDragUnderway = false; 100 bool _activeCardDragUnderway = false;
46 Set<int> _dismissedCardIndices = new Set<int>();
47 101
48 Point get _activeCardDragEndPoint { 102 Point get _activeCardDragEndPoint {
49 return new Point(_activeCardDragX.sign * _activeCardWidth * _kDismissCardThr eshold, 0.0); 103 return new Point(_activeCardDragX.sign * _activeCardWidth * _kDismissCardThr eshold, 0.0);
50 } 104 }
51 105
52 void _handleAnimationProgressChanged() { 106 void _handleAnimationProgressChanged() {
53 setState(() { 107 setState(() {
54 if (_activeCardAnimation.isCompleted && !_activeCardDragUnderway) 108 if (_activeCardAnimation.isCompleted && !_activeCardDragUnderway)
55 _dismissedCardIndices.add(_activeCardIndex); 109 visibleCardIndices.remove(_activeCardIndex);
56 }); 110 });
57 } 111 }
58 112
59 void _handleSizeChanged(Size newSize) { 113 void _handleSizeChanged(Size newSize) {
60 _activeCardWidth = newSize.width; 114 _activeCardWidth = newSize.width;
61 _activeCardTransform.position.end = _activeCardDragEndPoint; 115 _activeCardTransform.position.end = _activeCardDragEndPoint;
62 } 116 }
63 117
64 void _handlePointerDown(sky.PointerEvent event, int cardIndex) { 118 void _handlePointerDown(sky.PointerEvent event, int cardIndex) {
65 setState(() { 119 setState(() {
(...skipping 19 matching lines...) Expand all
85 }); 139 });
86 } 140 }
87 141
88 void _handlePointerUpOrCancel(_) { 142 void _handlePointerUpOrCancel(_) {
89 if (_activeCardWidth == null || _activeCardIndex < 0) 143 if (_activeCardWidth == null || _activeCardIndex < 0)
90 return; 144 return;
91 145
92 setState(() { 146 setState(() {
93 _activeCardDragUnderway = false; 147 _activeCardDragUnderway = false;
94 if (_activeCardAnimation.isCompleted) 148 if (_activeCardAnimation.isCompleted)
95 _dismissedCardIndices.add(_activeCardIndex); 149 visibleCardIndices.remove(_activeCardIndex);
96 else if (!_activeCardAnimation.isAnimating) 150 else if (!_activeCardAnimation.isAnimating)
97 _activeCardAnimation.progress = 0.0; 151 _activeCardAnimation.progress = 0.0;
98 }); 152 });
99 } 153 }
100 154
155 bool _isHorizontalFlingGesture(sky.GestureEvent event) {
156 double vx = event.velocityX.abs();
157 double vy = event.velocityY.abs();
158 return vx - vy > _kMinFlingVelocityDelta && vx > _kMinFlingVelocity;
159 }
160
101 void _handleFlingStart(sky.GestureEvent event) { 161 void _handleFlingStart(sky.GestureEvent event) {
102 if (_activeCardWidth == null || _activeCardIndex < 0) 162 if (_activeCardWidth == null || _activeCardIndex < 0)
103 return; 163 return;
104 164
105 _activeCardDragUnderway = false; 165 _activeCardDragUnderway = false;
106 double velocityX = event.velocityX / 1000; 166
107 if (velocityX.abs() >= _kMinCardFlingVelocity) { 167 if (_isHorizontalFlingGesture(event)) {
108 double distance = 1.0 - _activeCardAnimation.progress; 168 double distance = 1.0 - _activeCardAnimation.progress;
109 if (distance > 0.0) { 169 if (distance > 0.0) {
110 double duration = 150.0 * distance / velocityX.abs(); 170 double duration = 250.0 * 1000.0 * distance / event.velocityX.abs();
111 _activeCardDragX = velocityX.sign; 171 _activeCardDragX = event.velocityX.sign;
112 _activeCardAnimation.timeline.animateTo(1.0, duration: duration); 172 _activeCardAnimation.timeline.animateTo(1.0, duration: duration);
113 } 173 }
114 } 174 }
115 } 175 }
116 176
117 Widget _buildCard(int index, Color color) { 177 Widget _buildCard(int cardIndex) {
118 Widget label = new Center(child: new Text("Item ${index}", style: cardLabelS tyle)); 178 Widget label = new Center(child: new Text("Item ${cardIndex}", style: cardLa belStyle));
179 Color color = lerpColor(Red[500], Blue[500], cardIndex / cardHeights.length) ;
119 Widget card = new Card( 180 Widget card = new Card(
120 child: new Padding(child: label, padding: const EdgeDims.all(8.0)), 181 child: new Padding(child: label, padding: const EdgeDims.all(8.0)),
121 color: color 182 color: color
122 ); 183 );
123 184
124 // TODO(hansmuller) The code below changes the card's widget tree when 185 // TODO(hansmuller) The code below changes the card's widget tree when
125 // the user starts dragging it. Currently this causes Sky to drop the 186 // the user starts dragging it. Currently this causes Sky to drop the
126 // rest of the pointer gesture, see https://github.com/domokit/mojo/issues/3 12. 187 // rest of the pointer gesture, see https://github.com/domokit/mojo/issues/3 12.
127 // As a workaround, always create the Transform and Opacity nodes. 188 // As a workaround, always create the Transform and Opacity nodes.
128 if (index == _activeCardIndex) { 189 if (cardIndex == _activeCardIndex) {
129 card = _activeCardTransform.build(card); 190 card = _activeCardTransform.build(card);
130 } else { 191 } else {
131 card = new Transform(child: card, transform: new Matrix4.identity()); 192 card = new Transform(child: card, transform: new Matrix4.identity());
132 card = new Opacity(child: card, opacity: 1.0); 193 card = new Opacity(child: card, opacity: 1.0);
133 } 194 }
134 195
135 return new Listener( 196 return new Listener(
136 child: card, 197 key: "$cardIndex",
137 onPointerDown: (event) { _handlePointerDown(event, index); }, 198 child: new Container(child: card, height: cardHeights[cardIndex]),
199 onPointerDown: (event) { _handlePointerDown(event, cardIndex); },
138 onPointerMove: _handlePointerMove, 200 onPointerMove: _handlePointerMove,
139 onPointerUp: _handlePointerUpOrCancel, 201 onPointerUp: _handlePointerUpOrCancel,
140 onPointerCancel: _handlePointerUpOrCancel, 202 onPointerCancel: _handlePointerUpOrCancel,
141 onGestureFlingStart: _handleFlingStart 203 onGestureFlingStart: _handleFlingStart
142 ); 204 );
143 } 205 }
144 206
145 Widget _buildCardCollection(List<double> heights) { 207 Widget _builder(int index) {
146 List<Widget> items = <Widget>[]; 208 if (index >= visibleCardIndices.length)
147 for(int index = 0; index < heights.length; index++) { 209 return null;
148 if (_dismissedCardIndices.contains(index)) 210 return _buildCard(visibleCardIndices[index]);
149 continue;
150 Color color = lerpColor(Red[500], Blue[500], index / heights.length);
151 items.add(new Container(
152 child: _buildCard(index, color),
153 height: heights[index]
154 ));
155 }
156
157 return new Container(
158 child: new SizeObserver(child: new Block(items), callback: _handleSizeChan ged),
159 padding: const EdgeDims.symmetric(vertical: 12.0, horizontal: 8.0),
160 decoration: new BoxDecoration(backgroundColor: Theme.of(this).primarySwatc h[50])
161 );
162 } 211 }
163 212
164 Widget build() { 213 Widget build() {
214 Widget cardCollection = new CardCollection(
215 builder: _builder,
216 cardCount: visibleCardIndices.length
217 );
218
165 return new Scaffold( 219 return new Scaffold(
166 toolbar: new ToolBar(center: new Text('Swipe Away')), 220 toolbar: new ToolBar(center: new Text('Swipe Away')),
167 body: _buildCardCollection( 221 body: new SizeObserver(child: cardCollection, callback: _handleSizeChanged )
168 [48.0, 64.0, 82.0, 46.0, 60.0, 55.0, 84.0, 96.0, 50.0,
169 48.0, 64.0, 82.0, 46.0, 60.0, 55.0, 84.0, 96.0, 50.0])
170 ); 222 );
171 } 223 }
172 } 224 }
173 225
174 void main() { 226 void main() {
175 runApp(new CardCollectionApp()); 227 runApp(new CardCollectionApp());
176 } 228 }
OLDNEW
« no previous file with comments | « no previous file | sky/sdk/lib/widgets/block_viewport.dart » ('j') | sky/sdk/lib/widgets/block_viewport.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698