更新CustomPaint绘图

时间:2020-08-11 15:36:22

标签: flutter paint flutter-web

我的CustomPainter窗口小部件有问题。我想绘制一个可以正常工作的PieChart,然后添加一个Variable,将Chart绘制到该变量,直到达到该角度为止。现在,我要对其进行动画处理,我使用了Future.delayed函数,并在其中使用setState来更新变量,但不幸的是,该方法无法正常工作。

也许我太愚蠢了,无法理解我在做错什么,无法找到正确的答案... 但是如果可以的话,您可以帮我的忙。

如果有所作为,我正在为网络开发。

感谢您的帮助!

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

这是创建饼图所需的完整代码

2 个答案:

答案 0 :(得分:2)

您可以在下面复制粘贴运行完整代码
就您而言,要使用Future.delayed,可以将逻辑从build移到initState并使用addPostFrameCallback
有效的演示更改angle在2、4、6秒内完成,angle为150、250、360
代码段

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

工作演示

enter image description here

完整代码

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

答案 1 :(得分:1)

我无法使用您的代码来运行它(因为它只是一小部分),但是您需要的是:

  1. 在您的状态下定义动画和动画控制器
  2. 用“ AnimatedBuilder”包围CustomPainter,该动画将使用此动画,并在2秒内将0到360之间的值传递给CustomPainter。

下面是一个带有注释的示例(您必须将其从小部件中取出并放入您的小部件中。)


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