Physics Based Animations in Flutter
In the previous post I’ve tried to explain tween animations. Now it is time to dig in physics based animations.
To simulate real world movements like falling or swinging thoughtful Flutter team has created some simulation classes. With these classes we don’t have to figure out position out of acceleration and time or direction of a swinging widget.
Let’s jump in to the code with a GravitySimulation example.
import 'package:flutter/material.dart';
import 'package:flutter/animation.dart';
import 'package:flutter/physics.dart';
void main() => runApp(PhysicsAnimation());
class PhysicsAnimation extends StatefulWidget {
_PhysicsAnimation createState() => _PhysicsAnimation();
}
class _PhysicsAnimation extends State<PhysicsAnimation>
with SingleTickerProviderStateMixin{
AnimationController controller;
GravitySimulation simulation;
@override
void initState() {
super.initState();
simulation = GravitySimulation(
100.0, // acceleration
0.0, // starting point
500.0, // end point
0.0, // starting velocity
);
controller =
AnimationController(vsync: this, upperBound: 500)
..addListener(() {
setState(() {});
});
controller.animateWith(simulation);
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Stack(
children: [
Positioned(
left: 50,
top: controller.value,
height: 10,
width: 10,
child: Container(
color: Colors.redAccent,
),
),
]
),
);
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
}
I already did some explanations in tween animations post.
GravitySimulation: A class for simulating gravity initialized with acceleration, starting point, ending point and starting velocity. In this example we provide 100 units for acceleration and our destination is zero to 500 with zero initial velocity.
upperBound: This may be a bug or it is just me. End position in simulation will not work as expected because AnimationController class value has a max and min limit. Default value for upperBound is 1. When you use simulation and your controller.value stucks at 1 you should change upperBound value accordingly. There is lowerBound property too which is default 0. We don’t need it in this example but it would be useful if we wanted to set our starting point to a negative value.
controller.animateWith: We tell our controller to use the simulation we initialized. You may notice that we didn’t set any duration in our controller because controller will calculate duration of animation with parameters we set in our simulation.
After all these we use controller.value in the Positioned widget wrapped with a Stack widget. And we have this;
Next is SpringSimulation.
import 'package:flutter/material.dart';
import 'package:flutter/animation.dart';
import 'package:flutter/physics.dart';
void main() => runApp(PhysicsAnimation());
class PhysicsAnimation extends StatefulWidget {
_PhysicsAnimation createState() => _PhysicsAnimation();
}
class _PhysicsAnimation extends State<PhysicsAnimation>
with SingleTickerProviderStateMixin{
AnimationController controller;
SpringSimulation simulation;
@override
void initState() {
super.initState();
simulation = SpringSimulation(
SpringDescription(
mass: 1,
stiffness: 100,
damping: 1,
),
0.0, // starting point
300.0, // ending point
10, // velocity
);
controller =
AnimationController(vsync: this, upperBound: 500, )
..addListener(() {
print(controller.value);
setState(() {});
});
controller.animateWith(simulation);
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Stack(
children: [
Positioned(
left: 50,
top: controller.value,
height: 10,
width: 10,
child: Container(
color: Colors.redAccent,
),
),
]
),
);
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
}
Most is same with previous example. We’ve just used another simulation class. Notice upperBound value is bigger than ending point in simulation parameters because of spring’s oscillation.
Check out these GIFs to see how SpringDescription’s parameters affect the animation.
mass:1 stiffness:100 damping: varies
mass:1 damping:1 stiffness: varies
That’s it for now, thanks.