Created
March 28, 2022 19:35
-
-
Save jescacena/0b88dc001a9d79aaea19e94f3a034cb4 to your computer and use it in GitHub Desktop.
Flutter animation physics based example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:flutter/material.dart'; | |
import 'package:flutter/physics.dart'; | |
void main() { | |
runApp(const MaterialApp(home: PhysicsCardDragDemo())); | |
} | |
class PhysicsCardDragDemo extends StatelessWidget { | |
const PhysicsCardDragDemo({Key? key}) : super(key: key); | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
appBar: AppBar(), | |
body: const DraggableCard( | |
child: FlutterLogo( | |
size: 128, | |
), | |
), | |
); | |
} | |
} | |
/// A draggable card that moves back to [Alignment.center] when it's | |
/// released. | |
class DraggableCard extends StatefulWidget { | |
const DraggableCard({required this.child, Key? key}) : super(key: key); | |
final Widget child; | |
@override | |
_DraggableCardState createState() => _DraggableCardState(); | |
} | |
class _DraggableCardState extends State<DraggableCard> | |
with SingleTickerProviderStateMixin { | |
late AnimationController _controller; | |
/// The alignment of the card as it is dragged or being animated. | |
/// | |
/// While the card is being dragged, this value is set to the values computed | |
/// in the GestureDetector onPanUpdate callback. If the animation is running, | |
/// this value is set to the value of the [_animation]. | |
Alignment _dragAlignment = Alignment.center; | |
late Animation<Alignment> _animation; | |
/// Calculates and runs a [SpringSimulation]. | |
void _runAnimation(Offset pixelsPerSecond, Size size) { | |
_animation = _controller.drive( | |
AlignmentTween( | |
begin: _dragAlignment, | |
end: Alignment.center, | |
), | |
); | |
// Calculate the velocity relative to the unit interval, [0,1], | |
// used by the animation controller. | |
final unitsPerSecondX = pixelsPerSecond.dx / size.width; | |
final unitsPerSecondY = pixelsPerSecond.dy / size.height; | |
final unitsPerSecond = Offset(unitsPerSecondX, unitsPerSecondY); | |
final unitVelocity = unitsPerSecond.distance; | |
const spring = SpringDescription( | |
mass: 30, | |
stiffness: 1, | |
damping: 1, | |
); | |
final simulation = SpringSimulation(spring, 0, 1, -unitVelocity); | |
_controller.animateWith(simulation); | |
} | |
@override | |
void initState() { | |
super.initState(); | |
_controller = AnimationController(vsync: this); | |
_controller.addListener(() { | |
setState(() { | |
_dragAlignment = _animation.value; | |
}); | |
}); | |
} | |
@override | |
void dispose() { | |
_controller.dispose(); | |
super.dispose(); | |
} | |
@override | |
Widget build(BuildContext context) { | |
final size = MediaQuery.of(context).size; | |
return GestureDetector( | |
onPanDown: (details) { | |
_controller.stop(); | |
}, | |
onPanUpdate: (details) { | |
setState(() { | |
_dragAlignment += Alignment( | |
details.delta.dx / (size.width / 2), | |
details.delta.dy / (size.height / 2), | |
); | |
}); | |
}, | |
onPanEnd: (details) { | |
_runAnimation(details.velocity.pixelsPerSecond, size); | |
}, | |
child: Align( | |
alignment: _dragAlignment, | |
child: Card( | |
child: widget.child, | |
), | |
), | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment