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()
),
);
}
}
Related
I have a list view that its item may vary each time. I want to make auto scroll for it.
I tried this code
scrollController.animateTo(
scrollController.position.maxScrollExtent,
duration: Duration(seconds: 10),
curve: Curves.easeOut);
It works perfectly for small list view but when the list view items are 100 or more it start moving so fast.
I also tried to make the duration longer when the list view have more items but it mess up
The issue was caused from the animation itself not the duration.
I solved it by increasing the duration and setting
curve: Curves.linear.
// Declaring the controller and the item size
ScrollController _scrollController;
final itemSize = 100.0;
// Initializing
#override
void initState() {
_scrollController = ScrollController();
_scrollController.addListener(_scrollListener);
super.initState();
}
// Your list widget (must not be nested lists)
ListView.builder(
controller: _scrollController,
itemCount: <Your list length>,
itemExtent: itemSize,
itemBuilder: (context, index) {
return ListTile(<your items>);
},
),
// With the listener and the itemSize, you can calculate which item
// is on screen using the provided callBack. Something like this:
void _scrollListener() {
setState(() {
var index = (_scrollController.offset / itemSize).round() + 1;
});
}
import 'dart:async';
import 'package:flutter/material.dart';
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
final _controller = ScrollController();
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
Timer(
Duration(seconds: 2),
() => _controller.animateTo(
_controller.position.maxScrollExtent,
duration: Duration(seconds: 2),
curve: Curves.fastOutSlowIn,
),
);
return Scaffold(
body: ListView.builder(itemBuilder:(context, index) {
return Text('hi');
},
controller: _controller,
)
);
}
}
I'm trying GetX for my new project and tried to use AnimationController which is inspired this comment. Everything works fine with default value for Tween -> blur & spread & AnimationController -> duration.
What I'm doing:
1: Created an widget with corresponding controller (controller is binded through initialBinding of GetMaterialApp).
GetMaterialApp(
...
initialBinding: AnimationBinding()
...
);
class AnimationBinding extends Bindings {
#override
void dependencies() {
Get.lazyPut<AnimationController>(
() => AnimationController(),
);
}
}
class CustomAnimatedWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return GetBuilder<LogoAnimationController>(
builder: (_) {
return Container(
width: 150,
height: 150,
child: Text('for demo only, originally its not text widget'),
...
remaining code that uses `_.animation.value`
...
),
);
},
);
}
}
class AnimationController extends GetxController with SingleGetTickerProviderMixin {
Animation animation;
AnimationController _animationController;
#override
void onInit() {
super.onInit();
_animationController = AnimationController(
vsync: this, duration: Duration(seconds: 2));
_animationController.repeat(reverse: true);
animation = Tween(begin: 2.0, end: 15.0)
.animate(_animationController)
..addListener(() => update());
}
#override
void onReady() {
super.onReady();
}
#override
void onClose() {
super.onClose();
_animationController.dispose();
}
}
2: Used this widget inside view.
child: CustomAnimatedWidget();
Till now, everything is working fine. But I want to update blur, spread & duration, with:
updating CustomAnimatedWidget:
final double blur;
final double spread;
final int duration;
CustomAnimatedWidget({this.blur, this.spread, this.duration});
...
builder: (_) => ...
...
initState: (s) {
_.blur.value = blur;
_.spread.value = spread;
_.duration.value = duration;
},
...
updating AnimationController:
Rx<double> blur = 2.0.obs;
Rx<double> spread = 15.0.obs;
Rx<int> duration = 2.obs;
and using/calling the CustomAnimationWidget using:
child: CustomAnimatedWidget(duration: 4, blur: 2, spread: 10);
but don't know how to use it with _animationController & _animation because they are called in onInit.
Please share your solutions, thanks.
I Extended GetxController with GetTickerProviderStateMixin and then make a AnimationController and CurvedAnimation by code below:
late final AnimationController _controller = AnimationController(
duration: const Duration(seconds: 2),
vsync: this,
)..repeat(reverse: false);
late final Animation<double> animation = CurvedAnimation(
parent: _controller,
curve: Curves.ease,
);
After that i just created object of my CustomGetxController like this final _customController = Get.put(CustomGetxController()); then i called it like this:
FadeTransition(
opacity: driverController.animation,
child:const Text('My Flashing Text')
),
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()
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 would like to rotate an Image indefinitely.
This container is one of the widget within the stack and would like this to be rotating continuously non stop.
final AnimationController animation = AnimationController(
duration: const Duration(milliseconds: 1800),
vsync: const NonStopVSync(),
)..repeat();
final Tween tween = Tween(begin: 0.0, end: math.pi);
var square = Container(
width: 100,
height: 100,
transform: Matrix4.identity(),
color: Colors.amber,
);
...
class Foo extends State<Bar> {
...
animation.addListener((){
square.transform = Matrix4.rotationZ(tween.evaluate(animation));
});
Widget build(BuildContext context) {
return Stack(
children: [
...
Center(
child: square
)
]
)
}
}
and I get this error: 'transform' can't be used as a setter because it's final. (assignment_to_final at [digital_clock] lib/digital_clock.dart:139)
How would I do what I'm trying to do?
Try something like this:
class InfiniteAnimation extends StatefulWidget {
final Widget child;
final int durationInSeconds;
InfiniteAnimation({#required this.child, this.durationInSeconds = 2,});
#override
_InfiniteAnimationState createState() => _InfiniteAnimationState();
}
class _InfiniteAnimationState extends State<InfiniteAnimation>
with SingleTickerProviderStateMixin {
AnimationController animationController;
Animation<double> animation;
#override
void initState() {
super.initState();
animationController = AnimationController(
vsync: this,
duration: Duration(seconds: widget.durationInSeconds),
);
animation = Tween<double>(
begin: 0,
end: 12.5664, // 2Radians (360 degrees)
).animate(animationController);
animationController.forward();
animation.addStatusListener((status) {
if (status == AnimationStatus.completed) {
animationController.repeat();
}
});
}
#override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: animationController,
builder: (context, child) => Transform.rotate(
angle: animation.value,
child: widget.child,
),
);
}
#override
void dispose() {
animationController?.dispose();
super.dispose();
}
}
You basically need to create a StatefulWidget that mixes in (with keyword) the SingleTickerProviderStateMixin, provide an AnimationController, start the animation, then when the animation completes, repeat.
The AnimationBuilder is a better way of telling the widget to update on every frame without having to listen to the animationController and call setState explicitly.
You can use it like this:
InfiniteAnimation(
durationInSeconds: 2, // this is the default value
child: Icon(
Icons.expand_more,
size: 50.0,
color: Colors.white,
),
)