颤振图看起来不错,但我找不到适合的文档。根据我要实现的目标,我有几个问题:
问题也以注释的形式出现在代码中(并且我对一些参数加了一些理解,以防它对像我这样的初学者有所帮助)
和代码:
/admin/orders/orderid:id
答案 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;
}