Search code examples
flutterpaintflutter-web

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


Solution

  • 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

    enter image description here

    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),
          ),
        );
      }
    }