How to add some delay between AnimationController.repeat() in Flutter - flutter

I need to add some delay between each iteration of animation gets call to repeat. Something like the following image.
I tried to do it by passing value to the period parameter of the repeat method but it was not what I expected.
_controller = AnimationController(vsync: this, duration: widget.period)
..addStatusListener((AnimationStatus status) {
if (status != AnimationStatus.completed) {
return;
}
_count++;
if (widget.loop <= 0) {
//_controller.repeat(period: Duration(microseconds: 5000));
_controller.repeat();
} else if (_count < widget.loop) {
_controller.forward(from: 0.0);
}
});
I've also tried to add Tween with the animation. That didn't help either. Can you help me clarify where I went wrong?
AnimatedBuilder(
animation: Tween<double>(begin: 0.0, end: 1.0).animate(
CurvedAnimation(
parent: _controller,
curve: Interval(0.5, 1.0)
),
),
child: widget.child,
builder: (BuildContext context, Widget child) => _Shiner(
child: child,
direction: widget.direction,
gradient: widget.gradient,
percent: _controller.value,
enabled: widget.enabled,
),
);

Thanks to #pskink Now it's working as I expected. All you have to do is repeat the controller yourself instead of trying to add delay to controller.repeat()
_controller.addStatusListener((status) {
if(status == AnimationStatus.completed){
Future.delayed(Duration(milliseconds: 5000),(){
if(!mounted){
_controller.forward(from: 0.0);
}
});
}
}

Related

Flutter - how use "non-linear" animation with LinearProgressIndicator?

I using LinearProgressIndicator in my app. I need use "non-linear" animation with LinearProgressIndicator. I need the indicator to move unevenly (a little slower a little faster).
This my current code:
controller = AnimationController(
vsync: this,
upperBound: 1,
animationBehavior: AnimationBehavior.preserve,
duration: Duration(seconds: widget.duration),
);
....
controller.addListener(() {
setState(() {
.....
});
});
....
LinearProgressIndicator(value: controller.value);
this works fine but is "linear"
I tried:
TweenAnimationBuilder<double>(
tween: Tween<double>(begin: 0.0, end: 2),
duration: const Duration(milliseconds: 3500),
builder: (context, value, _) => LinearProgressIndicator(
value: value,
),)
but it doesn't give "non-linear" animation. how do i get "non-linear" animation for LinearProgressIndicator ? is it possible?

flutter : how to fix The method '[]' can't be unconditionally invoked because the receiver can be 'null'

I'm trying to fix errors on my project after migration to null safety . i have error on this line :
opacity: animation["opacity"],
child: Transform.translate(
offset: Offset(0, animation["translateY"] ), child: child),
error : The method '[]' can't be unconditionally invoked because the receiver can be 'null'
My code :
class FadeAnimation extends StatelessWidget {
final double delay;
final Widget child;
FadeAnimation(this.delay, this.child);
#override
Widget build(BuildContext context) {
final tween = MultiTrackTween([
Track("opacity")
.add(Duration(milliseconds: 500), Tween(begin: 0.0, end: 1.0)),
Track("translateY").add(
Duration(milliseconds: 500), Tween(begin: -130.0, end: 0.0),
curve: Curves.easeOut)
]);
return ControlledAnimation(
delay: Duration(milliseconds: (500 * delay).round()),
duration: tween.duration,
tween: tween,
child: child,
builderWithChild: (context, child, animation) => Opacity(
opacity: animation["opacity"],
child: Transform.translate(
offset: Offset(0, animation["translateY"] ), child: child),
),
);
}
}
How I can fix it ?
Thanks in advance
It means your object animation might be null (I guess its type is Map?).
So you cannot do animation['yourKey'] directly as null['yourKey'] is not valid.
You have several solutions:
Try to change the type of animation to Map if possible.
If animation has to be Map? but you are sure it won't be null when you use it, you can do
animation!['yourKey']
If it can be null then you can do:
animation?['yourKey'], // If it is okay to use a `null` value here
animation?['yourKey'] ?? defaultValue, // If you need to use a non-null value
For example, for you opacity you can to:
opacity: animation?["opacity"] ?? 0,
if your using nullsafety
final double? delay;
final Widget? child;
then for calling them will be end at !
e.g delay! or child!

How to spin widget form one to another angle over Y-axis in flutter?

I want to spin widget from 270 degreee(intially) to 360 degreee. I tried AnimationController with AnimatedBuilder with Transform, but not able to understand how to build and what lowerbound and uppperbound parameter do.
Here is sample
Inside initState
_controller = AnimationController(
value: 50,
animationBehavior: AnimationBehavior.normal,
duration: const Duration(milliseconds: 800),
vsync: this)
..addListener(() {
print(rotate.value.toString());
setState(() {});
});
rotate = Tween<double>(begin: 0, end: 90).animate(_controller);
Animated Builder is
AnimatedBuilder(
animation: rotate,
builder: (context, child) {
return Transform(
origin: Offset(100,100),
child: getChild(context, percent),
transform: Matrix4.identity()..rotateY(rotate.value/100),
);
},
)
It always rotate 360 degree n number of times. I am not able to understand how it working?

Flutter Scrollcontroller maxScrollExtent doesn't go till the end of list

it stops before last element in list. Is there a way to go till the end container of the last element
controller
..animateTo(
controller.position.maxScrollExtent,
curve: Curves.easeIn,
duration: const Duration(milliseconds: 300),
);
});```
Using Without Animation Solved the Problem
Timer(Duration(milliseconds: 500), () {
controller.jumpTo(controller.position.maxScrollExtent);
});
I think this is the best function to scroll to bottom with a good animation
scrollToBottom() {
if (_chatController.hasClients) {
Future.delayed(const Duration(milliseconds: 500)).then((value) {
_chatController.animateTo(
_chatController.position.maxScrollExtent,
duration: const Duration(milliseconds: 200),
curve: Curves.fastOutSlowIn,
);
});
}
}
In my case the problem was that the elements were not equal in size and the size was not known until rendered. So, the ListView scrolled to its initial estimated maxScrollExtent but while scrolling the elements were rendered and the new maxScrollExtent was bigger. I hacked it by giving it a much bigger value initially:
attachmentsScrollController.animateTo(
attachmentsScrollController.position.maxScrollExtent * 2,
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut);
I had same problem with CustomeScrollView widget, and I solved with this.
CustomScrollView(
controller: Get.find<ChatController>(tag: 'chatDetail')
.scrollController
.value,
reverse: true,
keyboardDismissBehavior:
ScrollViewKeyboardDismissBehavior.onDrag,
physics: ClampingScrollPhysics(),
cacheExtent: 99999, // <-- here !!! ADD THIS CODE
slivers: [
ChatList(
isQuestion: isQuestion,
postCreatedAt: postCreatedAt,
title: title,
),
],
),
my scroll function was normal as we talked before.
void chatScrollUp() async {
await scrollController.value.animateTo(
scrollController.value.position.maxScrollExtent,
duration: Duration(
milliseconds:
(scrollController.value.position.maxScrollExtent / 2).round()),
curve: Curves.easeOutCirc);}
What worked with me is this:
void scrollAnimateToEnd(ScrollController controller) {
Future.delayed(const Duration(milliseconds: 400)).then((_) {
try {
controller
.animateTo(
controller.position.maxScrollExtent,
duration: const Duration(seconds: 1),
curve: Curves.fastOutSlowIn,
)
.then((value) {
controller.animateTo(
controller.position.maxScrollExtent,
duration: const Duration(milliseconds: 200),
curve: Curves.easeInOut,
);
});
} catch (e) {
print('error on scroll $e');
}
});
}

How to restart an animation stopped before completion but with different parameters

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.
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.