Fun with
Flutter Animations
Divyanshu Bhargava
@divyanshub024, HighLevel
What is Animation?
A sequence of images or frames.
Source: adriennewollman
“To think creatively, we must be
able to look afresh at what we
normally take for granted”
- George Keller
“Animation is about creating the
illusion of life. And you can't create it
if you don't have one.”
- Brad Bird
Why Animations are important?
Loading...
Story of Progress Bar
Progress Bar
Is just an indicator to show
something is happening on the
device.
Brad A. Myres
It does not matter if progress
bar gives you accurate progress.
What matters was that a
progress is there.
Examples
Examples by me
Base concepts of animation
Animations Type
Tween Animation Physics-based Animation
- In-between.
- Models path between start and
end point.
- curve that defines the timing and
speed of the transition
- Animations with resemble
real-world behavior.
- Whenever possible, apply
real-world physics so they are
natural-looking.
Linear Curve Fling Spring
3 pillars of animation
Ticker Animation Animation
Controller
Animation<T> class
- An animation with a value of type T.
- Animation is nothing else but a value that change in a life span.
- Value change can be linear or curve
AnimationController class
- A controller for an animation.
- Can start, stop, repeat animation.
- Set the animation to a specific value.
- Define the upperBound and lowerBound
values of an animation. (default 0.0 - 1.0)
- Duration of animation.
- Needs Ticker Provider
Ticker class
- Calls its callback once per animation frame.
- around 60 times per second.
Curves
Animation
widgets
Implicit
Animated
widget
Explicit
Animated
widget
- AnimatedBuilder
- AlignTransition
- DecoratedBoxTransition
- DefaultTextStyleTransition
- PositionedTransition
- more...
- AnimatedAlign
- AnimatedContainer
- AnimatedDefaultTextStyle
- AnimatedOpacity
- AnimatedPadding
- more...
You need to
provide Animation
Controller
Manage own
Animation Controller
Implicit Animation
Color bgColor = Colors.redAccent;
return Scaffold(
body: Container(
color: bgColor,
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.autorenew),
onPressed: () { },
),
);
Color bgColor = Colors.redAccent;
return Scaffold(
body: Container(
color: bgColor,
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.autorenew),
onPressed: () {
setState(() {
bgColor = Colors.deepPurpleAccent;
});
},
),
);
Color bgColor = Colors.redAccent;
return Scaffold(
body: Container(
color: bgColor,
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.autorenew),
onPressed: () {
setState(() {
bgColor = Colors.deepPurpleAccent;
});
},
),
);
Color bgColor = Colors.redAccent;
return Scaffold(
body: AnimatedContainer(
color: bgColor,
duration: Duration(milliseconds: 500),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.autorenew),
onPressed: () {
setState(() {
bgColor = Colors.deepPurpleAccent;
});
},
),
);
Color bgColor = Colors.redAccent;
return Scaffold(
body: AnimatedContainer(
color: bgColor,
duration: Duration(milliseconds: 500),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.autorenew),
onPressed: () {
setState(() {
bgColor = Colors.deepPurpleAccent;
});
},
),
);
Color bgColor = Colors.redAccent;
return Scaffold(
body: AnimatedContainer(
color: bgColor,
duration: Duration(milliseconds: 500),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.autorenew),
onPressed: () {
setState(() {
bgColor = Colors.deepPurpleAccent;
_height = 100;
_width = 100;
_radius = 100.0;
});
},
),
);
Tween Animation Builder
return Scaffold(
body: TweenAnimationBuilder(
tween: ColorTween(
begin: Colors.redAccent,
end: Colors.deepPurpleAccent,
),
duration: Duration(milliseconds: 500),
builder: (context, value, child) {
return Container(
color: value,
);
},
),
);
Hero Animation
https://flutter.dev/docs/development/ui/animations/hero-animations
// First Screen
child: Hero(
tag: ‘'imageHero',
child: Image.network(
'https://picsum.photos/250?image=9',
),
),
// Second Screen
child: Hero(
tag: 'imageHero',
child: Image.network(
'https://picsum.photos/250?image=9',
),
),
Explicit Animation
Animated Builder
class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
AnimationController _controller;
void initState() {
_controller = AnimationController(
vsync: this,
duration: Duration(seconds: 3),
);
_controller.forward();
super.initState();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}
Animatable<Color> background = TweenSequence<Color>([
TweenSequenceItem(
weight: 1.0,
tween: ColorTween(
begin: color1,
end: color2,
),
),
TweenSequenceItem(
weight: 1.0,
tween: ColorTween(
begin: color2,
end: color3,
),
),
TweenSequenceItem(
weight: 1.0,
tween: ColorTween(
begin: color3,
end: color4,
),
),
]);
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _controller,
builder: (context, widget) => Container(
color: background.evaluate(_controller),
),
);
}
void initState() {
_controller = AnimationController(
vsync: this,
duration: Duration(seconds: 3),
)..repeat();
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _controller,
builder: (context, widget) => Container(
color: background.evaluate(_controller),
),
);
}
builder: (context, child) {
final double color =
pageController.hasClients ? pageController.page /
3 : 0.0;
return DecoratedBox(
decoration: BoxDecoration(
color: background.evaluate(
AlwaysStoppedAnimation(color),
),
),
child: PageView.builder(
controller: pageController,
itemCount: places.length,
itemBuilder: (context, index) {
return Center(
child: Text('Page $index'),
);
},
),
);
},
builder: (context, child) {
final double color =
pageController.hasClients ? pageController.page /
3 : 0.0;
return DecoratedBox(
decoration: BoxDecoration(
color: background.evaluate(
AlwaysStoppedAnimation(color),
),
),
child: PageView.builder(
controller: pageController,
itemCount: places.length,
itemBuilder: (context, index) {
return Center(
child: Text('Page $index'),
);
},
),
);
},
builder: (context, child) {
final double color =
pageController.hasClients ? pageController.page /
3 : 0.0;
return DecoratedBox(
decoration: BoxDecoration(
color: background.evaluate(
AlwaysStoppedAnimation(color),
),
),
child: PageView.builder(
controller: pageController,
itemCount: places.length,
itemBuilder: (context, index) {
return Center(
child: Text('Page $index'),
);
},
),
);
},
child: PageView.builder(
controller: pageController,
itemCount: places.length,
itemBuilder: (context, index) {
return CustomCard(
assetName: places[index].assetName,
title: places[index].title,
description: places[index].description,
offset: 0.0,
);
},
),
child: PageTransformer(
pageViewBuilder: (context, visibilityResolver) {
return PageView.builder(
controller: pageController,
itemCount: places.length,
itemBuilder: (context, index) {
return CustomCard(
assetName: places[index].assetName,
title: places[index].title,
description: places[index].description,
offset: visibilityResolver
.resolvePageVisibility(index)
.pagePosition,
);
},
);
},
),
Canvas Animation
class CrossPainter extends CustomPainter {
final double fraction;
var _crossPaint;
CrossPainter({this.fraction}) {
_crossPaint = Paint()
..color = crossColor
..strokeWidth = 12.0
..style = PaintingStyle.stroke
..strokeCap = StrokeCap.round;
}
@override
bool shouldRepaint(CrossPainter oldDelegate) {
return oldDelegate.fraction != fraction;
}
@override
void paint(Canvas canvas, Size size) {
double leftLineFraction, rightLineFraction;
if (fraction < .5) {
leftLineFraction = fraction / .5;
rightLineFraction = 0.0;
} else {
leftLineFraction = 1.0;
rightLineFraction = (fraction - .5) / .5;
}
}
canvas.drawLine(
Offset(0.0, 0.0),
Offset(size.width * leftLineFraction, size.height *
leftLineFraction),
_crossPaint);
if (fraction >= .5) {
canvas.drawLine(
Offset(size.width, 0.0),
Offset(size.width - size.width * rightLineFraction,
size.height * rightLineFraction),
_crossPaint);
}
class CirclePainter extends CustomPainter {
final double fraction;
var _circlePaint;
@override
void paint(Canvas canvas, Size size) {
var rect = Offset(0.0, 0.0) & size;
canvas.drawArc(rect, -pi / 2, pi * 2 * fraction, false,
_circlePaint);
}
@override
bool shouldRepaint(CirclePainter oldDelegate) {
return oldDelegate.fraction != fraction;
}
}
Navigation Transition
To Push: Android
To Push: iOS
class ScaleRoute extends PageRouteBuilder {
final Widget page;
ScaleRoute({this.page}) : super (
pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) => page,
transitionDuration: Duration(seconds: 2),
transitionsBuilder: (context, animation, secondaryAnimation, child
) =>
ScaleTransition(
scale: Tween<double>(
begin: 0.0,
end: 1.0,
).animate(
CurvedAnimation(
parent: animation,
curve: Curves.fastOutSlowIn,
),
),
child: child,
),
);
}
Navigator.push(
context,
ScaleRoute(page: SecondPage()),
);
class ScaleRotateRoute extends PageRouteBuilder {
final Widget page;
ScaleRotateRoute({this.page})
: super(
pageBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
) =>
page,
transitionDuration: Duration(seconds: 1),
transitionsBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
) =>
ScaleTransition(
scale: Tween<double>(
begin: 0.0,
end: 1.0,
).animate(
CurvedAnimation(
parent: animation,
curve: Curves.fastOutSlowIn,
),
),
child: RotationTransition(
turns: Tween<double>(
begin: 0.0,
end: 1.0,
).animate(
CurvedAnimation(
parent: animation,
curve: Curves.linear,
),
),
child: child,
),
),
Navigator.push(
context,
ScaleRotateRoute(page: SecondPage()),
);
Physics Based Animations
Spring Simulation
- A spring simulation.
- Models a particle attached to a spring that follows Hooke's law.
- “Hooke's law states that the force (F) needed to extend or compress a
spring by some distance x scales linearly with respect to that distance.”
- F = Kx
@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,
),
),
);
}
void _runAnimation(Offset pixelsPerSecond, Size size) {
_animation = _controller.drive(
AlignmentTween(
begin: _dragAlignment,
end: Alignment.center
,),);
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);
}
void _runAnimation(Offset pixelsPerSecond, Size size) {
_animation = _controller.drive(
AlignmentTween(
begin: _dragAlignment,
end: Alignment.center
,),);
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);
}
Bonus: Flare
What is Flare?
Flare is a powerful design and animation tool, which allows
designers and developers to easily add high-quality animation
to their apps and games.
Flare examples
- Real assets.
- No rebuilding in code.
- Manipulate anything at runtime.
- Open source.
Why Flare matters?
What’s Next?
Thank You
‫תודה‬
Divyanshu Bhargava
@divyanshub024, HighLevel

Fun with flutter animations - Divyanshu Bhargava, GoHighLevel

  • 1.
    Fun with Flutter Animations DivyanshuBhargava @divyanshub024, HighLevel
  • 2.
    What is Animation? Asequence of images or frames.
  • 3.
  • 5.
    “To think creatively,we must be able to look afresh at what we normally take for granted” - George Keller “Animation is about creating the illusion of life. And you can't create it if you don't have one.” - Brad Bird
  • 6.
  • 7.
  • 9.
  • 10.
    Progress Bar Is justan indicator to show something is happening on the device.
  • 11.
    Brad A. Myres Itdoes not matter if progress bar gives you accurate progress. What matters was that a progress is there.
  • 12.
  • 13.
  • 15.
  • 16.
    Animations Type Tween AnimationPhysics-based Animation - In-between. - Models path between start and end point. - curve that defines the timing and speed of the transition - Animations with resemble real-world behavior. - Whenever possible, apply real-world physics so they are natural-looking. Linear Curve Fling Spring
  • 17.
    3 pillars ofanimation Ticker Animation Animation Controller
  • 18.
    Animation<T> class - Ananimation with a value of type T. - Animation is nothing else but a value that change in a life span. - Value change can be linear or curve
  • 19.
    AnimationController class - Acontroller for an animation. - Can start, stop, repeat animation. - Set the animation to a specific value. - Define the upperBound and lowerBound values of an animation. (default 0.0 - 1.0) - Duration of animation. - Needs Ticker Provider
  • 21.
    Ticker class - Callsits callback once per animation frame. - around 60 times per second.
  • 22.
  • 23.
    Animation widgets Implicit Animated widget Explicit Animated widget - AnimatedBuilder - AlignTransition -DecoratedBoxTransition - DefaultTextStyleTransition - PositionedTransition - more... - AnimatedAlign - AnimatedContainer - AnimatedDefaultTextStyle - AnimatedOpacity - AnimatedPadding - more... You need to provide Animation Controller Manage own Animation Controller
  • 24.
  • 25.
    Color bgColor =Colors.redAccent; return Scaffold( body: Container( color: bgColor, ), floatingActionButton: FloatingActionButton( child: Icon(Icons.autorenew), onPressed: () { }, ), );
  • 26.
    Color bgColor =Colors.redAccent; return Scaffold( body: Container( color: bgColor, ), floatingActionButton: FloatingActionButton( child: Icon(Icons.autorenew), onPressed: () { setState(() { bgColor = Colors.deepPurpleAccent; }); }, ), );
  • 27.
    Color bgColor =Colors.redAccent; return Scaffold( body: Container( color: bgColor, ), floatingActionButton: FloatingActionButton( child: Icon(Icons.autorenew), onPressed: () { setState(() { bgColor = Colors.deepPurpleAccent; }); }, ), );
  • 28.
    Color bgColor =Colors.redAccent; return Scaffold( body: AnimatedContainer( color: bgColor, duration: Duration(milliseconds: 500), ), floatingActionButton: FloatingActionButton( child: Icon(Icons.autorenew), onPressed: () { setState(() { bgColor = Colors.deepPurpleAccent; }); }, ), );
  • 29.
    Color bgColor =Colors.redAccent; return Scaffold( body: AnimatedContainer( color: bgColor, duration: Duration(milliseconds: 500), ), floatingActionButton: FloatingActionButton( child: Icon(Icons.autorenew), onPressed: () { setState(() { bgColor = Colors.deepPurpleAccent; }); }, ), );
  • 30.
    Color bgColor =Colors.redAccent; return Scaffold( body: AnimatedContainer( color: bgColor, duration: Duration(milliseconds: 500), ), floatingActionButton: FloatingActionButton( child: Icon(Icons.autorenew), onPressed: () { setState(() { bgColor = Colors.deepPurpleAccent; _height = 100; _width = 100; _radius = 100.0; }); }, ), );
  • 31.
  • 32.
    return Scaffold( body: TweenAnimationBuilder( tween:ColorTween( begin: Colors.redAccent, end: Colors.deepPurpleAccent, ), duration: Duration(milliseconds: 500), builder: (context, value, child) { return Container( color: value, ); }, ), );
  • 33.
  • 34.
  • 38.
    // First Screen child:Hero( tag: ‘'imageHero', child: Image.network( 'https://picsum.photos/250?image=9', ), ), // Second Screen child: Hero( tag: 'imageHero', child: Image.network( 'https://picsum.photos/250?image=9', ), ),
  • 39.
  • 40.
  • 41.
    class _MyHomePageState extendsState<MyHomePage> with SingleTickerProviderStateMixin { AnimationController _controller; void initState() { _controller = AnimationController( vsync: this, duration: Duration(seconds: 3), ); _controller.forward(); super.initState(); } @override void dispose() { _controller.dispose(); super.dispose(); } }
  • 42.
    Animatable<Color> background =TweenSequence<Color>([ TweenSequenceItem( weight: 1.0, tween: ColorTween( begin: color1, end: color2, ), ), TweenSequenceItem( weight: 1.0, tween: ColorTween( begin: color2, end: color3, ), ), TweenSequenceItem( weight: 1.0, tween: ColorTween( begin: color3, end: color4, ), ), ]);
  • 43.
    @override Widget build(BuildContext context){ return AnimatedBuilder( animation: _controller, builder: (context, widget) => Container( color: background.evaluate(_controller), ), ); }
  • 44.
    void initState() { _controller= AnimationController( vsync: this, duration: Duration(seconds: 3), )..repeat(); } @override Widget build(BuildContext context) { return AnimatedBuilder( animation: _controller, builder: (context, widget) => Container( color: background.evaluate(_controller), ), ); }
  • 45.
    builder: (context, child){ final double color = pageController.hasClients ? pageController.page / 3 : 0.0; return DecoratedBox( decoration: BoxDecoration( color: background.evaluate( AlwaysStoppedAnimation(color), ), ), child: PageView.builder( controller: pageController, itemCount: places.length, itemBuilder: (context, index) { return Center( child: Text('Page $index'), ); }, ), ); },
  • 46.
    builder: (context, child){ final double color = pageController.hasClients ? pageController.page / 3 : 0.0; return DecoratedBox( decoration: BoxDecoration( color: background.evaluate( AlwaysStoppedAnimation(color), ), ), child: PageView.builder( controller: pageController, itemCount: places.length, itemBuilder: (context, index) { return Center( child: Text('Page $index'), ); }, ), ); },
  • 47.
    builder: (context, child){ final double color = pageController.hasClients ? pageController.page / 3 : 0.0; return DecoratedBox( decoration: BoxDecoration( color: background.evaluate( AlwaysStoppedAnimation(color), ), ), child: PageView.builder( controller: pageController, itemCount: places.length, itemBuilder: (context, index) { return Center( child: Text('Page $index'), ); }, ), ); },
  • 48.
    child: PageView.builder( controller: pageController, itemCount:places.length, itemBuilder: (context, index) { return CustomCard( assetName: places[index].assetName, title: places[index].title, description: places[index].description, offset: 0.0, ); }, ),
  • 49.
    child: PageTransformer( pageViewBuilder: (context,visibilityResolver) { return PageView.builder( controller: pageController, itemCount: places.length, itemBuilder: (context, index) { return CustomCard( assetName: places[index].assetName, title: places[index].title, description: places[index].description, offset: visibilityResolver .resolvePageVisibility(index) .pagePosition, ); }, ); }, ),
  • 50.
  • 53.
    class CrossPainter extendsCustomPainter { final double fraction; var _crossPaint; CrossPainter({this.fraction}) { _crossPaint = Paint() ..color = crossColor ..strokeWidth = 12.0 ..style = PaintingStyle.stroke ..strokeCap = StrokeCap.round; } @override bool shouldRepaint(CrossPainter oldDelegate) { return oldDelegate.fraction != fraction; }
  • 54.
    @override void paint(Canvas canvas,Size size) { double leftLineFraction, rightLineFraction; if (fraction < .5) { leftLineFraction = fraction / .5; rightLineFraction = 0.0; } else { leftLineFraction = 1.0; rightLineFraction = (fraction - .5) / .5; } }
  • 55.
    canvas.drawLine( Offset(0.0, 0.0), Offset(size.width *leftLineFraction, size.height * leftLineFraction), _crossPaint); if (fraction >= .5) { canvas.drawLine( Offset(size.width, 0.0), Offset(size.width - size.width * rightLineFraction, size.height * rightLineFraction), _crossPaint); }
  • 56.
    class CirclePainter extendsCustomPainter { final double fraction; var _circlePaint; @override void paint(Canvas canvas, Size size) { var rect = Offset(0.0, 0.0) & size; canvas.drawArc(rect, -pi / 2, pi * 2 * fraction, false, _circlePaint); } @override bool shouldRepaint(CirclePainter oldDelegate) { return oldDelegate.fraction != fraction; } }
  • 59.
  • 60.
  • 61.
  • 62.
    class ScaleRoute extendsPageRouteBuilder { final Widget page; ScaleRoute({this.page}) : super ( pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) => page, transitionDuration: Duration(seconds: 2), transitionsBuilder: (context, animation, secondaryAnimation, child ) => ScaleTransition( scale: Tween<double>( begin: 0.0, end: 1.0, ).animate( CurvedAnimation( parent: animation, curve: Curves.fastOutSlowIn, ), ), child: child, ), ); }
  • 63.
  • 64.
    class ScaleRotateRoute extendsPageRouteBuilder { final Widget page; ScaleRotateRoute({this.page}) : super( pageBuilder: ( BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, ) => page, transitionDuration: Duration(seconds: 1), transitionsBuilder: ( BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child, ) =>
  • 65.
    ScaleTransition( scale: Tween<double>( begin: 0.0, end:1.0, ).animate( CurvedAnimation( parent: animation, curve: Curves.fastOutSlowIn, ), ), child: RotationTransition( turns: Tween<double>( begin: 0.0, end: 1.0, ).animate( CurvedAnimation( parent: animation, curve: Curves.linear, ), ), child: child, ), ),
  • 66.
  • 67.
  • 68.
    Spring Simulation - Aspring simulation. - Models a particle attached to a spring that follows Hooke's law. - “Hooke's law states that the force (F) needed to extend or compress a spring by some distance x scales linearly with respect to that distance.” - F = Kx
  • 69.
    @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, ), ), ); }
  • 70.
    void _runAnimation(Offset pixelsPerSecond,Size size) { _animation = _controller.drive( AlignmentTween( begin: _dragAlignment, end: Alignment.center ,),); 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); }
  • 71.
    void _runAnimation(Offset pixelsPerSecond,Size size) { _animation = _controller.drive( AlignmentTween( begin: _dragAlignment, end: Alignment.center ,),); 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); }
  • 72.
  • 73.
    What is Flare? Flareis a powerful design and animation tool, which allows designers and developers to easily add high-quality animation to their apps and games.
  • 74.
  • 75.
    - Real assets. -No rebuilding in code. - Manipulate anything at runtime. - Open source. Why Flare matters?
  • 76.
  • 77.