I'm new to flutter I have a search field and upon clicking, it should expand to another input field. I have done it but the widget gets re-created upon clicking the search filed.
As you can see, I want these fields to open up nicely with animation so that it looks good. Do you have any suggestions?
You can wrap the second TextField in a AnimatedContainer (or in a SizeTransition, but you will have to use a AnimationController).
If you use a AnimatedContainer you can simply change the height of the widget by a bool and the widget will animate itself. Eg:
AnimatedContainer(
width: MediaQuery.of(context).size.width,
height: showSecondField ? 40 : 0,
child: ...
)
or with the SizeTransition:
class YourWidget extends State<...> with SingleTickerProviderStateMixin {
AnimationController _controller;
Animation<double> _anim;
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 200),
);
final CurvedAnimation curve = CurvedAnimation(parent: _controller, curve: Curves.fastOutSlowIn);
_anim = Tween<double>(begin: 0.0, end: 1.0).animate(curve);
}
#override
Widget build(BuildContext context) {
return SizeTransition(
sizeFactor: _anim,
child: TextField()
);
}
}
And you can open or close with: _controller.forward() and _controller.reverse()
Related
I am trying to animate color using the AnimatedBuilder widget in Flutter.
I can use this builder widget to animate scale, translate, rotate by returning a Transform object. But I am lost to as how I can do this to animate color.
Note, I can animate color using the AnimatedContainer widget but I want to be able to do more complex animation using Intervals (e.g. in this case, fade in a color, wait a period of time, then fade back out).
Is this possible?
Thanks!
class _AnimatedColorState extends State<AnimatedColor>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<Color?> _animFadeIn, _animFadeOut;
#override
void initState() {
_controller = AnimationController(
duration: Duration(milliseconds: 1000),
vsync: this,
);
_animFadeIn = ColorTween(begin: Colors.pinkAccent, end: Colors.green)
.animate(CurvedAnimation(
parent: _controller,
curve: Interval(0, 0.20, curve: Curves.fastOutSlowIn)));
_animFadeOut = ColorTween(begin: Colors.green, end: Colors.pinkAccent)
.animate(CurvedAnimation(
parent: _controller,
curve: Interval(0.80, 1.0, curve: Curves.fastOutSlowIn)));
_controller.forward();
super.initState();
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _controller,
builder: (context, child) {
return Container(
height: 300,
width: 100,
decoration: BoxDecoration(
color: // animate color here?
));
});
}
}
You can use the .lerp() method on Color to specify two colors and a value from your animationController.
https://api.flutter.dev/flutter/dart-ui/Color/lerp.html
I am trying to do a glow effect on the container. I am using streambuilder to build list items and when item price changes I want to show container glow effect. So far I have done this but the widget doesn't show the effect at all. didUpdateWidget doesn't show any update and when it does, all of the listview items animates themselves for infinite amount of time. Is there something I am missing?
AnimationController _animationController;
#override
void initState() {
super.initState();
_animationController = new AnimationController(
vsync: this, duration: new Duration(milliseconds: 500));
}
#override
void didUpdateWidget(CustomContainer oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.price != widget.price) {
print("changed");
_animationController.repeat(
reverse: true, period: Duration(milliseconds: 1000));
}
}
#override
void dispose() {
_animationController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _animationController,
builder: (context, _) {
return Container(
height:100,
width:double.infinity,
decoration: BoxDecoration(
color: Theme.of(context).canvasColor,
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
blurRadius: 4 * _animationController.value,
color: primaryColor.withOpacity(0.7),
),
],
),
);
});
AnimationController.repeat() will start running the animation and repeat it an infinite amount of times. This is why the items are animating themselves for infinitely. You would have to use .forward method instead:
_animationController = AnimationController(
duration: Duration(milliseconds: 500),
vsync: this);
_animationController.forward(); //tell the animation to start without repeating
From Flutter Docs:
AnimationController is a special Animation object that generates a new
value whenever the hardware is ready for a new frame. By default, an
AnimationController linearly produces the numbers from 0.0 to 1.0
during a given duration.
AnimationController derives from Animation, so it can be used
wherever an Animation object is needed. However, the
AnimationController has additional methods to control the animation.
For example, you start an animation with the .forward() method. The
generation of numbers is tied to the screen refresh, so typically 60
numbers are generated per second. After each number is generated, each
Animation object calls the attached Listener objects. To create a
custom display list for each child, see RepaintBoundary.
Here is the docs for AnimationController.forward(): https://api.flutter.dev/flutter/animation/AnimationController/forward.html
So I'm trying to add animation when adding item to gridview. the animation work, but had a litle problem. the way i insert the data is like a stack so last item inserted is the first item in list and every time an item added, the the animation of items that already in the list keep restarting
class _MainListItemState extends State<MainListItem>
with SingleTickerProviderStateMixin {
AnimationController animationController;
Animation<double> opacityAnimation;
#override
void initState() {
super.initState();
animationController = AnimationController(duration: const Duration(milliseconds: 500), vsync: this);
opacityAnimation = Tween<double>(begin: 0, end: 1).animate(animationController);
animationController.forward();
}
#override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: animationController,
builder: (BuildContext buiild, Widget mine) => Opacity(
opacity: opacityAnimation.value,
child: ItemWidget()
),
);
}
}
I want to animate the scale of a widget.
On tap down it should scale down and on tap up, scale up.
Problem is how can I use staggered animations on the same property?
There is an older question here on SO (Chaining seperate animations that work on the same properties) but it has no working answers.
Ive tried to adapt its solution but the problem is each animation listener is called even if the specified Interval has not been reached.
Ive added a condition which checks if _animationFuture is set but with this pattern you have to keep a value for any running animation you have in your widget which is a bit cumbersome.
Is there a "native" solution where you dont need a work around or are multiple animations on same value just not supported?
I do not want to reverse() the animation. Each tween must be able to have a custom curve, begin and end values.
Heres my widget:
class AnimatedIconButton extends StatefulWidget {
final Widget child;
final Function onPress;
AnimatedIconButton({#required this.child, this.onPress}): assert(child != null);
#override
_AnimatedIconButtonState createState() => _AnimatedIconButtonState();
}
class _AnimatedIconButtonState extends State<AnimatedIconButton> with SingleTickerProviderStateMixin {
AnimationController _animationController;
Animation<double> _animationIn;
Animation<double> _animationOut;
double _scale = 1;
TickerFuture _animationFuture;
Function get onPress => widget.onPress ?? () => null;
_tapDown(TapDownDetails details) {
print("_tapDown progress=${_animationController.value} scale=$_scale");
_animationFuture = _animationController.animateTo(0.5);
}
_tapUp(TapUpDetails details) {
assert(_animationFuture != null);
print("_tapUp progress=${_animationController.value} scale=$_scale");
_animationFuture.then((_) {
_animationFuture = null;
_animationController.animateTo(1);
});
}
#override
void initState() {
super.initState();
_animationController = AnimationController(value: 0, vsync: this, duration: const Duration(milliseconds: 1500), animationBehavior: AnimationBehavior.preserve);
_animationIn = Tween<double>(begin: 1.0, end: 0.9).animate(CurvedAnimation(
parent: _animationController,
curve: Interval(
0, 0.5, curve: Curves.easeOut,
),
))..addListener(() {
if (_animationFuture == null) return;
print("_animationIn.value ${_animationOut.value}");
_scale = _animationIn.value;
});
_animationOut = Tween<double>(begin: 0.9, end: 1).animate(CurvedAnimation(
parent: _animationController,
curve: Interval(
0.5, 1, curve: Curves.elasticOut,
),
))..addListener(() {
if (_animationFuture != null) return;
print("_animationOut.value ${_animationOut.value}");
_scale = _animationOut.value;
});
}
#override
void dispose() {
_animationController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return GestureDetector(
onTapUp: _tapUp,
onTapDown: _tapDown,
child: AnimatedBuilder(
animation: _animationController,
child: widget.child,
builder: (_, child) {
return Transform.scale(
scale: _scale,
transformHitTests: false,
child: child,
);
},
),
);
}
}
You can use a TweenSequence to stagger multiple animations behind each other. Borrowing the example from the documentation and changing it a bit, here's a copy-paste widget that animates a blue container from 100x100 to 200x200, pauses for a bit, and animates it back:
class _StaggeredState extends State<Staggered>
with SingleTickerProviderStateMixin {
/// Controller managing the animation
late final AnimationController controller;
/// The aninmation of our TweenSequence
late final Animation<double> animation;
/// An Animatable consisting of a series of tweens
final Animatable<double> tweenSequence = TweenSequence<double>(
<TweenSequenceItem<double>>[
// Animate from .5 to 1 in the first 40/80th of this animation
TweenSequenceItem<double>(
tween: Tween<double>(begin: 100, end: 200)
.chain(CurveTween(curve: Curves.ease)),
weight: 40.0,
),
// Maintain still at 1.0 for 20/80th
TweenSequenceItem<double>(
tween: ConstantTween<double>(200),
weight: 20.0,
),
// Animate back from 1 to 0.5 for the last 40/80th
TweenSequenceItem<double>(
tween: Tween<double>(begin: 200, end: 100)
.chain(CurveTween(curve: Curves.ease)),
weight: 40.0,
),
],
);
#override
void initState() {
super.initState();
controller = AnimationController(
vsync: this,
duration: const Duration(seconds: 1),
);
// Animate our TweenSequence using our controller
animation = tweenSequence.animate(controller);
// Add a listener that calls [setState] so our widget rebuilds
// when the animation value changes.
controller.addListener(() => setState(() {}));
// Set the controller to repeat indefinitely
controller.repeat();
}
#override
void dispose() {
super.dispose();
controller.dispose();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: Container(
width: animation.value,
height: animation.value,
color: Colors.blue,
),
),
),
);
}
}
I'm trying to animate a panel coming up from the bottom of the screen. My setup is to have a column with two Containers, then animate the size of the lower Container to get a "sliding up" panel effect. The panel shouldn't be on top of the other so I can't use a stack.
Anyway, the problem is using the SizeTransition. I create an AnimationController to control the sizeFactor of the SizeTransition. The problem is that using any Curves in the animation makes it horribly laggy and I don't know why. Obviously, there must be a way to make this run smoothly but I'm at a loss. Here is a minimal example of what I mean:
class WelcomePage extends StatefulWidget {
#override
_WelcomePageState createState() => _WelcomePageState();
}
class _WelcomePageState extends State<WelcomePage>
with SingleTickerProviderStateMixin {
AnimationController _controller;
#override
void initState() {
_controller = AnimationController(
vsync: this,
duration: Duration(milliseconds: 500),
);
// Setting to curve: Curves.linear here makes it run smoothly but anything else brings us to choppy town
_controller.animateTo(210.0, curve: Curves.ease);
super.initState();
}
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Expanded(child: Container(color: Colors.blue)),
SizeTransition(
sizeFactor: _controller,
child: Container(
height: 200,
color: Colors.green,
))
],
);
}
}