bluenile
bluenile

Reputation: 6029

How to restart an animation stopped before completion but with different parameters

I have an animation where two containers collide with each other. I want the containers to reverse back with animation to their respective starting offset. The containers move along varied paths and duration.

enter image description here

As seen in the animation gif attached the green and red containers collide and jump back to their starting offsets rather than sliding back.

Here is the code I used to make the GIF, in my actual code I'm using rectangle intersection to check when the containers collide.

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

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

class _MyAppState extends State<MyApp> with TickerProviderStateMixin {
  List<AnimationController> _controller = List(2);
  List<Animation<Offset>> _animation = List(2);
  List<Tween<Offset>> tween = List(2);

  @override
  void initState() {
    super.initState();
    tween[0] = Tween(begin: Offset(0, 10), end: Offset(200, 10));
    tween[1] = Tween(begin: Offset(200, 10), end: Offset(0, 10));
    for (int i = 0; i < 2; i++) {
      _controller[i] =
          AnimationController(vsync: this, duration: Duration(seconds: 1));

      _animation[i] = tween[i].animate(_controller[i])
        ..addListener(
          () {
            setState(
              () {
                print(
                    '${_animation[0].value.dx.toInt()}:${_animation[0].value.dy.toInt()} ${_animation[1].value.dx.toInt()}:${_animation[1].value.dy.toInt()}');
                if (((_animation[0].value.dx) / 2).round() ==
                    ((_animation[1].value.dx) / 2).round()) {
                  _controller[0].stop();
                  _controller[1].stop();

                  _controller[0].reset();
                  _controller[1].reset();

                  Future.delayed(Duration(milliseconds: 100)).then((_) {
                    tween[0] = Tween(
                        begin: Offset(
                            _animation[0].value.dx, _animation[0].value.dy),
                        end: Offset(0, 10));
                    tween[1] = Tween(
                        begin: Offset(
                            _animation[1].value.dx, _animation[1].value.dy),
                        end: Offset(200, 10));

                    _animation[0] = tween[0].animate(_controller[0]);
                    _animation[1] = tween[1].animate(_controller[1]);

                    _controller[0].forward();
                    _controller[1].forward();
                  });
                }
              },
            );
          },
        );
    }

    WidgetsBinding.instance.addPostFrameCallback(
      (_) => _afterLayout(),
    );
  }

  void _afterLayout() {
    for (int i = 0; i < 2; i++) {
      _controller[i].forward();
    }
  }

  @override
  void dispose() {
    super.dispose();
    for (int i = 0; i < 1; i++) {
      _controller[i].dispose();
    }
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text("collision"),
        ),
        body: Column(
          children: <Widget>[
            Expanded(
              child: Stack(
                children: <Widget>[
                  Container(
                    width: double.infinity,
                    height: double.infinity,
                  ),
                  Positioned(
                    left: _animation[0] != null ? _animation[0].value.dx : 0,
                    top: _animation[0] != null ? _animation[0].value.dy : 0,
                    child: Container(
                      width: 50,
                      height: 50,
                      color: Colors.red,
                    ),
                  ),
                  Positioned(
                    left: _animation[1] != null ? _animation[1].value.dx : 0,
                    top: _animation[1] != null ? _animation[1].value.dy : 0,
                    child: Container(
                      width: 50,
                      height: 50,
                      color: Colors.green,
                    ),
                  ),
                ],
              ),
            ),
            RaisedButton(
              onPressed: () {
                _controller[0].reset();
                _controller[1].reset();

                tween[0] = Tween(begin: Offset(0, 10), end: Offset(200, 10));
                tween[1] = Tween(begin: Offset(200, 10), end: Offset(0, 10));

                _controller[0].duration = Duration(seconds: 1);
                _controller[1].duration = Duration(seconds: 1);

                _animation[0] = tween[0].animate(_controller[0]);
                _animation[1] = tween[1].animate(_controller[1]);

                _controller[0].forward();
                _controller[1].forward();
              },
              child: Text("Animate"),
            )
          ],
        ),
      ),
    );
  }
}

I want the containers to smoothly slide back to their respective starting offsets with animation.

Upvotes: 3

Views: 4270

Answers (1)

danypata
danypata

Reputation: 10175

I will add a bit of explanation here to help others maybe.

Animations in flutter are controlled by an AnimationController. You can control one ore more animations using a single AnimationController ( you can use same controller if you want to have animations triggered simultaneously).

Now, to start an animation (or all animations linked to the same controller) you can use forward() method. The forward() method accepts a parameter that specify from which point on should the animation continue, the parameter value is between 0...1.

If you want to do the animation in backwards, the controller has a method called reverse() which will perform the animation in the reverse order. Same, the method accepts a parameter with value from 0 to 1, if no parameter is specified the animation will "reverse" from the last state.

Note in order to have a reverse animation you should not call reset() on the controller. reset() method will set all the values of the controller to the initial ones.

Upvotes: 6

Related Questions