how can I create a CustomPainter that animates between multiple colors depending on a value?
The code below only works on one Tween value, create a smooth transition from blue to orange if the value is greater than 400, and back from orange to blue if the value is smaller than 400.
class CircleChart extends CustomPainter {
double value;
final Animation<Color?> color;
CircleChart({required this.value, required Animation<double> animation})
: color = ColorTween(begin: Colors.orange, end: const Color(0xFF00D1FF))
.animate(animation),
super(repaint: animation);
@override
void paint(Canvas canvas, Size size) {
final p1 = Offset(50, 50);
final p2 = Offset(250, 150);
final paint = Paint()
..color = color.value!
..strokeWidth = 4;
canvas.drawLine(p1, p2, paint);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
I want to add one more color there. If the value is smaller than 400, set blue, if greater, transition to orange, if greater than 600, transition to red. If the value starts dropping, I want the animation to reverse and animate from red to orange and then to blue (if it drops below 400).
I was trying TweenSequence, but it didn't give me the expected result.
here is the code where i am triggering animation (from parent widget):
if (magnetometerState.v < 400) {
animationController.forward();
} else {
animationController.reverse();
}
Can anyone share their knowledge how to solve this? There are very few articles about custom painter animations
import 'package:flutter/material.dart';
void main() {
runApp(
const MaterialApp(
home: MyApp(),
),
);
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
double value = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const Spacer(),
AnimatedStick(
value: value / 1000,
),
const Spacer(),
Text('Value: ${value.toInt()}', textAlign: TextAlign.center),
Slider(
min: 0,
max: 1000,
inactiveColor: Colors.black,
value: value,
onChanged: (v) => setState(() {
value = v;
}),
),
const Spacer(),
],
),
);
}
}
class AnimatedStick extends StatefulWidget {
const AnimatedStick({
required this.value,
super.key,
});
final double value;
@override
State<AnimatedStick> createState() => _AnimatedStickState();
}
class _AnimatedStickState extends State<AnimatedStick>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<Color?> colorAnimation;
@override
void didUpdateWidget(_) {
super.didUpdateWidget(_);
_controller.animateTo(widget.value);
super.didChangeDependencies();
}
@override
void initState() {
super.initState();
_controller = AnimationController(
value: widget.value,
duration: const Duration(seconds: 1),
vsync: this,
);
colorAnimation = TweenSequence<Color?>(
<TweenSequenceItem<Color?>>[
TweenSequenceItem(
tween: ColorTween(begin: Colors.orange, end: const Color(0xFF00D1FF)),
weight: 4,
),
TweenSequenceItem(
tween: ColorTween(begin: const Color(0xFF00D1FF), end: Colors.black),
weight: 6,
),
],
).animate(_controller);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _controller,
builder: (context, child) {
return CustomPaint(
painter: CircleChart(
color: colorAnimation.value,
),
);
});
}
}
class CircleChart extends CustomPainter {
final Color? color;
CircleChart({required this.color});
@override
void paint(Canvas canvas, Size size) {
const p1 = Offset(50, 50);
const p2 = Offset(250, 150);
final paint = Paint()
..color = color!
..strokeWidth = 4;
canvas.drawLine(p1, p2, paint);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}