Animation affect other animations in Flutter CustomPainter - flutter
So, i can't achieve two independents animation in customPainter. The animation of the invaders is affecting the animation of the spaceship.The animation of the invaders consist of a TweenSequence, that produces an effect of move and stop just like space invaders original game.
But the movement of the spaceShip should be continuos. The problem is that the space ship also is having the movement pattern of the invaders, eventhough the animation has a different Tween and a different controller.
The curious thing is that if I comment all the lines that draw the invaders( so the animation value is not being used) the spaceship has a continuos movement(the movement expected)
Just to clarify:
The animation controller and animation of the spaceship are: controllerAsync and animationAsync of SpaceInvadersMainView.dart
Here is the code:
SpaceInvadersMainView.dart
import 'package:croco/SpaceInvaders/InvadersPositionManager.dart';
import '../SpaceInvaders/InvadersConstruction.dart';
import 'package:flutter/material.dart';
import '../SpaceInvaders/InvadersAnimationManager.dart';
class SpaceInvadersMainView extends StatelessWidget {
const SpaceInvadersMainView({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Space Invaders',
theme: ThemeData(
scaffoldBackgroundColor: Colors.black,
fontFamily: 'Segoe UI',
primarySwatch: Colors.lightBlue,
),
home: SpaceCanvas(),
);
}
}
class SpaceCanvas extends StatefulWidget {
SpaceCanvas({Key? key}) : super(key: key);
#override
State<SpaceCanvas> createState() => _SpaceCanvasState();
}
class _SpaceCanvasState extends State<SpaceCanvas> with TickerProviderStateMixin {
late AnimationController controller;
double animationStateValue = 0;
String keyLabel = "";
late AnimationController controllerAsync;
late double animationStateValueAsync;
late var animation = TweenSequence<double>(InvadersAnimationManager.getTweenRightMovement(-600, 500, 24)).animate(controller);
late var animationAsync = Tween<double>(begin: -700, end: 700).animate(controllerAsync);
#override
void initState() {
super.initState();
controllerAsync = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 10000)
);
animationAsync
.addListener(() {
setState(() {
print(animationAsync.value);
});
});
controller = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 10000 )
);
animation
.addListener(() {
setState(() {
});
});
controller.forward();
controllerAsync.forward();
}
#override
void dispose() {
controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return RawKeyboardListener(
autofocus: true,
focusNode: FocusNode(),
onKey: (event) {
keyLabel = event.logicalKey.keyLabel;
},
child: Scaffold(
body: Center(
child: Column(
children: <Widget>[
Align(
alignment: Alignment.topCenter,
child: Container(
padding: const EdgeInsets.only(top: 20),
child: const Text(
'Welcome to space Invaders!',
style: TextStyle(
fontWeight: FontWeight.w300,
fontSize: 25,
color: Colors.white70
)
),
),
),
Stack(
children: <Widget>[
CustomPaint(
painter : InvadersPaint(animation, animationAsync)
),
],
)
])
)
),
);
}
}
class InvadersPaint extends CustomPainter {
Paint basicSquarePaint = Paint();
Path originPath = Path();
late Animation animation;
late double animationStateValue;
late Animation animationAsync;
InvadersPaint(this.animation, this.animationAsync) : super(repaint: animation);
#override
void paint(Canvas canvas, Size size) {
Path testPath = Path();
Paint paint = Paint();
paint.color = Colors.greenAccent;
testPath = InvadersConstruction.drawInvader(animationAsync.value, 670, "spaceShip");
canvas.drawPath(testPath, paint);
InvadersPositionManager.placeInvadersOnLine(canvas, animation.value + 6 + 2, 100, "squid", 58, Colors.purpleAccent);
InvadersPositionManager.placeInvadersOnLine(canvas, animation.value + 2, 144, "crab", 58, Colors.lightBlueAccent);
InvadersPositionManager.placeInvadersOnLine(canvas, animation.value + 2, 188, "crab", 58, Colors.lightBlueAccent);
InvadersPositionManager.placeInvadersOnLine(canvas, animation.value, 232, "octopus", 58, Colors.yellowAccent);
InvadersPositionManager.placeInvadersOnLine(canvas, animation.value, 276, "octopus", 58, Colors.yellowAccent);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
return false;
}
}
InvadersContruction.dart
import 'package:flutter/material.dart';
class InvadersConstruction {
static Path drawInvader(double originX, double originY, String typeOfInvader) {
var transitionPath = Path();
var pathList = <List<int>>[];
const List<List<int>> octopusMatrix = [
[1,1,0,0,0,0,0,0,0,0,1,1],
[0,0,1,1,0,1,1,0,1,1,0,0],
[0,0,0,1,1,0,0,1,1,0,0,0],
[1,1,1,1,1,1,1,1,1,1,1,1],
[1,1,1,0,0,1,1,0,0,1,1,1],
[1,1,1,1,1,1,1,1,1,1,1,1],
[0,1,1,1,1,1,1,1,1,1,1,0],
[0,0,0,0,1,1,1,1,0,0,0,0],
];
const List<List<int>> crabMatrix = [
[0,0,0,1,1,0,1,1,0,0,0],
[1,0,1,0,0,0,0,0,1,0,1],
[1,0,1,1,1,1,1,1,1,0,1],
[1,1,1,1,1,1,1,1,1,1,1],
[0,1,1,0,1,1,1,0,1,1,0],
[0,0,1,1,1,1,1,1,1,0,0],
[0,0,0,1,0,0,0,1,0,0,0],
[0,0,1,0,0,0,0,0,1,0,0]
];
const List<List<int>> squidMatrix = [
[1,0,1,0,0,1,0,1],
[0,1,0,1,1,0,1,0],
[0,0,1,0,0,1,0,0],
[1,1,1,1,1,1,1,1],
[1,1,0,1,1,0,1,1],
[0,1,1,1,1,1,1,0],
[0,0,1,1,1,1,0,0],
[0,0,0,1,1,0,0,0]
];
const List<List<int>> spaceShipMatrix = [
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
[0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0],
[0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0],
[0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0],
];
void drawSquare( int row, int isItSquare, int sqrPosition ) {
double y;
double x;
Path secondaryPath = Path();
y = originY + row * -4;
if (sqrPosition == 0) {
x = originX;
} else {
x = sqrPosition * 4 + originX;
}
if (isItSquare == 1) {
secondaryPath.moveTo(x, y);
secondaryPath.relativeLineTo(4, 0);
secondaryPath.relativeLineTo(0, -4);
secondaryPath.relativeLineTo(-4, 0);
secondaryPath.close();
transitionPath = Path.combine(PathOperation.union, transitionPath, secondaryPath);
transitionPath.fillType = PathFillType.evenOdd;
}
}
int counterRow = -1;
int counterCol = -1;
if(typeOfInvader == "octopus") {
pathList = octopusMatrix;
} else if(typeOfInvader == "crab") {
pathList = crabMatrix;
} else if(typeOfInvader == "squid") {
pathList = squidMatrix;
} else if(typeOfInvader == "spaceShip") {
pathList = spaceShipMatrix;
}
for ( var row in pathList) {
counterRow++;
// print("This is the counter of the row: $counterRow");
for (var sqr in row) {
counterCol++;
// print("This is the counter of the square position: $counterCol");
drawSquare(counterRow, sqr, counterCol);
if (counterCol == row.length -1) {
counterCol = -1;
}
}
}
return transitionPath;
}
}
InvadersAnimationManager.dart
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'dart:html';
class InvadersAnimationManager {
//This method maps the right direction movement(-x -> x) of invaders in a List of Tween Sequence Items, that it's
//going to be procesed by the TweenSequence constructor '../Views/SpaceInvadersMainView.dart' line: 37
static List<TweenSequenceItem<double>> getTweenRightMovement (double originX, double finalX, double pixelsToRight) {
double steps = 16;
var tweenSequence = <TweenSequenceItem<double>>[];
for(int i = 1; i <= steps; i++ ) {
var subCounter = i - 1;
var tweenItem = TweenSequenceItem<double>(
tween: Tween<double>(begin: originX + pixelsToRight * subCounter, end: originX + pixelsToRight * i),
weight: 100/ steps
);
tweenSequence.add(tweenItem);
}
return tweenSequence;
}
}
InvadersPositionManager.dart
import 'package:flutter/material.dart';
import 'InvadersConstruction.dart';
class InvadersPositionManager {
static void placeInvadersOnLine(Canvas canvas, double originX, double originY, String invader, double inBetweenPixels, Color color) {
int _rowElements = 12;
Path _internalInvader;
Paint _paint = Paint();
double _invaderLength = 48;
_paint.color = color;
for(int i = 1; i <= 12 + 1; i++) {
if(i == 1) {
_internalInvader = InvadersConstruction.drawInvader(originX, originY, invader);
} else {
_internalInvader = InvadersConstruction.drawInvader(originX + _invaderLength + i * inBetweenPixels, originY, invader);
canvas.drawPath(_internalInvader, _paint);
}
}
}
}
Related
Flutter moving point over image
I want to create a moving point that moves across an image. The picture is drawn on canvas. The point should approach the specified x and y coordinates.Image and point are generated. The point should move to the new specified position every 500ms by a timer.But it only moves when I move the mouse over buttons on the app. class five_og extends StatefulWidget { #override State createState() { return _five_og(); } } class _five_og extends State<five_og> { ui.Image? image; Timer? timer; void refreshImage() { if (i >= xList.length - 1) { i = 0; } ImagePainter(image!); i++; } #override void dispose() { super.dispose(); timer!.cancel(); } #override void initState() { super.initState(); loadImage('assets/images/Erdgeschoss.png'); timer = Timer.periodic( Duration(milliseconds: 500), (timer) { refreshImage(); }, ); } Future loadImage(String path) async { final data = await rootBundle.load(path); final bytes = data.buffer.asUint8List(); final image = await decodeImageFromList(bytes); setState(() { this.image = image; }); } #override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Test'), ), body: Center( child: image == null ? CircularProgressIndicator() : Container( height: 600, width: 600, child: FittedBox( child: SizedBox( width: image!.width.toDouble(), height: image!.width.toDouble(), child: CustomPaint( painter: ImagePainter(image!), ), ), ) ) ), ); } } class ImagePainter extends CustomPainter { final ui.Image image; const ImagePainter(this.image); #override void paint(Canvas canvas, Size size) { //final paint = Paint(); canvas.drawImage(image, Offset.zero, Paint()); final paint = Paint() ..color = Color.fromARGB(255, 15, 182, 253) ..style = PaintingStyle.fill; canvas.drawCircle(Offset(xList[i], yList[i]), 13, paint); } #override bool shouldRepaint(ImagePainter oldDelegate) { return false; } }
Flutter - AnimatedScale not animating when widget update
I have a rating widget with 5 stars. I can give a rating by dragging. I want to animate the star with the star rating so that the scale gets bigger and the star without the star rating gets smaller again. Therefore, I made sure that the star with the star rating is created using the AnimatedScale. However, as you can see in the attached gif, the size of the star changes but it is not animated. I gave the Duration value of the AnimatedScale property to 2 seconds, but it is being changed as soon as it is graded. How can the stars grow and shrink smoothly? Here is my code import 'package:flutter/material.dart'; typedef void RatingChangeCallback(double rating); class SmoothStarRating extends StatelessWidget { final int starCount; final double rating; final RatingChangeCallback onRatingChanged; final Color? color; final Color? borderColor; final double size; final double spacing; SmoothStarRating({ this.starCount = 5, this.spacing = 0.0, this.rating = 0.0, required this.onRatingChanged, this.color, this.borderColor, this.size = 25, }); Widget getIcon(int starIndex) { if (starIndex >= rating) { return Icon(Icons.star_border, color: borderColor, size: size); } else if (starIndex > rating - 0.5 && starIndex < rating) { return AnimatedScale( duration: Duration(seconds: 2), scale: 1.1, child: Icon(Icons.star_half, color: color, size: size), ); } else { return AnimatedScale( duration: Duration(seconds: 2), scale: 1.1, child: Icon(Icons.star, color: color, size: size), ); } } Widget buildStar(BuildContext context, int starIndex) { return GestureDetector( onTap: () { onRatingChanged(starIndex + 1.0); }, onHorizontalDragUpdate: (dragDetails) { RenderBox box = context.findRenderObject() as RenderBox; var _pos = box.globalToLocal(dragDetails.globalPosition); var newRating = _pos.dx / size; if (newRating > starCount) newRating = starCount.toDouble(); if (newRating < 0) newRating = 0.0; onRatingChanged(newRating); }, child: getIcon(starIndex), ); } #override Widget build(BuildContext context) { return Wrap( alignment: WrapAlignment.start, spacing: spacing, children: List.generate(starCount, (starIndex) => buildStar(context, starIndex)), ); } } + ADDITIONAL I tried implementing it using animation_controller, but the results are the same. import 'package:flutter/material.dart'; typedef void RatingChangeCallback(double rating); class SmoothStarRating extends StatefulWidget { final int starCount; final double rating; final RatingChangeCallback onRatingChanged; final Color? color; final Color? borderColor; final double size; final double spacing; SmoothStarRating({ this.starCount = 5, this.spacing = 0.0, this.rating = 0.0, required this.onRatingChanged, this.color, this.borderColor, this.size = 25, }); #override State<SmoothStarRating> createState() => _SmoothStarRatingState(); } class _SmoothStarRatingState extends State<SmoothStarRating> with TickerProviderStateMixin { late final AnimationController _controller = AnimationController( duration: const Duration(milliseconds: 500), vsync: this, )..forward(); late final Animation<double> _animation = Tween(begin: 1.0, end: 1.1).animate(_controller); #override void dispose() { _controller.dispose(); super.dispose(); } Widget getIcon(int starIndex) { if (starIndex >= widget.rating) { return Icon(Icons.star_border, color: widget.borderColor, size: widget.size); } else if (starIndex > widget.rating - 0.5 && starIndex < widget.rating) { return Icon(Icons.star_half, color: widget.color, size: widget.size); } else { return ScaleTransition( scale: _animation, child: Icon(Icons.star, color: widget.color, size: widget.size), ); } } Widget buildStar(BuildContext context, int starIndex) { return GestureDetector( onTap: () { widget.onRatingChanged(starIndex + 1.0); }, onHorizontalDragUpdate: (dragDetails) { RenderBox box = context.findRenderObject() as RenderBox; var _pos = box.globalToLocal(dragDetails.globalPosition); var newRating = _pos.dx / widget.size; if (newRating > widget.starCount) newRating = widget.starCount.toDouble(); if (newRating < 0) newRating = 0.0; widget.onRatingChanged(newRating); }, child: getIcon(starIndex), ); } #override Widget build(BuildContext context) { return Wrap( alignment: WrapAlignment.start, spacing: widget.spacing, children: List.generate(widget.starCount, (starIndex) => buildStar(context, starIndex)), ); } }
Flutter resize circle_wave widget
Here we have a very awesome implemented widget to have this circular wave now in this code i can't resize width and height of this widget. before posting problem i try to resolve that, but i can't GitHub Link: demo_circle_wave import 'dart:math' as math; import 'dart:ui'; import 'package:flutter/material.dart'; import 'common.dart'; class DemoCircleWave extends StatefulWidget { const DemoCircleWave({ Key key, }) : super(key: key); #override _DemoCircleWaveState createState() => _DemoCircleWaveState(); } class _DemoCircleWaveState extends State<DemoCircleWave> with TickerProviderStateMixin { static const colors = <Color>[ Color(0xFFFF2964), Color(0xFF32FF3A), Color(0xFF4255FF) ]; AnimationController controller; AnimationController addPointController; Animation<double> addPointAnimation; int _counter = 0; #override void initState() { super.initState(); controller = AnimationController( vsync: this, upperBound: 2, duration: const Duration(seconds: 10), )..repeat(); addPointController = AnimationController( vsync: this, duration: const Duration(milliseconds: 500), ); addPointAnimation = addPointController.drive(CurveTween(curve: Curves.ease)); } #override void dispose() { controller.dispose(); super.dispose(); } void _incrementCounter() { setState(() { _counter++; addPointController.forward(from: 0); }); } #override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Circle wave')), backgroundColor: Colors.black, body: Stack( children: [ for (int i = 0; i < 3; i++) Positioned.fill( child: TweenAnimationBuilder( tween: Tween<double>(begin: 0, end: 1), duration: const Duration(milliseconds: 500), curve: Curves.easeIn, builder: (_, double opacity, __) { return CustomPaint( painter: CircleWavePainter( controller, addPointAnimation, i, colors[i].withOpacity(opacity), _counter, ), ); }, ), ), CounterText(counter: _counter), ], ), floatingActionButton: FloatingActionButton( onPressed: _incrementCounter, tooltip: 'Increment', child: const Icon(Icons.add), ), ); } } class CircleWavePainter extends CustomPainter { CircleWavePainter( this.animation, this.addAnimation, this.index, this.color, this.count, ) : super(repaint: animation); final Animation<double> animation; final Animation<double> addAnimation; final int index; final Color color; final int count; static const halfPi = math.pi / 2; static const twoPi = math.pi * 2; final n = 7; #override void paint(Canvas canvas, Size size) { final t = animation.value; final halfWidth = size.width / 2; final halfHeight = size.height / 2; final q = index * halfPi; List<Offset> computeOffsets(int length) { final offsets = <Offset>[]; for (var i = 0; i < length; i++) { final th = i * twoPi / length; double os = map(math.cos(th - twoPi * t), -1, 1, 0, 1); os = 0.125 * math.pow(os, 2.75); final r = 165 * (1 + os * math.cos(n * th + 1.5 * twoPi * t + q)); offsets.add(Offset( r * math.sin(th) + halfWidth, -r * math.cos(th) + halfHeight)); } return offsets; } final offsets = computeOffsets(count); if (count > 1 && addAnimation.value < 1) { final t = addAnimation.value; final oldOffsets = computeOffsets(count - 1); for (var i = 0; i < count - 1; i++) { offsets[i] = Offset.lerp(oldOffsets[i], offsets[i], t); } offsets[count - 1] = Offset.lerp( oldOffsets[count - 2], offsets[count - 1], t, ); } final path = Path()..addPolygon(offsets, true); canvas.drawPath( path, Paint() ..blendMode = BlendMode.lighten ..color = color ..strokeWidth = 8 ..style = PaintingStyle.stroke, ); } #override bool shouldRepaint(covariant CustomPainter oldDelegate) { return false; } }
How to animate line draw in custom painter in Flutter?
I want to animate the line drawing in custom painter canvas. So far what I can do is create two circles at two points and then create a line between those two points. But I don't know how to animate the line as if it is going from one point to the other. I have tried something but I can't make it work. Please check the code and suggest me if you have any idea. import 'package:flutter/material.dart'; import 'dart:math' as math; class ProgressMonitorAnimation extends StatefulWidget { #override State<StatefulWidget> createState() => _ProgressMonitorAnimationState(); } class _ProgressMonitorAnimationState extends State<ProgressMonitorAnimation> with TickerProviderStateMixin { double _progress = 0.0; Animation<double> animation; #override void initState() { var controller = AnimationController(duration: Duration(milliseconds: 3000), vsync: this); animation = Tween(begin: 1.0, end: 0.0).animate(controller)..addListener(() { setState(() { _progress = animation.value; }); }); controller.forward(); super.initState(); } #override Widget build(BuildContext context) { return Transform( alignment: Alignment.center, transform: Matrix4.rotationX(math.pi), child: CustomPaint( foregroundPainter: ProgressPainter(_progress), ), ); } } class ProgressPainter extends CustomPainter { double _progress; ProgressPainter(this._progress); #override void paint(Canvas canvas, Size size) { final Paint circlePainter = Paint()..color = Colors.green; final Paint linePainter = Paint()..color = Colors.black..strokeWidth = 4..strokeCap = StrokeCap.round; canvas.drawCircle(Offset(0.0, 30.0 * 3), 10.0, circlePainter); canvas.drawCircle(Offset(15.0 * 2, 80.0 * 3), 10.0, circlePainter); canvas.drawLine(Offset(0.0 / (_progress * 10), 30.0 * 3), Offset((30.0 * 3) + (15.0) / (_progress * 15) * 2, (80.0 * 3) / (_progress * 15)), linePainter); } #override bool shouldRepaint(CustomPainter oldDelegate) => true; }
You can do as follows using flutter Custom Painter. import 'dart:async'; import 'dart:ui'; import 'package:flutter/material.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; class AnimatedPathPainter extends CustomPainter { final Animation<double> _animation; AnimatedPathPainter(this._animation) : super(repaint: _animation); Path _createAnyPath(Size size) { return Path() // ..moveTo(size.height / 4, size.height / 4) // ..lineTo(size.height, size.width / 2) // ..lineTo(size.height / 2, size.width) ..quadraticBezierTo(size.height / 2, 100, size.width, size.height); } #override void paint(Canvas canvas, Size size) { final animationPercent = this._animation.value; print("Painting + ${animationPercent} - ${size}"); final path = createAnimatedPath(_createAnyPath(size), animationPercent); final Paint paint = Paint(); paint.color = Colors.black; paint.style = PaintingStyle.stroke; paint.strokeWidth = 4.0; canvas.drawPath(path, paint); } #override bool shouldRepaint(CustomPainter oldDelegate) => true; } Path createAnimatedPath( Path originalPath, double animationPercent, ) { // ComputeMetrics can only be iterated once! final totalLength = originalPath .computeMetrics() .fold(0.0, (double prev, PathMetric metric) => prev + metric.length); final currentLength = totalLength * animationPercent; return extractPathUntilLength(originalPath, currentLength); } Path extractPathUntilLength( Path originalPath, double length, ) { var currentLength = 0.0; final path = new Path(); var metricsIterator = originalPath.computeMetrics().iterator; while (metricsIterator.moveNext()) { var metric = metricsIterator.current; var nextLength = currentLength + metric.length; final isLastSegment = nextLength > length; if (isLastSegment) { final remainingLength = length - currentLength; final pathSegment = metric.extractPath(0.0, remainingLength); path.addPath(pathSegment, Offset.zero); break; } else { // There might be a more efficient way of extracting an entire path final pathSegment = metric.extractPath(0.0, metric.length); path.addPath(pathSegment, Offset.zero); } currentLength = nextLength; } return path; } class AnimatedPathDemo extends StatefulWidget { #override _AnimatedPathDemoState createState() => _AnimatedPathDemoState(); } class _AnimatedPathDemoState extends State<AnimatedPathDemo> with SingleTickerProviderStateMixin { AnimationController _controller; Completer<GoogleMapController> _controllerMap = Completer(); static final CameraPosition _initialPosition = CameraPosition( // target: LatLng(12.947437, 77.685345), target: LatLng(7.8731, 80.7718), zoom: 8, ); void _startAnimation() { _controller.stop(); _controller.reset(); _controller.repeat( period: Duration(seconds: 2), ); } #override Widget build(BuildContext context) { return new Scaffold( appBar: new AppBar(title: const Text('Animated Paint')), body: Stack( children: <Widget>[ GoogleMap( // rotateGesturesEnabled: false, mapType: MapType.normal, compassEnabled: false, initialCameraPosition: _initialPosition, // polylines: _polylines, // markers: _markers, onMapCreated: (GoogleMapController controller) { // controller.setMapStyle(Utils.mapStyles); _controllerMap.complete(controller); }, ), SizedBox( height: 300, width: 300, child: new CustomPaint( painter: new AnimatedPathPainter(_controller), ), ), ], ), floatingActionButton: new FloatingActionButton( onPressed: _startAnimation, child: new Icon(Icons.play_arrow), ), ); } #override void initState() { super.initState(); _controller = new AnimationController( vsync: this, ); } #override void dispose() { _controller.dispose(); super.dispose(); } }
Update CustomPaint drawing
I have a Problem with the CustomPainter Widget. I want to draw a PieChart which works fine, then I added a Variable which draws the Chart to until it reached this angle. Now I want to animate it, I used the Future.delayed function and in there with setState I wanted to update the variable but that doesn't work unfortunately. I am developing for the web. Thanks for helping! import 'dart:async'; import 'package:flutter/material.dart'; import 'package:stats/data/listLanguages.dart'; import 'painter/pieChartPainter.dart'; class Chart extends StatefulWidget { ListLanguages listLanguages; Chart({ListLanguages listLanguages}) { if (listLanguages == null) { listLanguages = new ListLanguages(); } this.listLanguages = listLanguages; } #override _ChartState createState() => _ChartState(); } class _ChartState extends State<Chart> { #override Widget build(BuildContext context) { List angles = widget.listLanguages.calcCounts(); int angle = 0; Future.delayed(new Duration(seconds: 2), (){ setState(() { angle = 360; print("test"); }); }); return Column( children: [ Spacer(flex: 2), Row( children: [ Spacer(), CustomPaint( size: Size.square(400), painter: PieChartPainter( angles: angles, colors: new List() ..add(Colors.green) ..add(Colors.blue) ..add(Colors.brown) ..add(Colors.pink) ..add(Colors.orange) ..add(Colors.grey.shade700), angle: angle, ), ), Spacer(flex: 10), ], ), Spacer(flex: 3), ], ); } } import 'package:flutter/material.dart'; import 'package:vector_math/vector_math.dart' as vm; class PieChartPainter extends CustomPainter { List angles, colors; int angle; PieChartPainter( {#required List angles, #required List colors, int angle: 360}) { this.angles = angles; this.colors = colors; this.angle = angle; } #override void paint(Canvas canvas, Size size) { Paint p = new Paint(); double start = -90; double tmp = 0; for (int i = 0; i < angles.length; i++) { if (i < 5) { p.color = colors[i]; } else { p.color = colors[5]; } if (tmp + angles[i] < angle) { canvas.drawArc(Rect.fromLTRB(0, 0, size.width, size.height), vm.radians(start), vm.radians(angles[i]), true, p); start = start + angles[i]; tmp = tmp + angles[i]; } else { double x = angle - tmp; canvas.drawArc(Rect.fromLTRB(0, 0, size.width, size.height), vm.radians(start), vm.radians(x), true, p); return; } } } #override bool shouldRepaint(CustomPainter oldDelegate) { return true; } } this is the complete code I have to create the Pie Chart
You can copy paste run full code below In your case, to work with Future.delayed, you can move logic from build to initState and use addPostFrameCallback working demo change angle in 2, 4, 6 seconds and angle is 150, 250, 360 code snippet class _ChartState extends State<Chart> { int angle = 0; List angles; #override void initState() { angles = widget.listLanguages.calcCounts(); WidgetsBinding.instance.addPostFrameCallback((_) { Future.delayed(Duration(seconds: 2), () { setState(() { angle = 150; }); }); Future.delayed(Duration(seconds: 4), () { setState(() { angle = 250; }); }); Future.delayed(Duration(seconds: 6), () { setState(() { angle = 360; }); }); }); working demo full code import 'package:flutter/material.dart'; import 'package:vector_math/vector_math.dart' as vm; class ListLanguages { List calcCounts() { return [10.0, 20.0, 100.0, 150.0, 250.0, 300.0]; } } class Chart extends StatefulWidget { ListLanguages listLanguages; Chart({ListLanguages listLanguages}) { if (listLanguages == null) { listLanguages = ListLanguages(); } this.listLanguages = listLanguages; } #override _ChartState createState() => _ChartState(); } class _ChartState extends State<Chart> { int angle = 0; List angles; #override void initState() { angles = widget.listLanguages.calcCounts(); WidgetsBinding.instance.addPostFrameCallback((_) { Future.delayed(Duration(seconds: 2), () { print("delay"); setState(() { angle = 150; print("test"); }); }); Future.delayed(Duration(seconds: 4), () { print("delay"); setState(() { angle = 250; print("test"); }); }); Future.delayed(Duration(seconds: 6), () { print("delay"); setState(() { angle = 360; print("test"); }); }); }); super.initState(); } #override Widget build(BuildContext context) { return Column( children: [ Spacer(flex: 2), Row( children: [ Spacer(), CustomPaint( size: Size.square(400), painter: PieChartPainter( angles: angles, colors: List() ..add(Colors.green) ..add(Colors.blue) ..add(Colors.brown) ..add(Colors.pink) ..add(Colors.orange) ..add(Colors.grey.shade700), angle: angle, ), ), Spacer(flex: 10), ], ), Spacer(flex: 3), ], ); } } class PieChartPainter extends CustomPainter { List angles, colors; int angle; PieChartPainter( {#required List angles, #required List colors, int angle: 360}) { this.angles = angles; this.colors = colors; this.angle = angle; } #override void paint(Canvas canvas, Size size) { Paint p = Paint(); double start = -90; double tmp = 0; for (int i = 0; i < angles.length; i++) { if (i < 5) { p.color = colors[i]; } else { p.color = colors[5]; } if (tmp + angles[i] < angle) { canvas.drawArc(Rect.fromLTRB(0, 0, size.width, size.height), vm.radians(start), vm.radians(angles[i]), true, p); start = start + angles[i]; tmp = tmp + angles[i]; } else { double x = angle - tmp; canvas.drawArc(Rect.fromLTRB(0, 0, size.width, size.height), vm.radians(start), vm.radians(x), true, p); return; } } } #override bool shouldRepaint(CustomPainter oldDelegate) { return true; } } void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { #override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, ), home: Chart( listLanguages: ListLanguages(), ), ); } } class MyHomePage extends StatefulWidget { MyHomePage({Key key, this.title}) : super(key: key); final String title; #override _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { int _counter = 0; void _incrementCounter() { setState(() { _counter++; }); } #override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Text( 'You have pushed the button this many times:', ), Text( '$_counter', style: Theme.of(context).textTheme.headline4, ), ], ), ), floatingActionButton: FloatingActionButton( onPressed: _incrementCounter, tooltip: 'Increment', child: Icon(Icons.add), ), ); } }
I can not use your code so that I can run it (since it's only a small part) but what you need is: Define an animation and animation controller in your state Surround your CustomPainter with an "AnimatedBuilder" which will use this animation and will pass the value between 0 to 360 to your CustomPainter in 2 seconds. Below is an example with comments (which you will have to take parts from and put in to your widget). class Test extends StatefulWidget { #override _TestState createState() => _TestState(); } // NOTE: You need to add "SingleTickerProviderStateMixin" for animation to work class _TestState extends State<Test> with SingleTickerProviderStateMixin { Animation _animation; // Stores animation AnimationController _controller; // Stores controller #override void initState() { super.initState(); _controller = AnimationController( vsync: this, duration: Duration(seconds: 2), ); // Create a 2 second duration controller _animation = IntTween(begin: 0, end: 360) .animate(_controller); // Create the animation using controller with a tween from 0 to 360 WidgetsBinding.instance.addPostFrameCallback((_) { _controller.forward(); // Start the animation when widget is displayed }); } #override void dispose() { _controller.dispose(); // Don't forget to dispose your controller super.dispose(); } #override Widget build(BuildContext context) { return AnimatedBuilder( // AnimatedBuilder using the animation animation: _animation, builder: (context, _){ return CustomPaint( size: Size.square(400), painter: PieChartPainter( angles: angles, colors: new List() ..add(Colors.green) ..add(Colors.blue) ..add(Colors.brown) ..add(Colors.pink) ..add(Colors.orange) ..add(Colors.grey.shade700), angle: _animation.value, // Pass _animation.value (0 to 360) as your angle ), ); }, ); } }