看到一个文章https://www.jianshu.com/p/df33f4595ce6
是一个饼状图,但是不适用,因为数据比较多的时候显示就很密集,所以要用线,添加了一些,不过目前文字大小还没怎么计算,所以是写固定的
整个代码如下
import 'package:flutter/material.dart';
import 'piechart.dart';
import 'PieData.dart';
import 'dart:math';
class View extends CustomPainter{
//中间文字
var text='111';
bool isChange=false;
//当前选中的扇形
var currentSelect=0;
//画笔
Paint _mPaint;
Paint TextPaint;
// 扇形大小
int mWidth, mHeight;
// 圆半径
num mRadius, mInnerRadius,mBigRadius;
// 扇形起始弧度(Andorid中是角度)
num mStartAngle = 0;
// 矩形(扇形绘制的区域)
Rect mOval,mBigOval;
// 扇形 数据
List mData;
PieData pieData;
// 构造函数,接受需要的参数值
View(this.mData,this.pieData,this.currentSelect,this.isChange);
/**
* 重写 paint方法,在其中写绘制饼状图的逻辑
*/
@override
void paint(Canvas canvas, Size size) {
// 初始化各类工具等
_mPaint = new Paint();
TextPaint = new Paint();
mHeight=100;mWidth=100;
/// 生成纵轴文字的TextPainter
TextPainter textPainter = TextPainter(
textDirection: TextDirection.ltr,
maxLines: 1,
);
// 文字画笔 风格定义
TextPainter _newVerticalAxisTextPainter(String text) {
return textPainter
..text = TextSpan(
text: text,
style: new TextStyle(
color: Colors.black,
fontSize: 10.0,
),
);
}
// 正常半径
mRadius = 50.0;
//加大半径 用来绘制被选中的扇形区域
mBigRadius=55.0;
//內园半径
mInnerRadius = mRadius * 0.50;
// 未选中的扇形绘制的矩形区域
mOval = Rect.fromLTRB(-mRadius, -mRadius, mRadius, mRadius);
// 选中的扇形绘制的矩形区域
mBigOval = Rect.fromLTRB(-mBigRadius, -mBigRadius, mBigRadius,
mBigRadius);
//当没有数据时 直接返回
if (mData.length == null || mData.length <= 0) {
return;
}
///绘制逻辑与Android差不多
canvas.save();
// 将坐标点移动到View的中心
canvas.translate(50.0, 50.0);
// 1. 画扇形
num startAngle = 0.0;
bool draw=false;
for (int i = 0; i < mData.length; i++) {
PieData p = mData[i];
double hudu=p.percentage;
//计算当前偏移量(单位为弧度)
double sweepAngle = 2*pi*hudu;
// print(sweepAngle);
//画笔的颜色
_mPaint..color = p.color;
if(currentSelect>=0 && i==currentSelect){
//如果当前为所选中的扇形 则将其半径加大 突出显示
canvas.drawArc(mBigOval, startAngle, sweepAngle, true, _mPaint);
Paint _paint=new Paint();
_paint.color=Color(0xff91c954);
_paint.strokeWidth=1.0;
Offset offset=Offset(mRadius*0.75*cos(startAngle+sweepAngle/2.0),mRadius*0.75*sin(startAngle+sweepAngle/2.0));
// print(mRadius*0.75*sin(2*pi*270.0/360.0));
// print(mRadius*0.75*cos(2*pi*270.0/360.0));
// if(!draw){print(offset);
//print(mRadius*0.75*sin(2*pi*270.0/360.0));
// print(mRadius*0.75*cos(2*pi*270.0/360.0));
TextPainter t=TextPainter(text: TextSpan(text: "11%",style: TextStyle(color: Colors.black),),textDirection: TextDirection.ltr,
maxLines: 1,);
canvas.drawLine(offset, Offset(mRadius*1.25*cos(startAngle+sweepAngle/2.0),mRadius*1.25*sin(startAngle+sweepAngle/2.0)), _paint);
if(mRadius*0.75*cos(startAngle+sweepAngle/2.0)<0){
Offset offset1=new Offset(mRadius*1.25*cos(startAngle+sweepAngle/2.0)-mRadius*0.2, mRadius*1.25*sin(startAngle+sweepAngle/2.0));
canvas.drawLine(Offset(mRadius*1.25*cos(startAngle+sweepAngle/2.0),mRadius*1.25*sin(startAngle+sweepAngle/2.0)), offset1, _paint);
// canvas.restore();
t.layout();
t.paint(canvas, Offset(mRadius*1.25*cos(startAngle+sweepAngle/2.0)-mRadius*0.2-20.0, mRadius*1.25*sin(startAngle+sweepAngle/2.0)-10.0));
}else{
Offset offset1=new Offset(mRadius*1.25*cos(startAngle+sweepAngle/2.0)+mRadius*0.2, mRadius*1.25*sin(startAngle+sweepAngle/2.0));
canvas.drawLine(Offset(mRadius*1.25*cos(startAngle+sweepAngle/2.0),mRadius*1.25*sin(startAngle+sweepAngle/2.0)), offset1, _paint);
// canvas.restore();
t.layout();
t.paint(canvas, Offset(mRadius*1.25*cos(startAngle+sweepAngle/2.0)-mRadius*0.2+20.0, mRadius*1.25*sin(startAngle+sweepAngle/2.0)-10.0));
}
// draw=true;
// }
}else{
// 绘制没被选中的扇形 正常半径
canvas.drawArc(mOval, startAngle, sweepAngle, true, _mPaint);
Offset origin=Offset((-mRadius+mRadius)/2.0, (-mRadius+mRadius)/2.0);
Paint _paint=new Paint();
_paint.color=Color(0xff91c954);
_paint.strokeWidth=1.0;
Offset offset=Offset( mRadius*0.75*cos(startAngle+sweepAngle/2.0),mRadius*0.75*sin(startAngle+sweepAngle/2.0));
canvas.drawLine(offset, Offset(mRadius*1.25*cos(startAngle+sweepAngle/2.0),mRadius*1.25*sin(startAngle+sweepAngle/2.0)), _paint);
TextPainter t=TextPainter(text: TextSpan(text: "11%",style: TextStyle(color: Colors.black),),textDirection: TextDirection.ltr,
maxLines: 1,);
if(mRadius*0.75*cos(startAngle+sweepAngle/2.0)<0){
Offset offset1=new Offset(mRadius*1.25*cos(startAngle+sweepAngle/2.0)-mRadius*0.2, mRadius*1.25*sin(startAngle+sweepAngle/2.0));
canvas.drawLine(Offset(mRadius*1.25*cos(startAngle+sweepAngle/2.0),mRadius*1.25*sin(startAngle+sweepAngle/2.0)), offset1, _paint);
// canvas.restore();
t.layout();
t.paint(canvas, Offset(mRadius*1.25*cos(startAngle+sweepAngle/2.0)-mRadius*0.2-25.0, mRadius*1.25*sin(startAngle+sweepAngle/2.0)-10.0));
}else{
Offset offset1=new Offset(mRadius*1.25*cos(startAngle+sweepAngle/2.0)+mRadius*0.2, mRadius*1.25*sin(startAngle+sweepAngle/2.0));
canvas.drawLine(Offset(mRadius*1.25*cos(startAngle+sweepAngle/2.0),mRadius*1.25*sin(startAngle+sweepAngle/2.0)), offset1, _paint);
// canvas.restore();
t.layout();
t.paint(canvas, Offset(mRadius*1.25*cos(startAngle+sweepAngle/2.0)-mRadius*0.2+20.0, mRadius*1.25*sin(startAngle+sweepAngle/2.0)-10.0));
}
// draw=true;
// }
}
//计算每次开始绘制的弧度
startAngle += sweepAngle ;
// print(startAngle);
}
// canvas.drawRect(mOval, _mPaint); // 矩形区域
// 2.画内圆
_mPaint..color = Colors.white;
canvas.drawCircle(Offset.zero, mInnerRadius, _mPaint);
canvas.restore();
//当前百分比值
double percentage = pieData.percentage*100;
// 绘制文字内容
var texts ='${percentage}%';
var tp = _newVerticalAxisTextPainter(texts)..layout();
// Text的绘制起始点 = 可用宽度 - 文字宽度 - 左边距
var textLeft = 35.0;
tp.paint(canvas, Offset(textLeft, 50.0 - tp.height / 2));
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
绘制text之前,一定要调用layout这个函数,这个跟安卓很像