波动图示例/文档/基础

时间:2018-11-07 17:12:15

标签: charts flutter

颤振图看起来不错,但我找不到适合的文档。根据我要实现的目标,我有几个问题:

enter image description here

问题也以注释的形式出现在代码中(并且我对一些参数加了一些理解,以防它对像我这样的初学者有所帮助)

  • 问题1:来自MAterialApp的主题不能自然地级联到儿童小部件...为什么?
  • 问题2:我想将网格的值限制为5,以为视口0.5会有所帮助,但它会完全分散散点图-为什么?
  • 问题3:如何在散点图中分别命名基准?
  • 问题4:我尝试绘制绿色圆弧来划定某些区域,但是在示例库中使用代码不起作用(我使用的是直线示例,而不是将圆弧作为纯副本,但是它没有仍然无法正常工作)-我可以使用具有透明填充颜色的原点,但是我需要能够将其剪切...
  • 问题5:如何命名轴?

和代码:

/admin/orders/orderid:id

1 个答案:

答案 0 :(得分:2)

所以我最终自己构建了一个组件(因此并没有真正回答问题,但仍然可以达到预期的结果)

我没看过主题级联

import 'package:flutter/material.dart';
//Initially used ParagraphBuilder and then canvas.drawParagraph
//but TextSpan and TextPainter allowed me to find the size of string
//import 'dart:ui' as ui;
import 'dart:math';

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Material App Title',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'A title'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;

  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return ListView(
      children: <Widget>[
        Text(widget.title),
        SizedBox(
          height: 350.0,
          //width not required as widget is taking all the space because of ListView I think
          //width: 300.0,
          child: Card(
            child: CustomPaint(
              //using the class defined below and passing an array of PlotPoint (also defined below)
              painter: ScatterPlot5(
                plotPoints: <PlotPoint>[
                  PlotPoint(
                    4,
                    2,
                    2.0,
                    text: 'test1',
                    textSize: 10.0,
                    textColor: Colors.blue,
                    shape: 'circ',
                    fillColor: Colors.green,
                    strokeColor: Colors.pink,
                    strokeWidth: 2.0,
                  ),
                  PlotPoint(
                    3,
                    4,
                    10.0,
                    text: 'test2',
                    textSize: 15.0,
                    textColor: Colors.blue,
                    shape: 'rect',
                    fillColor: Colors.green,
                    strokeColor: Colors.red,
                    strokeWidth: 2.0,
                  ),
                ],
                quadrantColor: Colors.green,
                quadrantStrokeWidth: 1.0,
                xAxisText: 'a large text',
                yAxisText: 'a large text too',
              ),
            ),
          ),
        ),
      ],
    );
  }
}

class ScatterPlot5 extends CustomPainter {
  //List of PlotPoints to be plotted
  List<PlotPoint> plotPoints;

  //Attributes for the frame of the plotting area
  Color quadrantColor;
  double quadrantStrokeWidth;

  //Attributes for the axis text
  Color axisTextColor;
  double axisTextFontSize;
  String yAxisText;
  String xAxisText;

  //I know my values will be between 0 and 5 but could be computed by going through the PlotPoints
  final num maxValue = 5.0;
  final num minValue = 0.0;

  //Y Space between the widget border and what will be plotted
  double yPadding = 5.0;
  //Y Space between the text and the arrow
  double yAxisTextMargin = 5.0;
  //Y Space for the height of the axis arrow (the measures for the Y axis arrow are the default as the X arrow is basically a rotation of the Y one, so not creating another set of var for it)
  double yAxisArrowHeight = 10.0;
  //Y Space between the axis arrow and the frame
  double yAxisArrowMargin = 5.0;
  //Y Space to be calculated in order for the plotting area to be square and centered
  double ySquareMargin = 0.0;
  //Y coordinate representing the 0 on the Y axis
  double yBase;
  //Y height of the plotting area
  double plotHeight;
  //Y factor to translate coordinate such as (0,4) into pixels coordinate
  double yIncrement;

  //X Space between the widget border and what will be plotted
  double xPadding = 5.0;
  //X offset for the axis text to be displayed (a bit on the left for the Y axis, a bit overflowing on the right for the X axis)
  double xAxisTextWidthOffset = 10.0;
  //X width of the axis arrow
  double xAxisArrowWidth = 10.0;
  //X space between the axis arrow and the frame
  double xAxisArrowMargin = 5.0;
  //X Space to be calculated in order for the plotting area to be square and centered
  double xSquareMargin = 0.0;
  //X coordinate representing the 0 on the X axis
  double xBase;
  //X width of the plotting area
  double plotWidth;
  //X factor to translate coordinate such as (0,4) into pixels coordinate
  double xIncrement;

  //Y offset when displaying the text of a PlotPoint (weirdly enough centering on Y the text still has an offset...so compensating with this)
  double yPlotPointTextOffset = 2.0;
  //X space between the PlotPoint displayed and its text
  double xPlotPointMargin = 5.0;

  ScatterPlot5(
      {this.plotPoints,
      this.quadrantColor,
      this.quadrantStrokeWidth,
      this.axisTextColor = Colors.black,
      this.axisTextFontSize = 15.0,
      this.yAxisText = 'Y',
      this.xAxisText = 'X'});

  //To calculate all the required values that will allow the ploting area to be square and centered + support translating point coordinates into pixels
  void _setValues(Size size) {
    //resting these values to be recalculated in case of a screen orientation change/reshape
    ySquareMargin = 0.0;
    xSquareMargin = 0.0;
    //finding the max space of the arrow (by default it fits in a square, but could be a pointy one)
    double maxArrow = (yAxisArrowHeight > xAxisArrowWidth)
        ? yAxisArrowHeight
        : xAxisArrowWidth;

    //Y space that between the border of the widget and the plotting area
    double nonPlotYUnit = yPadding +
        axisTextFontSize +
        yAxisTextMargin +
        maxArrow +
        yAxisArrowMargin;
    //Y base is hence the size of the widget minus the empty area
    yBase = size.height - nonPlotYUnit;
    //Y height is thus the size minus the 2 empty areas
    plotHeight = size.height - (2 * nonPlotYUnit);

    //X base is the the border of the widget (at 0) + the empty space
    //(using the right hand side where the axis arrow is as the default to make sure the plotting area will be centered)
    //Note that this is a worst case scenario as xAxisTextWidthOffset could fit within the space left by the other attributes
    xBase = xPadding + maxArrow + xAxisArrowMargin + xAxisTextWidthOffset;
    plotWidth = size.width - 2 * xBase;

    //reseting plot dimensions to the minimum to ensure we have a square and set the additional margin needed to achieve it
    if (plotHeight > plotWidth) {
      ySquareMargin = (plotHeight - plotWidth) / 2;
      plotHeight = plotWidth;
      //reseting the base as it moved
      yBase -= ySquareMargin;
    } else {
      xSquareMargin = (plotWidth - plotHeight) / 2;
      plotWidth = plotHeight;
      //reseting the base as it moved
      xBase += xSquareMargin;
    }
    //Setting the factor for translation of coordinates into pixels
    yIncrement = plotHeight / maxValue;
    xIncrement = plotWidth / maxValue;
  }

  //Takes plotting area coordinates (0-5) and returns their pixel equivalent
  Offset _coord(num x, num y) {
    return Offset(xBase + x * xIncrement, yBase - y * yIncrement);
  }

  @override
  void paint(Canvas canvas, Size size) {
    //Before painting reset values to make sure all data is in line with current size/orientation
    _setValues(size);

    //Setting up the paint for the frame
    Paint quadrantPaint = Paint()
      ..color = quadrantColor
      ..strokeWidth = quadrantStrokeWidth
      ..style = PaintingStyle.stroke;

    //The overall rectangle
    canvas.drawRect( Rect.fromPoints(_coord(0, 5), _coord(5, 0)), quadrantPaint, );
    //The top right arc
    canvas.drawArc(
      Rect.fromPoints(_coord(3.5, 6.5), _coord(6.5, 3.5)),
      //starting angle
      pi / 2,
      //angle to add to the starting angle (not the target angle...)
      pi / 2,
      true,
      quadrantPaint,
    );
    //The bottom left arc
    canvas.drawArc(
      Rect.fromPoints(_coord(-1.5, 1.5), _coord(1.5, -1.5)),
      3 * pi / 2,
      pi / 2,
      true,
      quadrantPaint,
    );
    //The little axis extension to get to the top left arrow
    Offset topLeft = _coord(0, 5);
    canvas.drawLine(topLeft, Offset(topLeft.dx, topLeft.dy - yAxisArrowMargin), quadrantPaint);
    //The little axis extension to get to the bottow right arrow
    Offset bottomRight = _coord(5, 0);
    canvas.drawLine(bottomRight, Offset(bottomRight.dx + xAxisArrowMargin, bottomRight.dy), quadrantPaint);

    //Changing the style to fill to draw the arrows
    quadrantPaint.style = PaintingStyle.fill;

    //creating the Y axis arrow
    Path yPath = Path();
    yPath.moveTo(_coord(0, 5).dx - xAxisArrowWidth / 2, _coord(0, 5).dy - yAxisArrowMargin);
    yPath.relativeLineTo(xAxisArrowWidth, 0.0);
    yPath.relativeLineTo(-xAxisArrowWidth / 2, -yAxisArrowHeight);
    yPath.relativeLineTo(-xAxisArrowWidth / 2, yAxisArrowHeight);
    canvas.drawPath(yPath, quadrantPaint);

    //creating the X axis arrow
    //remember that height/width of the arrow are for the Y axis top right arrow...need to rotate that for the X one to look the same
    Path xPath = Path();
    xPath.moveTo(_coord(5, 0).dx + xAxisArrowMargin, _coord(5, 0).dy - xAxisArrowWidth / 2);
    xPath.relativeLineTo(0.0, xAxisArrowWidth);
    xPath.relativeLineTo(yAxisArrowHeight, -xAxisArrowWidth / 2);
    xPath.relativeLineTo(-yAxisArrowHeight, -xAxisArrowWidth / 2);
    canvas.drawPath(xPath, quadrantPaint);

    /* I initually used Paragraph builder but couldn't calculate height and width for the text object...leaving it here as an example
    ui.ParagraphBuilder yAxisbuilder = ui.ParagraphBuilder(
        ui.ParagraphStyle(
          fontSize: axisTextFontSize,
          textAlign: TextAlign.left,
        ),
      )
        ..pushStyle(ui.TextStyle(color: axisTextColor))
        ..addText(yAxisText);
    ui.Paragraph yPara = yAxisbuilder.build()
      ..layout(ui.ParagraphConstraints(width: 100.0));

    canvas.drawParagraph(
      yPara,
      Offset(_coord(0,5).dx-xAxisTextWidthOffset,_coord(0,5).dy-yAxisArrowMargin-yAxisArrowHeight-yAxisTextMargin-yAxisTextHeight),
      );
    */

    //X axis label 1) create span, 2) create TextPainter, 3) layout the painter and paint it
    TextSpan xSpan = TextSpan(
      style: TextStyle(
        color: axisTextColor,
        fontSize: axisTextFontSize,
      ),
      text: xAxisText,
    );
    TextPainter xtp = TextPainter(
      text: xSpan,
      textAlign: TextAlign.left,
      textDirection: TextDirection.ltr,
    );
    xtp.maxLines = 1;
    xtp.layout();
    xtp.paint(canvas, Offset(_coord(5, 0).dx + xAxisTextWidthOffset - xtp.width, _coord(5, 0).dy + xAxisArrowWidth / 2 + yAxisTextMargin), );

    //Y axis label
    TextSpan ySpan = TextSpan(
      style: TextStyle(
        color: axisTextColor,
        fontSize: axisTextFontSize,
      ),
      text: yAxisText,
    );
    TextPainter ytp = TextPainter(
      text: ySpan,
      textAlign: TextAlign.left,
      textDirection: TextDirection.ltr,
    );
    ytp.maxLines = 1;
    ytp.layout();
    ytp.paint(canvas, Offset(_coord(0, 5).dx - xAxisTextWidthOffset, _coord(0, 5).dy - yAxisArrowMargin - yAxisArrowHeight - yAxisTextMargin - axisTextFontSize),);

    //Now the points
    for (int i = 0; i < plotPoints.length; i++) {

      //Creating the paint for each point with first the fill information
      Paint ppPaint = Paint()
        ..color = plotPoints[i].fillColor
        ..strokeWidth = plotPoints[i].strokeWidth
        ..style = PaintingStyle.fill;

      //defining the point position
      Offset ppOffset = _coord(plotPoints[i].x, plotPoints[i].y);
      //Depending on the shape wanted, draw a rect of a circle
      //note that 2 things are painted, 1 the filled version, then another version with the border only after ppPaint.style has been changed
      if (plotPoints[i].shape == 'rect') {
        Rect rect = Rect.fromCircle(
          center: ppOffset,
          radius: plotPoints[i].radius,
        );
        canvas.drawRect(rect, ppPaint);
        //changing paint to focus on the stroke
        ppPaint.color = plotPoints[i].strokeColor;
        ppPaint.style = PaintingStyle.stroke;
        //paint the same rect but with the stroke style set
        canvas.drawRect(rect, ppPaint);
      } else {
        canvas.drawCircle(ppOffset, plotPoints[i].radius, ppPaint);
        //changing paint to focus on the stroke
        ppPaint.color = plotPoints[i].strokeColor;
        ppPaint.style = PaintingStyle.stroke;
        //paint the same rect but with the stroke style set
        canvas.drawCircle(ppOffset, plotPoints[i].radius, ppPaint);
      }

      //Text for the PlotPoint
      TextSpan ppSpan = TextSpan(
        style: TextStyle(
          color: plotPoints[i].textColor,
          fontSize: plotPoints[i].textSize,
        ),
        text: plotPoints[i].text,
      );
      TextPainter pptp = TextPainter(
        text: ppSpan,
        textAlign: TextAlign.left,
        textDirection: TextDirection.ltr,
      );
      pptp.maxLines = 1;
      pptp.layout();
      //XXX add collision detection with other plotpoint text too
      //if text is going out of canvas then paint it to the left of the plot point otherwise on the right
      if (ppOffset.dx + plotPoints[i].radius + xPlotPointMargin + pptp.width > size.width) {
        pptp.paint(
          canvas,Offset(ppOffset.dx - plotPoints[i].radius - xPlotPointMargin - pptp.width, ppOffset.dy - yPlotPointTextOffset - plotPoints[i].textSize / 2),);
      } else {
        pptp.paint(canvas, Offset(ppOffset.dx + plotPoints[i].radius + xPlotPointMargin, ppOffset.dy - yPlotPointTextOffset - plotPoints[i].textSize / 2),);
      }
    }
  }

  @override
  bool shouldRepaint(ScatterPlot5 old) => true;
}

//class to provide point info
class PlotPoint {
  num _x;
  num _y;
  num _radius;
  String text;
  double textSize;
  Color textColor;
  String shape;
  Color fillColor;
  Color strokeColor;
  double strokeWidth;

  PlotPoint(this._x, this._y, this._radius,
      {this.text = '',
      this.textSize = 10.0,
      this.textColor = Colors.black,
      this.shape = 'circ',
      this.fillColor = Colors.blue,
      this.strokeColor = Colors.black,
      this.strokeWidth = 1.0});

  num get x => _x;
  num get y => _y;
  num get radius => _radius;
}