My Animation involving a GravitySimulation won't animate - flutter

The container widget which is being animated stays at the starting point of the animation ie. the animation doesn't start, but if a single widget is used instead of a list of widgets like the particles list I made then the animation works fine.
Why is this so and how do I fix it? Thanks in advance!
Here's my code:
import 'package:flutter/material.dart';
import 'package:flutter/animation.dart';
import 'package:flutter/physics.dart';
import 'dart:math';
void main() => runApp(PhysicsAnimation());
class PhysicsAnimation extends StatefulWidget {
_PhysicsAnimation createState() => _PhysicsAnimation();
}
class _PhysicsAnimation extends State<PhysicsAnimation>
with TickerProviderStateMixin{
AnimationController controller;
GravitySimulation simulation;
List<Widget> particles=[];
bool isLoad=true;
Random random=new Random();
#override
void initState() {
super.initState();
simulation = GravitySimulation(
100, // acceleration
0.0, // starting point
2000.0, // end point
5, // starting velocity
);
controller =
AnimationController(vsync: this, upperBound: 800)
..addListener(() {
setState(() {});
});
controller.animateWith(simulation);
}
#override
Widget build(BuildContext context) {
if (isLoad) {
for(int i=0;i<20;i++){
particles.add(Positioned(
left: 50+random.nextDouble()*100,
top: controller.value,
height: 10,
width: 10,
child: Container(
color: Colors.redAccent,
)));}
}
isLoad=false;
return MaterialApp(
home: Stack(
children:
particles
),
);
}
#override
void dispose() {
controller.dispose();
super.dispose();
}
}

I know it's been awhile, but did you managed to solve this? I think you need to reset isLoad in your setState and clear the particles list before adding more particles.
controller = AnimationController(vsync: this, upperBound: 800)
..addListener(() {
setState(() {
**isLoad = true;**
});
});
...
Widget build(BuildContext context) {
if (isLoad) {
**particles.clear();**
for (int i = 0; i < 5; i++) {
particles.add(Positioned(
left: 50 + random.nextDouble() * 500,
top: controller.value,
height: 10,
width: 10,
child: Container(
color: Colors.redAccent,
)));
}
}
isLoad = false;
return Stack(children: particles);
}

Related

AutoScroll to a offset in interactive viewer in flutter

With the Interactive viewer documentation i came to know that we can autoscroll to a particular position in Interactive viewer with toScene method.But i tried but everything in vain.How to autoscroll to an offset given the positions in interactive viewer
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
class MyPlanet extends StatefulWidget {
#override
_MyPlanetState createState() => _MyPlanetState();
}
class _MyPlanetState extends State<MyPlanet> {
final TransformationController transformationController =
TransformationController();
#override
void initState() {
SchedulerBinding.instance.addPostFrameCallback((_) async {
Timer(Duration(seconds: 5), () {
setState(() {
transformationController.value = Matrix4.identity()
..translate(800, 0.0);
transformationController.toScene(Offset(800, 0.0));
});
});
});
super.initState();
}
#override
Widget build(BuildContext context) {
return InteractiveViewer(
minScale: 0.5,
maxScale: 1,
constrained: false,
transformationController: transformationController,
child: Container(
height: 896,
width: 2000,
child: Image.asset(
'assets/images/rain_forest.png',
fit: BoxFit.cover,
height: double.infinity,
),
),
);
}
}
Thanks #pskink.
Autoscroll in interactive viewer can be achieved by using TransformationController and matrix4
#override
void initState() {
TransformationController transformationController =
TransformationController();
transformationController.value = Matrix4.identity()
..translate(-200.0, 0.0); // translate(x,y);
}
Above solution not work for me - it move whole image on the screen, not only set needed position. I resolve issue with this example https://api.flutter.dev/flutter/widgets/InteractiveViewer/transformationController.html
Define end point for scroll for example by link:
Matrix4 scrollEnd = Matrix4.identity();
scrollEnd.setEntry(1, 3, -700); // y = 700
_controllerReset.reset();
_animationReset = Matrix4Tween(
begin: Matrix4.identity(),
end: scrollEnd,
).animate(_controllerReset);
_animationReset!.addListener(_onAnimateReset);
_controllerReset.forward();

How do you tell if scrolling is not needed in a SingleChildScrollView when the screen is first built?

I have a SingleChildScrollView. Sometimes its child is longer than the screen, in which case the SingleChildScrollView lets you scroll. But also sometimes its child is shorter than the screen, in which case no scrolling is needed.
I am trying to add an arrow to the bottom of the screen which hints to the user that they can/should scroll down to see the rest of the contents. I successfully implemented this except in the case where the child of the SingleChildScrollView is shorter than the screen. In this case no scrolling is needed, so I would like to not show the arrow at all.
I've tried making a listener to do this, but the listener isn't activated until you start scrolling, and in this case you can't scroll.
I've also tried accessing the properties of the _scrollController in the ternary operator which shows the arrow, but an exception is thrown: ScrollController not attached to any scroll views.
Here is a complete sample app showing what I'm doing, so you can just copy and paste it if you want to see it run. I replaced all of the contents with a Column of Text widgets for simplicity:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) =>
MaterialApp(home: Scaffold(body: MyScreen()));
}
class MyScreen extends StatefulWidget {
#override
_MyScreenState createState() => _MyScreenState();
}
class _MyScreenState extends State<MyScreen> {
ScrollController _scrollController = ScrollController();
bool atBottom = false;
#override
void initState() {
super.initState();
// Activated when you get to the bottom:
_scrollController.addListener(() {
if (_scrollController.position.extentAfter == 0) {
setState(() {
atBottom = true;
});
}
});
// Activated as soon as you start scrolling back up after getting to the bottom:
_scrollController.addListener(() {
if (_scrollController.position.extentAfter > 0 && atBottom) {
setState(() {
atBottom = false;
});
}
});
// I want this to activate if you are at the top of the screen and there is
// no scrolling to do, i.e. the widget being displayed fits in the screen:
_scrollController.addListener(() {
if (_scrollController.offset == 0 &&
_scrollController.position.extentAfter == 0) {
setState(() {
atBottom = false;
});
}
});
}
#override
void dispose() {
_scrollController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Stack(
children: [
SingleChildScrollView(
controller: _scrollController,
scrollDirection: Axis.vertical,
child: Container(
width: MediaQuery.of(context).size.width,
child: Column(
children: [
for (int i = 0; i < 100; i++)
Text(
i.toString(),
),
],
),
),
),
atBottom
? Container()
: Positioned(
bottom: 10,
right: 10,
child: Container(
child: Icon(
Icons.arrow_circle_down,
),
),
),
],
);
}
}
Import the scheduler Flutter library:
import 'package:flutter/scheduler.dart';
Create a boolean flag inside the state object but outside of the build method to track whether build has been called yet:
bool buildCalledYet = false;
Add the following in the beginning of the build method:
if (!firstBuild) {
firstBuild = true;
SchedulerBinding.instance.addPostFrameCallback((_) {
setState(() {
atBottom = !(_scrollController.position.maxScrollExtent > 0);
});
});
}
(The boolean flag prevents this code from causing build to be called over and over again.)
Here's the complete code of the sample app implementing this solution:
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) =>
MaterialApp(home: Scaffold(body: MyScreen()));
}
class MyScreen extends StatefulWidget {
#override
_MyScreenState createState() => _MyScreenState();
}
class _MyScreenState extends State<MyScreen> {
ScrollController _scrollController = ScrollController();
bool atBottom = false;
// ======= new code =======
bool buildCalledYet = false;
// ========================
#override
void initState() {
super.initState();
_scrollController.addListener(() {
if (_scrollController.position.extentAfter == 0) {
setState(() {
atBottom = true;
});
}
});
_scrollController.addListener(() {
if (_scrollController.position.extentAfter > 0 && atBottom) {
setState(() {
atBottom = false;
});
}
});
// ======= The third listener is not needed. =======
}
#override
void dispose() {
_scrollController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
// =========================== new code ===========================
if (!buildCalledYet) {
buildCalledYet = true;
SchedulerBinding.instance.addPostFrameCallback((_) {
setState(() {
atBottom = !(_scrollController.position.maxScrollExtent > 0);
});
});
}
// ================================================================
return Stack(
children: [
SingleChildScrollView(
controller: _scrollController,
scrollDirection: Axis.vertical,
child: Container(
width: MediaQuery.of(context).size.width,
child: Column(
children: [
for (int i = 0; i < 100; i++)
Text(
i.toString(),
),
],
),
),
),
atBottom
? Container()
: Positioned(
bottom: 10,
right: 10,
child: Container(
child: Icon(
Icons.arrow_circle_down,
),
),
),
],
);
}
}
I found this solution on another Stack Overflow question: Determine Scroll Widget height

How to change color with certain second on timer?

Objective : I want to have different timing duration for each color
Blue = 2 seconds
Red = 5 seconds
Green = 1 seconds
Yellow = 8 seconds
I got the code from here How to render child without parent when using setState?,
import 'dart:async';
import 'package:flutter/material.dart';
import 'dart:math';
void main() {
runApp(MaterialApp(debugShowCheckedModeBanner: false, home: Sample()));
}
class Sample extends StatelessWidget {
#override
Widget build(BuildContext context) {
print("Parent Widget rebuild");
return SafeArea(
child: Column(
children: [
Container(
height: 50,
width: 50,
color: Colors.red,
),
SizedBox(height: 20),
ChangingColor(),
],
),
);
}
}
class ChangingColor extends StatefulWidget {
#override
_ChangingColorState createState() => _ChangingColorState();
}
class _ChangingColorState extends State<ChangingColor> {
Timer _timer;
Color _color;
List<Color> arrColors = [Colors.blue, Colors.red, Colors.green, Colors.yellow] ;
int _pos =0;
List<int> arrSeconds = [2,5,1,8]; //here I set array for seconds
#override
void initState() {
for (var e in arrSeconds) {
_timer = Timer.periodic(Duration(seconds: e), (Timer t) { //I want the array looping using different seconds
print("seconds: $e");
setState(() {
_pos = (_pos + 1) % arrColors.length;
});
});}
super.initState();
}
#override
Widget build(BuildContext context) {
print("Child Widget rebuild");
return Container(
height: 50,
width: 50,
color: arrColors[_pos],
);
}
#override
void dispose() {
_timer.cancel();
_timer = null;
super.dispose();
}
}
The problem is seems like the timer did not finish yet but the color keep changing. Should I use async?
Please try this:
import 'dart:async';
import 'package:flutter/material.dart';
import 'dart:math';
void main() {
runApp(MaterialApp(debugShowCheckedModeBanner: false, home: Sample()));
}
class Sample extends StatelessWidget {
#override
Widget build(BuildContext context) {
print("Parent Widget rebuild");
return SafeArea(
child: Column(
children: [
Container(
height: 50,
width: 50,
color: Colors.red,
),
SizedBox(height: 20),
ChangingColor(),
],
),
);
}
}
class ChangingColor extends StatefulWidget {
#override
_ChangingColorState createState() => _ChangingColorState();
}
class _ChangingColorState extends State<ChangingColor> {
Timer _timer;
Color _color;
List<Color> arrColors = [Colors.blue, Colors.red, Colors.green, Colors.yellow] ;
int _pos =0;
List<int> arrSeconds = [2,5,1,8]; //here I set array for seconds
#override
void initState() {
super.initState();
doSomething();
}
#override
Widget build(BuildContext context) {
print("Child Widget rebuild");
return Container(
height: 50,
width: 50,
color: arrColors[_pos],
);
}
#override
void dispose() {
_timer.cancel();
_timer = null;
super.dispose();
}
Future<void> doSomething() async {
var second=0;
var colorCount=1;
var count=0;
var totalList=[];
for(var e in arrSeconds){
count=count+e;
totalList.add(count);
}
print(totalList);
_timer = Timer.periodic(Duration(seconds: 1), (Timer t) {
if(totalList[totalList.length-1]==second){
_timer.cancel();
setState(() {
_pos=0;
});
doSomething();
}else{
if(totalList.contains(second)){
setState(() {
_pos =colorCount;
colorCount++;
print(second);
});
}
}
second++;
});
}
}

Flutter image animate bigger and smaller

I want to make a simple recurring animation to an image. It should keep animating until the user closes the screen. What I want it that the image slowly gets bigger and then smaller.
I've checked the animatedContainer, but that doesn't seem to do this dynamically. Here is the code I used with AnimatedContainer:
#override
void didChangeDependencies() {
increaseSize(widget.seconds);
super.didChangeDependencies();
}
void increaseSize(int toSize) {
for (i = 0; i > toSize; i++) {
setState(() {
_width += i;
_height += i;
});
}
}
#override
Widget build(BuildContext context) {
return AnimatedContainer(
// Use the properties stored in the State class.
width: _width,
height: _height,
decoration: BoxDecoration(
color: _color,
borderRadius: _borderRadius,
),
// Define how long the animation should take.
duration: Duration(seconds: 30),
// Provide an optional curve to make the animation feel smoother.
curve: Curves.easeInOutBack,
);
;
}
Has anyone come across this before, any help would be appreciated.
Many thanks
Try something like this:
It's an example of a container which grows and shrinks from 50 to 100 and back to 50.
class SizeWidget extends StatefulWidget {
final int seconds;
const SizeWidget({this.seconds = 3});
#override
_SizeWidgetState createState() => _SizeWidgetState();
}
class _SizeWidgetState extends State<SizeWidget> with TickerProviderStateMixin {
AnimationController _animationController;
Animation _sizeAnimation;
bool reverse = false;
#override
void initState() {
super.initState();
_animationController = AnimationController(
vsync: this, duration: Duration(seconds: widget.seconds))
..addStatusListener((status) {
if (status == AnimationStatus.completed) {
_animationController.repeat(reverse: !reverse);
reverse = !reverse;
}
});
_sizeAnimation =
Tween<double>(begin: 50.0, end: 100.0).animate(_animationController);
_animationController.forward();
}
#override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _sizeAnimation,
builder: (context, child) => Container(
width: _sizeAnimation.value,
height: _sizeAnimation.value,
color: Colors.red,
),
);
}
}
Another option would be to use a TweenSequence and tell the animation controller to repeat itself:
class SizeWidget extends StatefulWidget {
final int seconds;
const SizeWidget({this.seconds = 3});
#override
_SizeWidgetState createState() => _SizeWidgetState();
}
class _SizeWidgetState extends State<SizeWidget> with TickerProviderStateMixin {
AnimationController _animationController;
Animation _sizeAnimation;
#override
void initState() {
super.initState();
_animationController = AnimationController(
vsync: this, duration: Duration(seconds: widget.seconds));
_sizeAnimation = TweenSequence<double>(
[
TweenSequenceItem<double>(
tween: Tween<double>(
begin: 50,
end: 100,
),
weight: 50,
),
TweenSequenceItem<double>(
tween: Tween<double>(
begin: 100,
end: 50,
),
weight: 50,
),
],
).animate(_animationController);
_animationController.repeat(reverse: true);
}
#override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _sizeAnimation,
builder: (context, child) => Container(
width: _sizeAnimation.value,
height: _sizeAnimation.value,
color: Colors.red,
),
);
}
}

Animate a color periodically in flutter

I have a container widget that I try to animate from Colors.blue to Colors.blueGrey and back periodically every 2 seconds.
How can I most easily tackle this in Flutter?
You can use an infinite while loop, don't think this is the best way of doing this, but it gets the job done.
I have a Color Changing class
class ColorChanger extends StatefulWidget {
#override
_ColorChangerState createState() => _ColorChangerState();
}
class _ColorChangerState extends State<ColorChanger>
with SingleTickerProviderStateMixin {
AnimationController _animationController;
Animation _colorTween;
#override
void initState() {
_animationController = AnimationController(
vsync: this, duration: Duration(milliseconds: 1999));
_colorTween = ColorTween(begin: Colors.blue, end: Colors.blueGrey)
.animate(_animationController);
changeColors();
super.initState();
}
Future changeColors() async {
while (true) {
await new Future.delayed(const Duration(seconds: 2), () {
if (_animationController.status == AnimationStatus.completed) {
_animationController.reverse();
} else {
_animationController.forward();
}
});
}
}
#override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _colorTween,
builder: (context, child) => Container(
color: _colorTween.value,
),
);
}
}
This is a rough example, but should lead you in the right direction.
Please see ColorTween Class
I would suggest using the AnimatedContainer. This widget allows you to build it with a particular atribute like color and when you rebuild it with a different value it performs linear interpolation between those values.
#override
Widget build(BuildContext context) {
return AnimatedContainer(
width: 100,
height: 100,
duration: _animationDuration,
color: _color,
);
}
Then you just have to rebuild the widget with a different color:
void _changeColor() {
final newColor = _color == Colors.blue ? Colors.blueGrey : Colors.blue;
setState(() {
_color = newColor;
});
}
The make it periodically I would use a timer class:
_timer = Timer.periodic(_animationDuration, (timer) => _changeColor());
The whole code:
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
class AnimationTest extends StatefulWidget {
#override
_AnimationTestState createState() => _AnimationTestState();
}
class _AnimationTestState extends State<AnimationTest> {
final _animationDuration = Duration(seconds: 2);
Timer _timer;
Color _color;
#override
void initState() {
super.initState();
_timer = Timer.periodic(_animationDuration, (timer) => _changeColor());
_color = Colors.blue;
}
void _changeColor() {
final newColor = _color == Colors.blue ? Colors.blueGrey : Colors.blue;
setState(() {
_color = newColor;
});
}
#override
Widget build(BuildContext context) {
return AnimatedContainer(
width: 100,
height: 100,
duration: _animationDuration,
color: _color,
);
}
#override
void dispose() {
super.dispose();
_timer.cancel();
}
}