FadeTransition() widget is animated only once in flutter? - animation

class pin extends StatefulWidget {
#override
_PinState createState() => _PinState();
}
class _PinState extends State<pin> with TickerProviderStateMixin {
AnimationController _controller;
Animation<double> _animation;
bool error = false;
#override
void initState() {
super.initState();
this._controller = AnimationController(
duration: const Duration(milliseconds: 1000), vsync: this);
this._animation =
Tween(begin: 0.0, end: 1.0).animate(CurvedAnimation(
parent: _controller,
curve: Curves.easeIn,
));
}
#override
Widget build(BuildContext context) {
if(this.error) {
this.error = false;
_controller.forward();
}
return Container(
child: if (this.error)
Container(
child: FadeTransition(
opacity: _animation,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: Image.asset("assets/images/sad_face.png"),
),
],
),
),
),
),
}
}
In the above code FadeTransition() widget is animated when the app is first launched.
and the visibility of FadeTransition() is toggled by the error variable.
but when next time the FadeTransition() widget is visible it is not animated?
what is missing, when toggling FadeTransition() the widget should be animated every time it appears!
the error variable is set from outside using Providers and wherever the error is changed the widget is rebuilt, so no need to use setState()

One thing I noticed is error is always false. There is no code to turn it to true and there are two places where it would be set to false. One of them dependent on if it is true (which it will never be since error = true doesn't exist)
That being said, if you want to make your animation run again, where ever you are toggling this property (usually in a button's onTap method) you have to call setState.
In the setState you can either use
controller.forward(from: 0);
// or
controller.reset(); // stops the animation if in progress
controller.forward();

Related

Flutter: Cant position a container over another using offset

So I have written this code which lets an animation play where i pressed. So if i press the screen there is a short animation playing where i pressed, if you press multiple times there are multiple animations on the screen for a short period. Each time I press the screen there is a container taking up place where the animation is played, but I cant stack a container over another, So if i press on the screen where there already is a container an animation won't be played. It seems that Positioned wont work if i press where there already is a container. How do I stack a container over another so that I can press on the same place and play multiple animation over another?
This is all the code responsible for that animation:
class HomePage extends StatefulWidget{
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage>
with SingleTickerProviderStateMixin {
final tappedPositions = <Offset>[];
AnimationController _animationController;
#override
void initState() {
_animationController = AnimationController(
vsync: this,
duration: const Duration(seconds: 2),
);
super.initState();
}
#override
Widget build(BuildContext context) {
return new Stack(
children: <Widget>[
new Column(
children: <Widget>[
new Expanded(
child: Container(
color: Colors.blue,
))]),
new GestureDetector(
onTapDown: (tabDetails) {
setState(() {
tappedPositions.add(tabDetails.localPosition);
Future.delayed(const Duration(seconds: 1), () {
tappedPositions.removeLast();
});
});
},
child: Stack(
children: <Widget>[
Container(
height: 370,
color: Colors.transparent
),
])
),
for (final position in tappedPositions)
Positioned(
top: position.dy-50,
left: position.dx-50,
child: MyAnimatedWidget(
animation: _animationController,
))]);
}
}
class MyAnimatedWidget extends StatelessWidget {
final Animation animation;
const MyAnimatedWidget({Key key, this.animation}) : super(key: key);
#override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: animation,
child: Stack(
children: <Widget>[
Container(
height: 100.0,
width: 100.0,
color: Colors.red
/* new FlareActor(
"assets/images/tryck2.flr",
animation:"tryck",*/
),
],
),
builder: (context, child) {
return Stack(
children: <Widget>[
new Container(alignment: Alignment.center,
child: child,)
],
);
},
);
}
}
PS (I have already asked this question and tried to make it work but have not succeeded, therefore I am asking again, I'm new to dart and programing in general)

Is there a way in Flutter to show user where to click if he click on wrong btn or widget?

I want to show user correct button or other place to click if he clik on wrong thing, like touch ripples effect or other way...
Im new to flutter and i dont know if is possible to have reference to widget and than call some effect on it...
Dont have code for at this time..
So i need "effect" when user click on "wrong" button right button to flash or some animation signal right button.
Thanks.
Use BottonTheme and animationController
when click this button you use another button's AnimationController
code snippet
ButtonTheme(
minWidth: 88.0 *
_animation
.value, //multiply the static width value with current animation.value value
height: 36.0 *
_animation
.value, //multiply the static height value with current animation.value value
child: RaisedButton(
child: Text('Tap this button to Animate Button on top'),
onPressed: () {
_animationController1
.forward();
},
),
)
full code
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
),
home: AnimatingButton(),
);
}
}
class AnimatingButton extends StatefulWidget {
#override
AnimatingButtonState createState() {
return new AnimatingButtonState();
}
}
class AnimatingButtonState extends State<AnimatingButton>
with TickerProviderStateMixin {
//Uses a Ticker Mixin for Animations
Animation<double> _animation;
AnimationController _animationController;
Animation<double> _animation1;
AnimationController _animationController1;
#override
void initState() {
super.initState();
_animationController = AnimationController(
vsync: this,
duration: Duration(
seconds:
2)); //specify the duration for the animation & include `this` for the vsyc
_animation = Tween<double>(begin: 1.0, end: 2.5).animate(
_animationController); //use Tween animation here, to animate between the values of 1.0 & 2.5.
_animation.addListener(() {
//here, a listener that rebuilds our widget tree when animation.value chnages
setState(() {});
});
_animation.addStatusListener((status) {
//AnimationStatus gives the current status of our animation, we want to go back to its previous state after completing its animation
if (status == AnimationStatus.completed) {
_animationController
.reverse(); //reverse the animation back here if its completed
}
});
_animationController1 = AnimationController(
vsync: this,
duration: Duration(
seconds:
2)); //specify the duration for the animation & include `this` for the vsyc
_animation1 = Tween<double>(begin: 1.0, end: 2.5).animate(
_animationController1); //use Tween animation here, to animate between the values of 1.0 & 2.5.
_animation1.addListener(() {
//here, a listener that rebuilds our widget tree when animation.value chnages
setState(() {});
});
_animation1.addStatusListener((status) {
//AnimationStatus gives the current status of our animation, we want to go back to its previous state after completing its animation
if (status == AnimationStatus.completed) {
_animationController1
.reverse(); //reverse the animation back here if its completed
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Animating a Button'),
),
body: Column(
children: <Widget>[
Center(
child: ButtonTheme(
minWidth: 88.0 *
_animation1
.value, //multiply the static width value with current animation.value value
height: 36.0 *
_animation1
.value, //multiply the static height value with current animation.value value
child: RaisedButton(
child: Text('Tap this button to Animate Button below '),
onPressed: () {
_animationController
.forward(); // tapping the button, starts the animation.
},
),
),
),
Center(
child: ButtonTheme(
minWidth: 88.0 *
_animation
.value, //multiply the static width value with current animation.value value
height: 36.0 *
_animation
.value, //multiply the static height value with current animation.value value
child: RaisedButton(
child: Text('Tap this button to Animate Button on top'),
onPressed: () {
_animationController1
.forward();
},
),
),
),
],
),
);
}
}

Flutter - Call animate function of a child from parent, or from another child class

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);
}

How to animate widget after the app is launched

I want to animate my container height after launching the application (after the widget tree is rendered and shown on screen). For example, animate height from 86 to 210.
What I have tried:
class MyAppState extends State<HomePage> with TickerProviderStateMixin {
double appBarHeight = 86.0;
#override
void initState() {
super.initState();
WidgetsBinding.instance
.addPostFrameCallback((_) => animate());
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: <Widget>[
AnimatedContainer(
curve: Curves.fastOutSlowIn,
child: Container(
color: Colors.red,
),
height: appBarHeight,
duration: Duration(milliseconds: 400),
),
Align(
alignment: Alignment.bottomRight,
child: Padding(
padding: EdgeInsets.only(right: 16.0, bottom: 16.0),
child: FloatingActionButton(
child: Icon(Icons.brush),
onPressed: () {
animate();
},
),
))
],
),
);
}
void animate(){
setState(() {
if (appBarHeight == 210.0) appBarHeight = 86.0;
else appBarHeight = 210.0;
});
}
}
But it does not work, because widget is animated before appearing on screen. I see white screen while app is launching and then my widget appears on screen with a final height.
In Android for this purpose we can use addOnLayoutChangeListener().
Is there an analogue of addOnLayoutChangeListener() in Flatter?
As you mentioned, looks like it's an issue on Android, I tested on iOS and the animation runs at the beginning.
You can try this workaround :
_startAnimation(_) async {
await Future.delayed(Duration(milliseconds: 200));
animate();
}
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback(_startAnimation);
}

Scale Transition in Flutter -Loader Animation

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(

Resources