20

I'm trying to add the curves class animation 'Curves.bounceOut' to my code that uses an AnimationController.

Here is my code:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
  var squareScale = 1.0;
  static AnimationController _controller;

  @override
  void initState() {
    _controller = AnimationController(
        vsync: this,
        lowerBound: 0.5,
        upperBound: 1.0,
        duration: Duration(milliseconds: 300));

    _controller.addListener(() {
      setState(() {
        squareScale = _controller.value;
      });
    });

    super.initState();
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Bounce Example"),
      ),
      body: Stack(
        children: <Widget>[
          Container(
            width: 150.0,
            height: 150.0,
            color: Colors.yellowAccent,
          ),
          Column(
            children: <Widget>[
              Row(
                children: <Widget>[
                  GestureDetector(
                    onTap: () {
                      _controller.forward(from: 0.0);
                    },
                    child: Transform.scale(
                      scale: squareScale,
                      child: Container(
                        width: 150.0,
                        height: 150.0,
                        color: Colors.green,
                      ),
                    ),
                  ),
                ],
              ),
            ],
          ),
        ],
      ),
    );
  }
}

Currently the green container animates from 0.5 scale to 1.0 scale but does not bounce. How can I add the 'Curves.bounceOut' animation so the container bounces when tapped?

Thanks in advance for any help!

hello_friend
  • 707
  • 3
  • 9
  • 19

2 Answers2

41

You'll want to use both an animation controller and a tween, which will allow you to add Curves.bounceOut.

Somewhere in your code add:

AnimationController _controller;
var scaleAnimation;

And in your initState() method:

_controller = new AnimationController(
 duration: new Duration(milliseconds: 300),
 vsync: this
)
..addListener(() {
  setState(() {});
});
scaleAnimation = new Tween(
  begin: 0.5,
  end: 1.0,
).animate(new CurvedAnimation(
  parent: _controller,
  curve: Curves.bounceOut
));

Note the ..addListener() is a nice bit of dart code that allows you to add a listener easily. The Tween is basically telling your animation that it needs to go from the value in begin to the value in end within the timeframe given in the AnimationController. To use this value in your code you can now just set the scale of your result of your animation:

child: Transform.scale(
  scale: scaleAnimation.value,
  child: Container(
    width: 150.0,
    height: 150.0,
    color: Colors.green,
  ),
),

And when the animation is called it will then automatically update the scale of the container.

Edit:

Change your widget to this:

child: isChanging ? Transform.scale(
  scale: scaleAnimation.value,
  child: Container(
    width: 150.0,
    height: 150.0,
    color: Colors.green,
  ),
) : Transform.scale(
  scale: 1.0,
  child: Container(
    width: 150.0,
    height: 150.0,
    color: Colors.green,
  ),
),

Keep your begin and end parameters as 0.5 and 1.0 respectively, but in your onTap() method before you forward your controller add:

onTap: () {
  setState(() {
    isChanging = true
  });
  _controller.forward(from: 0.0);
}

And anywhere in your code add the line bool isChanging. Finally you'll want to reset the shown widget at the end of your animation, so change your scaleAnimation to:

child: Transform.scale(
  scale: scaleAnimation.value,
  child: Container(
    width: 150.0,
    height: 150.0,
    color: Colors.green,
  ),
)..addStatusListener((AnimationStatus status) {
  if (status == AnimationStatus.completed) { //the animation is finished
    _controller.reset();
    setState(() {
      isChanging = false;
    }
  }
});
RhysD
  • 1,597
  • 2
  • 17
  • 30
  • Thanks this worked! Do you know how I can have the container start at a width of 150.0 by 150.0 before any animation has been triggered? Right now it starts at a scale of 0.5, not 1.0. – hello_friend Mar 26 '19 at 20:06
  • You could swap the `begin` and `end` parameters within the `scaleAnimation`. This would make the scale start at `1.0` and get smaller and bigger. Hope this helps! – RhysD Mar 26 '19 at 20:08
  • Sorry I must not have been clear enough. I want the animation to go from the scale of 0.5 to 1.0 every time the container is tapped. However when the app is first launched the container starts at a scale of 0.5, I want it to start at a scale of 1.0 and then when tapped jump straight to scale 0.5 and animate up to scale 1.0. Is this possible? Thanks again for your help. – hello_friend Mar 26 '19 at 20:20
  • @hello_friend It might be possible.... you would have to set *both* the `begin` and `end` parameters to 1.0, and then have your curve be part of a `sin(x)` curve. Even then, however, the animation would not jump from being at 1.0 to being at 0.5 instantly. Potentially you could have a conditional statement where the widget is, where if a boolean is true then it display a 1.0 scale of the container, then when you click the button it would set it to false, changing the widget to a widget of scale 0.5 and then run the animation on that widget. Let me know if you need me to add that to my answer. – RhysD Mar 26 '19 at 20:26
  • That would be great if you added this to your answer. – hello_friend Mar 26 '19 at 20:32
12

A more succinct way to do this is to just use controller.drive() and CurveTween:

anim = animController.drive(CurveTween(curve: Curves.easeOut));

Then use anim.value in your widget tree.

Can also do this inline if you want:

Opacity(opacity: controller.drive(CurveTween(curve: Curves.easeOut)).value)
shawnblais
  • 1,038
  • 11
  • 23