title: How do you animate between two different lists of CatmullRomSpline points?
body: <p>I've been playing around with the CatmullRomSpline class from Flutter the past few days. Drawing points with is trivial, however, I've been looking for information on interpolating between two sets of values and haven't had much luck.</p>
  <p>to give a better overview here is the first plot of offsets: <a href="https://i.stack.imgur.com/TMivu.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/TMivu.png" alt="1" /></a></p>
  <p>This is the next plot of offsets that I would like to morph/animate to:</p>
  <p><a href="https://i.stack.imgur.com/Xo3Py.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/Xo3Py.png" alt="2" /></a></p>
  <p>Here is the code I have so far:</p>
  <pre><code>import 'dart:ui';
  
  import 'package:curves/data.dart';
  import 'package:flutter/material.dart';
  import 'dart:math' as math;
  
  import 'models/price_plot.dart';
  
  void main() {
    runApp(const App());
  }
  
  class App extends StatelessWidget {
    const App({Key? key}) : super(key: key);
  
    @override
    Widget build(BuildContext context) {
      return const MaterialApp(
        home: Home(),
      );
    }
  }
  
  class Home extends StatefulWidget {
    const Home({
      Key? key,
    }) : super(key: key);
  
    @override
    State&lt;Home&gt; createState() =&gt; _HomeState();
  }
  
  class _HomeState extends State&lt;Home&gt; with SingleTickerProviderStateMixin {
    late Animation&lt;List&lt;Offset&gt;&gt; animation;
    late AnimationController controller;
  
    List&lt;Offset&gt; controlPoints = [];
  
    @override
    void initState() {
      super.initState();
  
      WidgetsBinding.instance.addPostFrameCallback((_) {
        controlPoints = buildControlPoints(hourItems);
  
        setState(() {});
      });
    }
  
    List&lt;Offset&gt; buildControlPoints(List&lt;List&lt;double&gt;&gt; items) {
      final max = items.map((x) =&gt; x.last).fold&lt;double&gt;(
            items.first.last,
            math.max,
          );
      final min = items.map((x) =&gt; x.last).fold&lt;double&gt;(
            items.first.last,
            math.min,
          );
  
      final range = max - min;
  
      final priceMap = {
        for (var v in items)
          v.last: ((max - v.last) / range) * MediaQuery.of(context).size.height,
      };
  
      final pricePlots =
          priceMap.entries.map((e) =&gt; PricePlot(e.value.toInt(), e.key)).toList();
  
      return controlPoints = List.generate(
        pricePlots.length,
        (index) {
          return Offset(
            calculateXOffset(
                MediaQuery.of(context).size.width, pricePlots, index),
            pricePlots[index].yAxis.toDouble(),
          );
        },
      ).toList();
    }
  
    double calculateXOffset(double width, List&lt;PricePlot&gt; list, int index) {
      if (index == (list.length - 1)) {
        return width;
      }
  
      return index == 0 ? 0 : width ~/ list.length * (index + 1);
    }
  
    void _startAnimation() {
      controller.stop();
      controller.reset();
      controller.forward();
    }
  
    @override
    Widget build(BuildContext context) {
      return Scaffold(
        appBar: AppBar(
          title: const Text(&quot;App&quot;),
        ),
        body: Stack(
          children: [
            CustomPaint(
              painter: SplinePainter(controlPoints),
              size: Size(MediaQuery.of(context).size.width,
                  MediaQuery.of(context).size.height),
            ),
            FloatingActionButton(onPressed: () {
              _startAnimation();
            })
          ],
        ),
      );
    }
  }
  
  class SplinePainter extends CustomPainter {
    final List&lt;Offset&gt; items;
  
    SplinePainter(this.items);
  
    @override
    void paint(Canvas canvas, Size size) {
      canvas.drawPaint(Paint()..color = Colors.white);
  
      if (items.isEmpty) {
        return;
      }
  
      final spline = CatmullRomSpline(items);
  
      final bezierPaint = Paint()
        ..strokeCap = StrokeCap.round
        ..strokeWidth = 2
        ..color = Colors.blue;
  
      canvas.drawPoints(
        PointMode.points,
        spline.generateSamples(tolerance: 1e-17).map((e) =&gt; e.value).toList(),
        bezierPaint,
      );
    }
  
    @override
    bool shouldRepaint(SplinePainter oldDelegate) =&gt; true;
  }
  
  </code></pre>