How to create animations in Flutter with Redux?

Paulina Szklarska
Apr 10, 2019 · 8 min read

Let’s go for shopping!

Image for post
Image for post
Shopping list app
Image for post
Image for post
Shopping list app (with animation!)

First — let’s check our cart value 💸

We’ll start firstly with adding a simple bar at the bottom of the screen, showing current cart value. Take a look at the code for this bar:

import 'package:flutter/material.dart';

const CART_BAR_HEIGHT = 48.0;

class CartValueBar extends StatelessWidget {
final double cartValue;

const CartValueBar({Key key, this.cartValue})
: super(key: key);

@override
Widget build(BuildContext context) {
return cartValue != 0
? Container(
height: CART_BAR_HEIGHT,
color: Colors.orange,
child: Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
'Cart value ${_getCartValue()} zł',
),
),
],
),
)
: Container();
}

String _getCartValue() => cartValue.toStringAsFixed(2);
}
Image for post
Image for post
Shopping list app (without animation)

Show me some nice animation!

First one is to convert our CartValueBar into StatefulWidget :

class CartValueBar extends StatefulWidget {
final double cartValue;

const CartValueBar({Key key, this.cartValue}) : super(key: key);

@override
_CartValueBarState createState() => _CartValueBarState();
}
class _CartValueBarState extends State<CartValueBar> {

@override
Widget build(BuildContext context) {
//TODO we'll insert CartValueBar code here
return Container();
}

}
class _CartValueBarState extends State<CartValueBar>
with SingleTickerProviderStateMixin {
AnimationController _controller;
Animation<double> _animation;

@override
void initState() {
super.initState();

_controller = AnimationController(
vsync: this,
duration: Duration(milliseconds: 300),
)..addListener(() {
setState(() {});
});

_animation = Tween<double>(
begin: CART_BAR_HEIGHT,
end: 0,
).animate(
CurvedAnimation(
parent: _controller,
curve: Curves.easeOut,
reverseCurve: Curves.easeIn,
),
);
}

...

@override
void dispose() {
super.dispose();
_controller.dispose();
}
}
class _CartValueBarState extends State<CartValueBar>
with SingleTickerProviderStateMixin {

...

@override
Widget build(BuildContext context) {
return widget.cartValue != 0
? Align(
alignment: Alignment.bottomCenter,
child: Transform.translate( //changed
offset: Offset(0, _animation.value), //changed
child: Container(
color: Colors.orange,
child: Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
'Cart value ${_getCartValue()} zł',
),
),
],
),
),
),
)
: Container();
}

...
}

Where should we start animation?

To decide in what place in the code should we put animation start point, we need to know one important thing about Redux — whenever store emits some change event, our Widgets are rebuilt. So actually with every update, we only know about the current values (in our case, current cart value). It’s not enough to make an animation, because to make it properly, we need to know about the previous state also:

  • we want to hide cart bar when current cart value is zero
@override
void didUpdateWidget(CartValueBar oldWidget) {
super.didUpdateWidget(oldWidget);

if (oldWidget.cartValue == 0) {
_controller.forward();
}

if (widget.cartValue == 0) {
_controller.reverse();
}
}
Image for post
Image for post
Cool animation is almost there…
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Text(
widget.cartValue != 0
? 'Cart value ${_getCartValue()} zł'
: '',
),
),
Image for post
Image for post
Final effect!

Cool, it’s the end! Hmm.. Is it?

Actually, we could stop right here and end the article. We achieved what we wanted. But hey, we’re Flutter developers, we know that everything is a widget in Flutter, so… maybe there’s already a Widget that can make this whole “didUpdateWidget()” job for us? Of course, it is.

import 'package:flutter/material.dart';const CART_BAR_HEIGHT = 48.0;class CartValueBar extends StatelessWidget {
final double cartValue;

const CartValueBar({Key key, this.cartValue})
: super(key: key);

@override
Widget build(BuildContext context) {
return AnimatedContainer( //changed
height: CART_BAR_HEIGHT,
duration: Duration(milliseconds: 300), //changed
transform: Matrix4.translationValues( //changed
0, cartValue != 0 ? 0 : CART_BAR_HEIGHT, 0),
//changed
color: Colors.orange,
child: Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16.0),
child: Text(
cartValue != 0
? 'Cart value ${_getCartValue()} zł'
: '',
),
),
],
),
);
}

String _getCartValue() => cartValue.toStringAsFixed(2);
}

That’s the end!

Ok, that’s the real end of this article.

Flutter Community

Articles and Stories from the Flutter Community

Thanks to Michal Lankof

Paulina Szklarska

Written by

Flutter GDE / Android&Flutter Developer / GDG & WTM Wrocław Lead / blogger / speaker / cat owner / travel enthusiast

Flutter Community

Articles and Stories from the Flutter Community

Paulina Szklarska

Written by

Flutter GDE / Android&Flutter Developer / GDG & WTM Wrocław Lead / blogger / speaker / cat owner / travel enthusiast

Flutter Community

Articles and Stories from the Flutter Community

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store