In some Flutter animation tutorials, some uses a Tween and an Animation object. Some uses AnimationController only. Both code below seems to output the same result. So when do we use a Tween with animation and when do we use AnimationController only?
With Tween and animation
import 'dart:core';
import 'package:flutter/material.dart';
class Test extends StatefulWidget {
#override
_State createState() {
return _State();
}
}
class _State extends State<Test> with TickerProviderStateMixin {
AnimationController _animationController;
Animation _animation;
bool faded = true;
#override
void initState() {
super.initState();
_animationController = new AnimationController(
value:0.0,
vsync: this,
upperBound: 1.0,
lowerBound: 0.0,
duration: new Duration(seconds:1),
);
_animation = Tween(begin: 0.0, end: 1.0).animate(_animationController);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: new AppBar(
elevation: 0.5,
title: new Text(
"Testing views",
style: Theme.of(context).textTheme.title,
),
),
body: _buildBodyAnimationTest(),
// body: _buildTuto(),
);
}
Widget _buildBodyAnimationTest(){
return FadeTransition(
opacity: _animation, //here is the difference
child: InkWell(
onTap: (){
if(faded){
faded = false;
_animationController.reverse();
}else{
faded = true;
_animationController.forward();
}
},
child: new Container(
color: Colors.red,
),
),
);
}
}
Without Tween and Animation
import 'dart:core';
import 'package:flutter/material.dart';
class Test extends StatefulWidget {
#override
_State createState() {
return _State();
}
}
class _State extends State<Test> with TickerProviderStateMixin {
AnimationController _animationController;
bool faded = true;
#override
void initState() {
super.initState();
_animationController = new AnimationController(
value:0.0,
vsync: this,
upperBound: 1.0,
lowerBound: 0.0,
duration: new Duration(seconds:1),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: new AppBar(
elevation: 0.5,
title: new Text(
"Testing views",
style: Theme.of(context).textTheme.title,
),
),
body: _buildBodyAnimationTest(),
// body: _buildTuto(),
);
}
Widget _buildBodyAnimationTest(){
return FadeTransition(
opacity: _animationController, //here is the difference
child: InkWell(
onTap: (){
if(faded){
faded = false;
_animationController.reverse();
}else{
faded = true;
_animationController.forward();
}
},
child: new Container(
color: Colors.red,
),
),
);
}
}
Tweens are objects used to transform the output of an Animation (such as AnimationController).
With AnimationController, you typically have a 0-1 floating value. But you rarely want that.
Tweens allows to map that 0-1 to something more concrete such as red to blue, left to right, ...
Background is my tween sequence color.
Animatable<Color> background = TweenSequence<Color>(
[
TweenSequenceItem(
weight: 1.0,
tween: ColorTween(
begin: colors[_Substance.dayEndBackground],
end: colors[_Substance.dayStartBackground],
),
),
TweenSequenceItem(
weight: 1.0,
tween: ColorTween(
begin: colors[_Substance.dayStartBackground],
end: colors[_Substance.dayEndBackground],
),
),
],
);
This is my controller defined in initState() and updated in every one second:
bgUpdateController = AnimationController(
value: _currentDateTime.hour / 24,
upperBound: 1,
lowerBound: 0,
duration: const Duration(hours: 24),
vsync: this,
)..repeat();
I have used the above background and controller as AnimatedBuilder like below:
AnimatedBuilder(
animation: bgUpdateController,
builder: (context, child) {
return Scaffold(
backgroundColor: background
.chain(
CurveTween(curve: Curves.easeInOutCirc),
)
.evaluate(
AlwaysStoppedAnimation(bgUpdateController.value),
),
...
and the result of my code is:
Conclusion
AnimationController is for how long the animation would be and how to control from time, upper and lower boundary, how to control data with time, length, sequence, etc. while AnimationTween is for the range of animation with time, colour, range, sequence, etc as long the animation would be while.
Related
I am trying to create a Slide in and Slide out animation in Flutter. Animation should look like this:
----- Widget slides in ---> Wait for 1 seconds -----Widget slides out of screen -->
I have tried following code but my animation is stuck in a loop.
class _MyStatefulWidgetState extends State<MyStatefulWidget> with SingleTickerProviderStateMixin {
AnimationController _controller;
Animation<Offset> _positionAnimation;
Animation<double> opacityAnimation;
#override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 2),
vsync: this,
);
_positionAnimation = Tween<Offset>(
begin: const Offset(-1, 0),
end: const Offset(0, 0.0),
).animate(
CurvedAnimation(
parent: _controller,
curve: Curves.elasticOut),
)..addStatusListener((status) {
print('animation 1 status $status');
if (status == AnimationStatus.completed) {
_controller.reset();
}
if (status == AnimationStatus.dismissed) {
_positionAnimation = Tween<Offset>(
begin: const Offset(0, 0),
end: const Offset(1, 0.0),
).animate(
CurvedAnimation(
parent: _controller,
curve: Curves.elasticIn),
)..addStatusListener((status2) {
print('animation 2 status $status2');
if (status == AnimationStatus.dismissed) {
_controller.reset();
}
});
_controller.forward();
}
});
_controller.forward();
}
#override
void dispose() {
super.dispose();
_controller.dispose();
}
#override
Widget build(BuildContext context) {
return AnimatedBuilder(
builder: _buildAnimation,
animation: _controller,
);
}
Widget _buildAnimation(BuildContext context, Widget child) {
return Opacity(
opacity: 1,
child: SlideTransition(
position: _positionAnimation,
child: Container(
color: Colors.blueAccent,
height: 100,
child: Center(
child: Text(
'Hello, World!',
style: Theme.of(context).textTheme.display1,
),
),
),
),
);
}
With this approach animation is stuck in a loop.
I cannot use staggered animation as I am trying to animate same property. (Or is there a way to use staggered animation on same property?).
Any better way for implementing this?
Taking idea from #pskink code in comments, achieved desired effect with following code using TweenAnimationBuilder:
class _SlideInOutWidgetState extends State<SlideInOutWidget>
with SingleTickerProviderStateMixin {
double startPos = -1.0;
double endPos = 0.0;
Curve curve = Curves.elasticOut;
#override
Widget build(BuildContext context) {
return TweenAnimationBuilder(
tween: Tween<Offset>(begin: Offset(startPos, 0), end: Offset(endPos, 0)),
duration: Duration(seconds: 1),
curve: curve,
builder: (context, offset, child) {
return FractionalTranslation(
translation: offset,
child: Container(
width: double.infinity,
child: Center(
child: child,
),
),
);
},
child: Text('animated text', textScaleFactor: 3.0,),
onEnd: () {
print('onEnd');
Future.delayed(
Duration(milliseconds: 500),
() {
curve = curve == Curves.elasticOut
? Curves.elasticIn
: Curves.elasticOut;
if (startPos == -1) {
setState(() {
startPos = 0.0;
endPos = 1.0;
});
}
},
);
},
);
}
}
Use Marquee plugin for text Animations
Install :
marquee: ^1.3.1
Example :
Marquee(
text: 'There once was a boy who told this story about a boy: "',
)
Fore more info try marquee | flutterpckage
I have two animating menus. dropDownMenu is working fine, and I can set onPress events from this child to perform functions in the parent class using callbacks, for example:
class dropDownMenu extends StatefulWidget {
final Function() onPressed;
final String tooltip;
final IconData icon;
final _callback;
dropDownMenu({Key key, this.onPressed, this.tooltip, this.icon, #required void singlePlayerCallbacks(String callBackType) } ):
_callback = singlePlayerCallbacks, super(key: key);
#override
_dropDownMenuState createState() => _dropDownMenuState();
}
class _dropDownMenuState extends State<dropDownMenu>
with SingleTickerProviderStateMixin {
#override
Widget build(BuildContext context) {
return Stack(
children: <Widget> [
Column(
Container(
child: Opacity(
opacity: 0.0,
child: FloatingActionButton(
heroTag: null,
onPressed: isOpened == true? (){
widget?._callback('dice');
} : () {},
),
),
),
],
);
}
And then in the parent class:
class SinglePlayerMode extends StatefulWidget {
#override
SinglePlayerModeParentState createState() => SinglePlayerModeParentState();
}
class SinglePlayerModeParentState extends State<SinglePlayerMode> {
callBacks(String callBackType) {
switch(callBackType) {
case 'dice':
{
diceVisible = !diceVisible;
int rng = new Random().nextInt(19) + 1;
setState(() {
diceResult = rng;
});
}
}
break;
}
#override
Widget build(BuildContext context) {
child: Scaffold(
body: Container(
Padding(
padding: EdgeInsets.all(0.0),
child: dropDownMenu(
singlePlayerCallbacks: callBacks,
),
),
),
),
}
As a quick example, and this works perfectly fine.
What I need to do next is have another animated menu, called styleMenu, that animates when a button from dropDownMenu is pressed. This is where I am running into massive hurdles. I honestly don't mind HOW I get this done, I just need to get it done. This is what I am trying currently, without any success:
In dropDownMenu I have another button with a callback to the parent first:
Container(
child: Opacity(
opacity: 0.0,
child: FloatingActionButton(
heroTag: null,
onPressed: isOpened == true? (){
widget?._callback('theme');
} : () {},
),
),
),
Which triggers the callback function of the parent again, with a different switch case:
callBacks(String callBackType) {
case 'theme':
{
styleMenuState().animate();
}
break;
I obviously can't do this because it tells me that I am trying to animate a null object. Like I somehow have to instantiate styleMenu before I can call this function from here, but I don't know how to do this or even if it is possible.
My styleMenu class (extract):
class styleMenu extends StatefulWidget {
final Function() onPressed;
final String tooltip;
final IconData icon;
final _callback;
final VoidCallback animate;
styleMenu({this.onPressed, this.tooltip, this.animate, this.icon, #required void singlePlayerCallbacks(String callBackType) } ):
_callback = singlePlayerCallbacks;
#override
styleMenuState createState() => styleMenuState();
}
class styleMenuState extends State<styleMenu>
with SingleTickerProviderStateMixin {
bool isOpened = false;
AnimationController animationController;
Animation<Color> _buttonColor;
Animation<double> _animateIcon;
Animation<double> _translateButton;
Curve _curve = Curves.easeOut;
double _fabHeight = 52.0;
#override
initState() {
animationController =
AnimationController(vsync: this, duration: Duration(milliseconds: 600))
..addListener(() {
setState(() {});
});
_animateIcon =
Tween<double>(begin: 0.0, end: 1.0).animate(animationController);
_buttonColor = ColorTween(
begin: Colors.blue,
end: Colors.red,
).animate(CurvedAnimation(
parent: animationController,
curve: Interval(
0.0,
1.0,
curve: Curves.linear,
),
));
_translateButton = Tween<double>(
begin: 0.0,
end: _fabHeight,
).animate(CurvedAnimation(
parent: animationController,
curve: Interval(
0.0,
1.0,
curve: _curve,
),
));
super.initState();
}
#override
dispose() {
animationController.dispose();
super.dispose();
}
animate() {
if (!isOpened) {
styleMenuState().animationController.forward();
} else {
styleMenuState().animationController.reverse();
}
isOpened = !isOpened;
}
#override
Widget build(BuildContext context) {
return Stack(
children: <Widget> [
Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Stack(
children: <Widget>[
Transform(
transform: Matrix4.translationValues(
0,
_translateButton.value,
0,
),
child: blueTheme(),
),
Transform(
transform: Matrix4.translationValues(
0,
_translateButton.value * 2,
0,
),
child: greenTheme(),
),
Transform(
transform: Matrix4.translationValues(
0,
_translateButton.value * 3,
0,
),
child: redTheme(),
),
blackTheme(),
],
),
],
),
);
}
Again, I just need to be able to trigger the animate function from the styleMenu to pop this menu out by pressing a button inside the dropDownMenu and I just can't get my head around how to do this! I am sure there must be a simple way, but I am unable to find anything online.
Any pros out there?
I was just working on a similar problem. Using Streams, Provider with ChangeNotifiers, or inherited widgets are viable options. If you do want to simply trigger the child widget animations on setState called from the parent you can do that by using the didUpdateWidget in the child widget as shown below
#override
void didUpdateWidget(ImageHeartAnimation oldWidget) {
_controller.forward().orCancel;
super.didUpdateWidget(oldWidget);
}
I have made one container with scale transition which grows from 0 height and width to 90 height and width.
Now what,I wanted to do is it should slowly fade out as it grows.Do i need to create another animation controller for opacity ? what is the best way to do it ? Can Some one Help?
My Code Look Like This
import 'package:flutter/animation.dart';
import 'package:flutter/material.dart';
void main() => runApp(new MyAnimationApp());
class MyAnimationApp extends StatefulWidget {
#override
_MyAnimationAppState createState() => _MyAnimationAppState();
}
class _MyAnimationAppState extends State<MyAnimationApp>
with TickerProviderStateMixin {
Animation<double> animation;
AnimationController _controller;
#override
void initState() {
super.initState();
_controller =
new AnimationController(vsync: this, duration: Duration(seconds: 3))
..repeat();
animation = new CurvedAnimation(parent: _controller, curve: Curves.linear);
}
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new Scaffold(
body: new Container(
child: new Center(
child: new ScaleTransition(
scale: animation,
child: new Container(
decoration: new BoxDecoration(
color: Color(0XFFEC3457), shape: BoxShape.circle),
height: 90.0,
width: 90.0,
),
),
),
),
),
);
}
}
SS is here
Thanks !!! Hoping for a reply ......
You'd need to add a double value to your code:
double _opacity = 1;
And a listener for your animation controller at the end of the initState
_controller.addListener(() {
setState(() {
_opacity = 1 - _controller.value;
});
});
And on your widget tree, you can add this:
ScaleTransition(
scale: animation,
child: Opacity(
opacity: _opacity,
child: Container(
Issue
I made an ellipse loading animation inside of flutter, but had to use a Timer on all three different controllers. (See example below...)
Is there any widget that can help chaining three different animations?
I tried using Interval Widget for multiple curves, but it did not provide a smooth transition. e.g. Interval(0.0, 0.3), Interval(0.3, 0.6), Interval(0.6, 0.9) for the animation curves.
Sample
Sample Code
import 'package:flutter/material.dart';
import 'package:flutter/animation.dart';
import 'dart:async';
void main() {
runApp(
new MaterialApp(
home: new Scaffold(
body: new CircleLoader(),
)
)
);
}
class CircleLoader extends StatefulWidget {
#override
_CircleLoaderState createState() => new _CircleLoaderState();
}
class _CircleLoaderState extends State<CircleLoader>
with TickerProviderStateMixin {
Animation<double> animation;
Animation<double> animation2;
Animation<double> animation3;
AnimationController controller;
AnimationController controller2;
AnimationController controller3;
int duration = 1000;
Widget circle = new Container(
height: 10.0,
width: 10.0,
decoration: new BoxDecoration(
shape: BoxShape.circle,
color: Colors.grey[300],
),
);
#override
initState() {
super.initState();
controller = new AnimationController(
duration: new Duration(milliseconds: duration), vsync: this);
controller2 = new AnimationController(
duration: new Duration(milliseconds: duration), vsync: this);
controller3 = new AnimationController(
duration: new Duration(milliseconds: duration), vsync: this);
final CurvedAnimation curve =
new CurvedAnimation(parent: controller, curve: Curves.easeInOut);
final CurvedAnimation curve2 =
new CurvedAnimation(parent: controller2, curve: Curves.easeInOut);
final CurvedAnimation curve3 =
new CurvedAnimation(parent: controller3, curve: Curves.easeInOut);
animation = new Tween(begin: 0.85, end: 1.5).animate(curve)
..addStatusListener((status) {
if (status == AnimationStatus.completed) {
controller.reverse();
} else if (status == AnimationStatus.dismissed) {
controller.forward();
}
});
animation2 = new Tween(begin: 0.85, end: 1.5).animate(curve2)
..addStatusListener((status) {
if (status == AnimationStatus.completed) {
controller2.reverse();
} else if (status == AnimationStatus.dismissed) {
controller2.forward();
}
});
animation3 = new Tween(begin: 0.85, end: 1.5).animate(curve3)
..addStatusListener((status) {
if (status == AnimationStatus.completed) {
controller3.reverse();
} else if (status == AnimationStatus.dismissed) {
controller3.forward();
}
});
controller.forward();
new Timer(const Duration(milliseconds: 300), () {
controller2.forward();
});
new Timer(const Duration(milliseconds: 600), () {
controller3.forward();
});
}
#override
Widget build(BuildContext context) {
return new Center(
child: new Container(
width: 100.0,
height: 50.0,
color: Colors.grey,
child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
new ScaleTransition(scale: animation, child: circle),
new ScaleTransition(scale: animation2, child: circle),
new ScaleTransition(scale: animation3, child: circle),
],
),
),
);
}
}
The trick is that you can create your own tween.
In short, what you want is a smooth curve that goes from 0 to 1 and then 1 to 0 smoothly. Which you can assimilate to a (sin(t * 2 * PI) + 1) / 2 where 0 <= t <= 1
And then delay that curve for each cicles.
class TestTween extends Tween<double> {
final double delay;
TestTween({double begin, double end, this.delay}) : super(begin: begin, end: end);
#override
double lerp(double t) {
return super.lerp((sin((t - delay) * 2 * PI) + 1) / 2);
}
}
Which allows to do
_controller = new AnimationController(
vsync: this,
duration: const Duration(seconds: 2),
)..repeat();
instead of having to add an animation listener and reverse the animation.
The end result is
class CircleLoader extends StatefulWidget {
#override
_CircleLoaderState createState() => new _CircleLoaderState();
}
class _CircleLoaderState extends State<CircleLoader>
with SingleTickerProviderStateMixin {
AnimationController _controller;
#override
initState() {
super.initState();
_controller = new AnimationController(
vsync: this,
duration: const Duration(seconds: 2),
)..repeat();
}
#override
dispose() {
_controller.dispose();
super.dispose();
}
buildCircle(double delay) {
return new ScaleTransition(
scale: new TestTween(begin: .85, end: 1.5, delay: delay)
.animate(_controller),
child: new Container(
height: 10.0,
width: 10.0,
decoration: new BoxDecoration(
shape: BoxShape.circle,
color: Colors.grey[300],
),
),
);
}
#override
Widget build(BuildContext context) {
return new Center(
child: new Container(
width: 100.0,
height: 50.0,
color: Colors.grey,
child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
buildCircle(.0),
buildCircle(.2),
buildCircle(.4),
],
),
),
);
}
}
class TestTween extends Tween<double> {
final double delay;
TestTween({double begin, double end, this.delay})
: super(begin: begin, end: end);
#override
double lerp(double t) {
return super.lerp((sin((t - delay) * 2 * PI) + 1) / 2);
}
}
I am making a game with flutter and found this problem.
While animation starts and ends correctly, everything in between that is a miss as you can see here.
It starts going down (Because of the first vector in the matrix) but even if I put first vector exactly the same then matrix animation won't do anything but just appear after the animation has ended in a new state.
Code for state (I removed everything I think is unnecessary but if you need more info I can paste whole class)
class _GameState extends State<GameWindow> with TickerProviderStateMixin{
static Matrix4 originalTransformation = new Matrix4.compose(
new vector.Vector3(1.0, 1.0, 1.0),
new vector.Quaternion.euler(0.0, 0.0, 0.0),
new vector.Vector3(1.0, 1.0, 1.0));
static Matrix4 animatedTransformation = new Matrix4.compose(
new vector.Vector3(5.0, 260.0, 1.0),
new vector.Quaternion.euler(0.0, 1.0, -0.7),
new vector.Vector3(0.6, 0.6, 0.6));
Matrix4 currentMatrix = originalTransformation;
#override
Widget build(BuildContext context){
return new Scaffold(
body: new Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.end,
children: [
new Container(
// Applying default transformation matrix
transform: currentMatrix,
child: _buildSquares(), // Builds GridView with custom squares
),
_getFooter(), // Footer
],
),
);
}
#override
void initState(){
// Init animation tween
animationTween = new Matrix4Tween(
begin: originalTransformation,
end: animatedTransformation
);
// Init animation controller
animationController = new AnimationController(
vsync: this,
duration: new Duration(milliseconds: 800),
)..addListener((){
this.setState((){
currentMatrix = animationTween.evaluate(animationController);
});
});
}
_clickListener(){
// Trigger animation
animationController.forward(from: 0.0);
if(_squares[_currentSquareIndex].state.isClicked()){
_increaseScore();
}else{
_showGameOver();
}
}
}
So after on click animation obviously starts and a matrix is changing but not in a smooth way. Any ideas why? Did I miss something?
Working example with one square:
import 'package:flutter/material.dart';
import 'package:vector_math/vector_math_64.dart' as vector;
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin{
static Matrix4 originalTransformation = new Matrix4.compose(
new vector.Vector3(1.0, 1.0, 1.0),
new vector.Quaternion.euler(0.0, 0.0, 0.0),
new vector.Vector3(1.0, 1.0, 1.0));
static Matrix4 animatedTransformation = new Matrix4.compose(
new vector.Vector3(5.0, 260.0, 1.0),
new vector.Quaternion.euler(0.0, 1.0, -0.7),
new vector.Vector3(0.6, 0.6, 0.6));
Matrix4 currentMatrix = originalTransformation;
AnimationController animationController;
Matrix4Tween animationTween;
#override
Widget build(BuildContext context) {
return new Scaffold(
resizeToAvoidBottomPadding: true,
body: new Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: [
new AnimatedBuilder(
// Pass animation controller
animation: animationController,
builder: (BuildContext context, Widget child) => new Container(
// Apply transformation
transform: animationTween.evaluate(animationController),
child: child,
),
// Passing child argument
child: new GestureDetector(
onTap: _onClick,
child: new Container(
height: 400.0,
width: 400.0,
color: Colors.red,
),
),
),
],
),
);
}
_onClick() {
// Start animation
animationController.forward(from: 0.0);
return;
}
#override
void initState() {
super.initState();
// Init tween for matrix
animationTween = new Matrix4Tween(
begin: originalTransformation,
end: animatedTransformation
);
// Init animation controller
animationController = new AnimationController(
vsync: this,
duration: new Duration(milliseconds: 800),
);
}
}
Edit: I tried removing listener and passing animation to AnimatedBuilder like this but that did not work either.
Edit 2: Added working example of this with one square.
Pass your Animation to an AnimatedBuilder instead of calling addListener. This reduces the amount of private state you're carrying around and allows you to use the result of _buildSquares() as the child argument so you aren't recomputing everything on every frame when the animation ticks. It should improve animation smoothness.
Edit: Added a working code sample below.
Edit 2: It sounds like you're running into a bug that I'm not experiencing, so please file an issue.
Edit 3: It sounds like the issue has been fixed already on master.
import 'package:flutter/material.dart';
import 'package:vector_math/vector_math_64.dart' as vector;
class HomeScreen extends StatefulWidget {
HomeScreenState createState() => new HomeScreenState();
}
class HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
AnimationController _controller;
#override initState() {
_controller = new AnimationController(
vsync: this, duration: const Duration(milliseconds: 300));
}
static Matrix4 originalTransformation = new Matrix4.compose(
new vector.Vector3(1.0, 1.0, 1.0),
new vector.Quaternion.euler(0.0, 0.0, 0.0),
new vector.Vector3(1.0, 1.0, 1.0));
static Matrix4 animatedTransformation = new Matrix4.compose(
new vector.Vector3(5.0, 260.0, 1.0),
new vector.Quaternion.euler(0.0, 1.0, -0.7),
new vector.Vector3(0.6, 0.6, 0.6));
Widget build(BuildContext context) {
return new Scaffold(
floatingActionButton: new FloatingActionButton(
child: new Icon(Icons.play_arrow),
onPressed: () => _controller.forward(from: 0.0),
),
body: new Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
children: [
new AnimatedBuilder(
builder: (BuildContext context, Widget child) {
return new Container(
color: Colors.red,
width: 100.0,
height: 100.0,
transform: new Matrix4Tween(
begin: originalTransformation,
end: animatedTransformation
).evaluate(_controller)
);
},
animation: _controller,
),
],
),
);
}
}
class ExampleApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
theme: new ThemeData(
primarySwatch: Colors.teal,
),
home: new HomeScreen(),
debugShowCheckedModeBanner: false,
);
}
}
void main() {
runApp(new ExampleApp());
}