Reputation: 6029
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.
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
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