Skip to content

Commit b51d759

Browse files
authored
Add scroll physics demo (flutter#123)
* add spring physics card demo * fix button colors in animations project * code review updates * refactor method into runAnimation * combine updateAnimation and runAnimation * update comment
1 parent cc0c0da commit b51d759

File tree

4 files changed

+122
-2
lines changed

4 files changed

+122
-2
lines changed

animations/lib/main.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import 'src/misc/card_swipe.dart';
1414
import 'src/misc/carousel.dart';
1515
import 'src/misc/expand_card.dart';
1616
import 'src/misc/focus_image.dart';
17+
import 'src/misc/physics_card_drag.dart';
1718
import 'src/misc/repeating_animation.dart';
1819

1920
void main() => runApp(AnimationSamples());
@@ -48,6 +49,8 @@ final miscDemos = [
4849
Demo('Card Swipe', CardSwipeDemo.routeName, (context) => CardSwipeDemo()),
4950
Demo('Repeating Animation', RepeatingAnimationDemo.routeName,
5051
(context) => RepeatingAnimationDemo()),
52+
Demo('Spring Physics', PhysicsCardDragDemo.routeName,
53+
(context) => PhysicsCardDragDemo()),
5154
];
5255

5356
final basicDemoRoutes =

animations/lib/src/basics/02_page_route_builder.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ class PageRouteBuilderDemo extends StatelessWidget {
99
body: Center(
1010
child: RaisedButton(
1111
child: Text('Go!'),
12-
color: Theme.of(context).primaryColor,
1312
onPressed: () {
1413
Navigator.of(context).push(_createRoute());
1514
},

animations/lib/src/basics/05_custom_tween.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,13 @@ class _CustomTweenDemoState extends State<CustomTweenDemo>
4343
return Scaffold(
4444
appBar: AppBar(
4545
actions: [
46-
RaisedButton(
46+
MaterialButton(
4747
child: Text(
4848
controller.status == AnimationStatus.completed
4949
? 'Delete Essay'
5050
: 'Write Essay',
5151
),
52+
textColor: Colors.white,
5253
onPressed: () {
5354
if (controller.status == AnimationStatus.completed) {
5455
controller.reverse();
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
// Copyright 2019 The Flutter team. 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+
5+
import 'package:flutter/material.dart';
6+
import 'package:flutter/physics.dart';
7+
8+
class PhysicsCardDragDemo extends StatelessWidget {
9+
static const String routeName = '/misc/physics_card';
10+
11+
@override
12+
Widget build(BuildContext context) {
13+
return Scaffold(
14+
appBar: AppBar(),
15+
body: DraggableCard(
16+
child: FlutterLogo(
17+
size: 128,
18+
),
19+
),
20+
);
21+
}
22+
}
23+
24+
/// A draggable card that moves back to [Alignment.center] when it's
25+
/// released.
26+
class DraggableCard extends StatefulWidget {
27+
final Widget child;
28+
DraggableCard({this.child});
29+
30+
@override
31+
_DraggableCardState createState() => _DraggableCardState();
32+
}
33+
34+
class _DraggableCardState extends State<DraggableCard>
35+
with SingleTickerProviderStateMixin {
36+
AnimationController _controller;
37+
38+
/// The alignment of the card as it is dragged or being animated.
39+
///
40+
/// While the card is being dragged, this value is set to the values computed
41+
/// in the GestureDetector onPanUpdate callback. If the animation is running,
42+
/// this value is set to the value of the [_animation].
43+
Alignment _dragAlignment = Alignment.center;
44+
45+
Animation<Alignment> _animation;
46+
47+
/// Calculates and runs a [SpringSimulation]
48+
void _runAnimation(Offset pixelsPerSecond, Size size) {
49+
_animation = _controller.drive(
50+
AlignmentTween(
51+
begin: _dragAlignment,
52+
end: Alignment.center,
53+
),
54+
);
55+
// Calculate the velocity relative to the unit interval, [0,1],
56+
// used by the animation controller.
57+
final unitsPerSecondX = pixelsPerSecond.dx / size.width;
58+
final unitsPerSecondY = pixelsPerSecond.dy / size.height;
59+
final unitsPerSecond = Offset(unitsPerSecondX, unitsPerSecondY);
60+
final unitVelocity = unitsPerSecond.distance;
61+
62+
const spring = SpringDescription(
63+
mass: 30,
64+
stiffness: 1,
65+
damping: 1,
66+
);
67+
68+
final simulation = SpringSimulation(spring, 0, 1, -unitVelocity);
69+
70+
_controller.animateWith(simulation);
71+
}
72+
73+
@override
74+
void initState() {
75+
super.initState();
76+
_controller = AnimationController(vsync: this);
77+
78+
_controller.addListener(() {
79+
setState(() {
80+
_dragAlignment = _animation.value;
81+
});
82+
});
83+
}
84+
85+
@override
86+
void dispose() {
87+
_controller.dispose();
88+
super.dispose();
89+
}
90+
91+
@override
92+
Widget build(BuildContext context) {
93+
final size = MediaQuery.of(context).size;
94+
return GestureDetector(
95+
onPanDown: (details) {
96+
_controller.stop();
97+
},
98+
onPanUpdate: (details) {
99+
setState(() {
100+
_dragAlignment += Alignment(
101+
details.delta.dx / (size.width / 2),
102+
details.delta.dy / (size.height / 2),
103+
);
104+
});
105+
},
106+
onPanEnd: (details) {
107+
_runAnimation(details.velocity.pixelsPerSecond, size);
108+
},
109+
child: Align(
110+
alignment: _dragAlignment,
111+
child: Card(
112+
child: widget.child,
113+
),
114+
),
115+
);
116+
}
117+
}

0 commit comments

Comments
 (0)