1

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

JBTG
  • 91
  • 1
  • 7
  • 1
    https://gist.github.com/mpfaff/5c6247687d4595dde3f69a7e6008f14e#file-main-dart-L33 – pskink Apr 17 '23 at 08:36
  • @pskink Its an old code, does not work properly after adjusting (displaying red screen) – JBTG Apr 17 '23 at 08:48
  • I don't say it is exact answer to your question, I pointed the line how to build `TweenSequenceItem` list to be used by `TweenSequence` – pskink Apr 17 '23 at 09:26
  • 1
    besides, you cannot use stuff like `animationController.forward()` - you need `animationController.animateTo(0.4)` for example if the current controller value is 0.2 or 0.6 (all it applies if you have 6 colors, so the values for each color is 0.0 0.2 0.4 0.6 0.8 and 1.0), more https://api.flutter.dev/flutter/animation/AnimationController/animateTo.html – pskink Apr 17 '23 at 09:39

1 Answers1

2

enter image description here

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;
  }
}
Kherel
  • 14,882
  • 5
  • 50
  • 80