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

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: Updates per review feedback 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
« no previous file with comments | « no previous file | sky/sdk/lib/widgets/block_viewport.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 VariableHeightScrollable extends Scrollable {
abarth-chromium 2015/07/10 02:37:27 This class came out great. Nice and clean.
30 VariableHeightScrollable({
31 String key,
32 this.builder,
33 this.token
34 }) : super(key: key);
35
36 IndexedBuilder builder;
37 Object token;
38
39 void syncFields(VariableHeightScrollable source) {
40 builder = source.builder;
41 token = source.token;
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;
abarth-chromium 2015/07/10 02:37:27 Where does this number come from? Maybe BlockView
hansmuller 2015/07/10 15:45:58 Yes. For this case maybe BlockViewport just needs
52 });
53 }
54
55 Widget buildContent() {
56 Widget viewport = new BlockViewport(
57 builder: builder,
58 startOffset: scrollOffset,
59 token: token
60 );
61 return new SizeObserver(child: viewport, callback: _handleSizeChanged);
abarth-chromium 2015/07/10 02:37:27 The style we've used in other framework widgets is
hansmuller 2015/07/10 15:45:58 Sure, I'll conform. I've tried to limit the depth
62 }
63 }
24 64
25 class CardCollectionApp extends App { 65 class CardCollectionApp extends App {
26 66
27 final TextStyle cardLabelStyle = 67 final TextStyle cardLabelStyle =
28 new TextStyle(color: White, fontSize: 18.0, fontWeight: bold); 68 new TextStyle(color: White, fontSize: 18.0, fontWeight: bold);
29 69
70 final List<double> cardHeights = [
71 48.0, 64.0, 82.0, 46.0, 60.0, 55.0, 84.0, 96.0, 50.0,
72 48.0, 64.0, 82.0, 46.0, 60.0, 55.0, 84.0, 96.0, 50.0,
73 48.0, 64.0, 82.0, 46.0, 60.0, 55.0, 84.0, 96.0, 50.0
74 ];
75
76 List<int> visibleCardIndices;
77
30 CardCollectionApp() { 78 CardCollectionApp() {
31 _activeCardTransform = new AnimatedContainer() 79 _activeCardTransform = new AnimatedContainer()
32 ..position = new AnimatedType<Point>(Point.origin) 80 ..position = new AnimatedType<Point>(Point.origin)
33 ..opacity = new AnimatedType<double>(1.0, end: 0.0); 81 ..opacity = new AnimatedType<double>(1.0, end: 0.0);
82
34 _activeCardAnimation = _activeCardTransform.createPerformance( 83 _activeCardAnimation = _activeCardTransform.createPerformance(
35 [_activeCardTransform.position, _activeCardTransform.opacity], 84 [_activeCardTransform.position, _activeCardTransform.opacity],
36 duration: new Duration(milliseconds: _kCardDismissFadeoutMS)); 85 duration: new Duration(milliseconds: _kCardDismissFadeoutMS));
37 _activeCardAnimation.addListener(_handleAnimationProgressChanged); 86 _activeCardAnimation.addListener(_handleAnimationProgressChanged);
87
88 visibleCardIndices = new List.generate(cardHeights.length, (i) => i);
38 } 89 }
39 90
40 int _activeCardIndex = -1; 91 int _activeCardIndex = -1;
41 AnimatedContainer _activeCardTransform; 92 AnimatedContainer _activeCardTransform;
42 AnimationPerformance _activeCardAnimation; 93 AnimationPerformance _activeCardAnimation;
43 double _activeCardWidth; 94 double _activeCardWidth;
44 double _activeCardDragX = 0.0; 95 double _activeCardDragX = 0.0;
45 bool _activeCardDragUnderway = false; 96 bool _activeCardDragUnderway = false;
46 Set<int> _dismissedCardIndices = new Set<int>();
47 97
48 Point get _activeCardDragEndPoint { 98 Point get _activeCardDragEndPoint {
49 return new Point(_activeCardDragX.sign * _activeCardWidth * _kDismissCardThr eshold, 0.0); 99 return new Point(_activeCardDragX.sign * _activeCardWidth * _kDismissCardThr eshold, 0.0);
50 } 100 }
51 101
52 void _handleAnimationProgressChanged() { 102 void _handleAnimationProgressChanged() {
53 setState(() { 103 setState(() {
54 if (_activeCardAnimation.isCompleted && !_activeCardDragUnderway) 104 if (_activeCardAnimation.isCompleted && !_activeCardDragUnderway)
55 _dismissedCardIndices.add(_activeCardIndex); 105 visibleCardIndices.remove(_activeCardIndex);
56 }); 106 });
57 } 107 }
58 108
59 void _handleSizeChanged(Size newSize) { 109 void _handleSizeChanged(Size newSize) {
60 _activeCardWidth = newSize.width; 110 _activeCardWidth = newSize.width;
61 _activeCardTransform.position.end = _activeCardDragEndPoint; 111 _activeCardTransform.position.end = _activeCardDragEndPoint;
62 } 112 }
63 113
64 void _handlePointerDown(sky.PointerEvent event, int cardIndex) { 114 void _handlePointerDown(sky.PointerEvent event, int cardIndex) {
65 setState(() { 115 setState(() {
(...skipping 19 matching lines...) Expand all
85 }); 135 });
86 } 136 }
87 137
88 void _handlePointerUpOrCancel(_) { 138 void _handlePointerUpOrCancel(_) {
89 if (_activeCardWidth == null || _activeCardIndex < 0) 139 if (_activeCardWidth == null || _activeCardIndex < 0)
90 return; 140 return;
91 141
92 setState(() { 142 setState(() {
93 _activeCardDragUnderway = false; 143 _activeCardDragUnderway = false;
94 if (_activeCardAnimation.isCompleted) 144 if (_activeCardAnimation.isCompleted)
95 _dismissedCardIndices.add(_activeCardIndex); 145 visibleCardIndices.remove(_activeCardIndex);
96 else if (!_activeCardAnimation.isAnimating) 146 else if (!_activeCardAnimation.isAnimating)
97 _activeCardAnimation.progress = 0.0; 147 _activeCardAnimation.progress = 0.0;
98 }); 148 });
99 } 149 }
100 150
151 bool _isHorizontalFlingGesture(sky.GestureEvent event) {
152 double vx = event.velocityX.abs();
153 double vy = event.velocityY.abs();
154 return vx - vy > _kMinFlingVelocityDelta && vx > _kMinFlingVelocity;
155 }
156
101 void _handleFlingStart(sky.GestureEvent event) { 157 void _handleFlingStart(sky.GestureEvent event) {
102 if (_activeCardWidth == null || _activeCardIndex < 0) 158 if (_activeCardWidth == null || _activeCardIndex < 0)
103 return; 159 return;
104 160
105 _activeCardDragUnderway = false; 161 _activeCardDragUnderway = false;
106 double velocityX = event.velocityX / 1000; 162
107 if (velocityX.abs() >= _kMinCardFlingVelocity) { 163 if (_isHorizontalFlingGesture(event)) {
108 double distance = 1.0 - _activeCardAnimation.progress; 164 double distance = 1.0 - _activeCardAnimation.progress;
109 if (distance > 0.0) { 165 if (distance > 0.0) {
110 double duration = 150.0 * distance / velocityX.abs(); 166 double duration = 250.0 * 1000.0 * distance / event.velocityX.abs();
111 _activeCardDragX = velocityX.sign; 167 _activeCardDragX = event.velocityX.sign;
112 _activeCardAnimation.timeline.animateTo(1.0, duration: duration); 168 _activeCardAnimation.timeline.animateTo(1.0, duration: duration);
113 } 169 }
114 } 170 }
115 } 171 }
116 172
117 Widget _buildCard(int index, Color color) { 173 Widget _buildCard(int cardIndex) {
118 Widget label = new Center(child: new Text("Item ${index}", style: cardLabelS tyle)); 174 Widget label = new Center(child: new Text("Item ${cardIndex}", style: cardLa belStyle));
175 Color color = lerpColor(Red[500], Blue[500], cardIndex / cardHeights.length) ;
119 Widget card = new Card( 176 Widget card = new Card(
120 child: new Padding(child: label, padding: const EdgeDims.all(8.0)), 177 child: new Padding(child: label, padding: const EdgeDims.all(8.0)),
121 color: color 178 color: color
122 ); 179 );
123 180
124 // TODO(hansmuller) The code below changes the card's widget tree when 181 // 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 182 // 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. 183 // 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. 184 // As a workaround, always create the Transform and Opacity nodes.
128 if (index == _activeCardIndex) { 185 if (cardIndex == _activeCardIndex) {
129 card = _activeCardTransform.build(card); 186 card = _activeCardTransform.build(card);
130 } else { 187 } else {
131 card = new Transform(child: card, transform: new Matrix4.identity()); 188 card = new Transform(child: card, transform: new Matrix4.identity());
132 card = new Opacity(child: card, opacity: 1.0); 189 card = new Opacity(child: card, opacity: 1.0);
133 } 190 }
134 191
135 return new Listener( 192 return new Listener(
136 child: card, 193 key: "$cardIndex",
137 onPointerDown: (event) { _handlePointerDown(event, index); }, 194 child: new Container(child: card, height: cardHeights[cardIndex]),
195 onPointerDown: (event) { _handlePointerDown(event, cardIndex); },
138 onPointerMove: _handlePointerMove, 196 onPointerMove: _handlePointerMove,
139 onPointerUp: _handlePointerUpOrCancel, 197 onPointerUp: _handlePointerUpOrCancel,
140 onPointerCancel: _handlePointerUpOrCancel, 198 onPointerCancel: _handlePointerUpOrCancel,
141 onGestureFlingStart: _handleFlingStart 199 onGestureFlingStart: _handleFlingStart
142 ); 200 );
143 } 201 }
144 202
145 Widget _buildCardCollection(List<double> heights) { 203 Widget _builder(int index) {
146 List<Widget> items = <Widget>[]; 204 if (index >= visibleCardIndices.length)
147 for(int index = 0; index < heights.length; index++) { 205 return null;
148 if (_dismissedCardIndices.contains(index)) 206 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 } 207 }
163 208
164 Widget build() { 209 Widget build() {
210 Widget cardCollection = new Container(
211 padding: const EdgeDims.symmetric(vertical: 12.0, horizontal: 8.0),
212 decoration: new BoxDecoration(backgroundColor: Theme.of(this).primarySwatc h[50]),
213 child: new VariableHeightScrollable(
214 builder: _builder,
215 token: visibleCardIndices.length
216 )
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') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698