Rodrigo Bastos
Rodrigo Bastos

Reputation: 2448

How to start intervaled animations in flutter

I want to animate three balls falling on the screen. But i want the animation of the first ball to start sooner than the animation of the second ball and the animation of the second ball start a little sooner than the animation of the third ball.

I tried to give some sort of timeout in between the forward of the animatons but for some reason that didn't worked out. On the code bellow i acomplished the same animation for the three balls.

import 'package:flutter/material.dart';

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

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> with TickerProviderStateMixin {
  AnimationController animationController1,
      animationController2,
      animationController3;

  Animation<double> animation, animation2, animation3;

  @override
  void initState() {
    animationController1 =
        AnimationController(duration: Duration(milliseconds: 300), vsync: this);
    animationController2 =
        AnimationController(duration: Duration(milliseconds: 300), vsync: this);
    animationController3 =
        AnimationController(duration: Duration(milliseconds: 300), vsync: this);

    animation = Tween(
      begin: 0.0,
      end: 1.0,
    ).animate(
      CurvedAnimation(parent: animationController1, curve: Interval(0.0, 1.0)),
    );

    animation2 = Tween(
      begin: 0.0,
      end: 1.0,
    ).animate(
      CurvedAnimation(parent: animationController2, curve: Interval(1.0, 2.0)),
    );

    animation3 = Tween(
      begin: 0.0,
      end: 1.0,
    ).animate(
      CurvedAnimation(parent: animationController3, curve: Interval(2.0, 3.0)),
    );

    animationController1.addListener(() {
      print('1 ${animation.value}');
      print('2 ${animation.value}');
      print('3 ${animation.value}');
      setState(() {});
    });

    animationController1.addStatusListener((status) {
      if (status == AnimationStatus.completed) {
        animationController1.reverse();
      } else if (status == AnimationStatus.dismissed) {
        animationController1.forward();
      }
    });
    animationController2.addStatusListener((status) {
      if (status == AnimationStatus.completed) {
        animationController2.reverse();
      } else if (status == AnimationStatus.dismissed) {
        animationController2.forward();
      }
    });
    animationController3.addStatusListener((status) {
      if (status == AnimationStatus.completed) {
        animationController3.reverse();
      } else if (status == AnimationStatus.dismissed) {
        animationController3.forward();
      }
    });

    animationController1.forward();
    animationController2.forward();
    animationController3.forward();
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Container(
          child: Center(
            child: Row(
              mainAxisAlignment: MainAxisAlignment.center,
              crossAxisAlignment: CrossAxisAlignment.stretch,
              children: <Widget>[
                buildBallOne(),
                SizedBox(
                  width: 6.0,
                ),
                buildBallTwo(),
                SizedBox(
                  width: 6.0,
                ),
                buildBallThree()
              ],
            ),
          ),
        ),
      ),
    );
  }

  Widget buildBallOne() {
    return Container(
      margin: EdgeInsets.only(top: 100 - (animationController1.value * 50)),
      width: 35,
      height: 35,
      decoration: ShapeDecoration(
        shape: CircleBorder(),
        color: Colors.blue,
      ),
    );
  }

  Widget buildBallTwo() {
    return Container(
      margin: EdgeInsets.only(top: 100 - (animationController2.value * 50)),
      width: 35,
      height: 35,
      decoration: ShapeDecoration(
        shape: CircleBorder(),
        color: Colors.blue,
      ),
    );
  }

  Widget buildBallThree() {
    return Container(
      margin: EdgeInsets.only(top: 100 - (animationController3.value * 50)),
      width: 35,
      height: 35,
      decoration: ShapeDecoration(
        shape: CircleBorder(),
        color: Colors.blue,
      ),
    );
  }
}

Upvotes: 1

Views: 1232

Answers (1)

Kherel
Kherel

Reputation: 16185

That's because you run all your controllers at the same time.

enter image description here

I've only added delays and made some tunes.

import 'package:flutter/material.dart';

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

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> with TickerProviderStateMixin {
  AnimationController animationController1, animationController2, animationController3;

  Animation<double> animation1, animation2, animation3;

  @override
  void initState() {
    animationController1 = AnimationController(
      duration: Duration(milliseconds: 600),
      vsync: this,
    );
    animationController2 = AnimationController(
      duration: Duration(milliseconds: 600),
      vsync: this,
    );
    animationController3 = AnimationController(
      duration: Duration(milliseconds: 600),
      vsync: this,
    );

    animation1 = Tween(
      begin: 0.0,
      end: 1.0,
    ).animate(
      CurvedAnimation(parent: animationController1, curve: Interval(0.0, 1.0)),
    );

    animation2 = Tween(
      begin: 0.0,
      end: 1.0,
    ).animate(
      CurvedAnimation(parent: animationController2, curve: Interval(0.0, 1.0)),
    );

    animation3 = Tween(
      begin: 0.0,
      end: 1.0,
    ).animate(
      CurvedAnimation(parent: animationController3, curve: Interval(0.0, 1.0)),
    );

    animationController1.addListener(() {
      print('1 ${animation1?.value}');
      print('2 ${animation2?.value}');
      print('3 ${animation3?.value}');
      setState(() {});
    });

    animationController1.addStatusListener((status) {
      if (status == AnimationStatus.completed) {
        animationController1.reverse();
      } else if (status == AnimationStatus.dismissed) {
        animationController1.forward();
      }
    });
    animationController2.addStatusListener((status) {
      if (status == AnimationStatus.completed) {
        animationController2.reverse();
      } else if (status == AnimationStatus.dismissed) {
        animationController2.forward();
      }
    });
    animationController3.addStatusListener((status) {
      if (status == AnimationStatus.completed) {
        animationController3.reverse();
      } else if (status == AnimationStatus.dismissed) {
        animationController3.forward();
      }
    });

    Future.delayed(
      Duration(milliseconds: 300),
      () => animationController1.forward(),
    );

    Future.delayed(
      Duration(milliseconds: 600),
      () => animationController2.forward(),
    );
    Future.delayed(
      Duration(milliseconds: 900),
      () => animationController3.forward(),
    );

    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Container(
          child: Center(
            child: Row(
              mainAxisAlignment: MainAxisAlignment.center,
              crossAxisAlignment: CrossAxisAlignment.stretch,
              children: <Widget>[
                buildBallOne(),
                SizedBox(
                  width: 6.0,
                ),
                buildBallTwo(),
                SizedBox(
                  width: 6.0,
                ),
                buildBallThree()
              ],
            ),
          ),
        ),
      ),
    );
  }

  Widget buildBallOne() {
    return Container(
      margin: EdgeInsets.only(top: 100 - (animationController1.value * 50)),
      width: 35,
      height: 35,
      decoration: ShapeDecoration(
        shape: CircleBorder(),
        color: Colors.blue,
      ),
    );
  }

  Widget buildBallTwo() {
    return Container(
      margin: EdgeInsets.only(top: 100 - (animationController2.value * 50)),
      width: 35,
      height: 35,
      decoration: ShapeDecoration(
        shape: CircleBorder(),
        color: Colors.blue,
      ),
    );
  }

  Widget buildBallThree() {
    return Container(
      margin: EdgeInsets.only(top: 100 - (animationController3.value * 50)),
      width: 35,
      height: 35,
      decoration: ShapeDecoration(
        shape: CircleBorder(),
        color: Colors.blue,
      ),
    );
  }
}

Actually because your animation path is the same for all balls, you could use one controller and calculate the position differently for each ball

P.s. use Animated Widget or AnimatedBuilder, to not trigger setState to refresh the page, it will provide better performance.

Upvotes: 1

Related Questions