? ? ? ? UI圖如下
? ? ? ? 通過Flutter中文網(wǎng)的電子書知道,F(xiàn)lutter要實(shí)現(xiàn)一個(gè)自定義控件,需要用到CustomPaint和CustomPainter。
? ?CustomPaint當(dāng)中有一個(gè)參數(shù)painter
這個(gè)painter就是CustomPainter
在CustomPaint中還有一個(gè)child的Widget參數(shù),這個(gè)應(yīng)該都很熟悉,就是一個(gè)子控件,這個(gè)就可以用來設(shè)置自定義控件的大小尺寸。
? ? ? ? 而CustomPainter通過源碼可以知道,里面的參數(shù)并不能實(shí)現(xiàn)這個(gè)UI效果圖的計(jì)算和傳參,那么就需要自己去生成一個(gè)CustomChartPainter繼承于CustomPainter,并重載shouldRepaint、shouldRebuildSemantics和paint方法,其中shouldRepaint和shouldRebuildSemantics設(shè)置分別返回true和false。
根據(jù)UI圖呢,不需要x軸和y軸,但是有坐標(biāo)顯示,那么我們需要兩個(gè)集合xAxis和yAxis,還需要展示的集合數(shù)據(jù)datas,單個(gè)柱形的寬度barWidth,y軸的刻度的個(gè)數(shù)num,那我們也把y軸刻度之間的間隔傳進(jìn)來gap
分別通過size.width和size.height來獲取畫布的寬、高,那么y軸刻度之間的間隔就應(yīng)該是size.height/num,通過這幾個(gè)值,就可以來計(jì)算出y軸刻度的繪制位置
首先先計(jì)算出各個(gè)刻度到畫布頂部的距離,(itemH*index)是刻度的高度,距離頂部的高度的話,就應(yīng)該用畫布的高度減去刻度的高度 ??
final double _top=sizeH-(itemH*index);
刻度繪制的位置應(yīng)該在y軸的左側(cè), 并且我想讓相對(duì)應(yīng)的x軸的水平位置在刻度text的中間位置,那么就需要做一個(gè)偏移量的計(jì)算
final Offset textOffset =Offset(
????0 -ScreenUtil().setSp(12),
? _top -ScreenUtil().setSp(12) /2,
);
繪制文字,我們用TextPainter來實(shí)現(xiàn)
TextPainter(
????text:TextSpan(
????text: value,
? ? ????style:TextStyle(
????????????fontSize:ScreenUtil().setSp(12),
? ? ? ? ????color: ColorUtils.getColor("#C2C2C2")),
????? ),????
????? textAlign: TextAlign.right,
????? textDirection: TextDirection.ltr,
? ????textWidthBasis: TextWidthBasis.longestLine,
?)
?..layout(minWidth:0, maxWidth:ScreenUtil().scaleWidth *40)
..paint(canvas, textOffset);
這樣,y軸刻度這邊就繪制完成了。
接下來繪制x軸和柱狀圖:先定義一個(gè)Paint,
final paint =Paint()..style = PaintingStyle.fill;
計(jì)算出x軸刻度之間的距離(為什么是length-1,是因?yàn)閤軸最左側(cè)不按0刻度來做)
final itemW = size.width / (xAxis.length -1);
按y軸相同的方式把x軸繪制出來(高度+12的原因就是x刻度標(biāo)識(shí)要在x軸下方)
for (var i =0; i < xAxis.length; i++) {
????final xData =xAxis[i];
?????final xOffset =Offset(itemW * i, sh +12);
? // 繪制橫軸標(biāo)識(shí)
? TextPainter(
????????textAlign: TextAlign.center,
? ? ????text:TextSpan(
????????text:'$xData',
? ? ????? style:TextStyle(
????????fontSize:ScreenUtil().setSp(14), color: ColorUtils.getColor("#C2C2C2")),
? ????? ),
? ? ????textDirection: TextDirection.ltr,
? )
..layout(
????minWidth:0,
? ? ? maxWidth: size.width,
? ? )
..paint(canvas, xOffset);
再把柱狀圖之間的間隔計(jì)算出來
final barGap = (size.width -barWidth *xAxis.length) / (xAxis.length -1);
按照UI圖,有個(gè)底色的柱狀圖,然后是有一個(gè)標(biāo)識(shí)數(shù)量的藍(lán)色的柱狀圖,那么通過data * sh / (gap *num)來計(jì)算出柱狀圖的高度,?i *barWidth + (i * barGap) + barGap /2來計(jì)算出柱狀圖的左側(cè)x坐標(biāo)
for (int i =0; i < datas.length; i++) {
????final double data =datas[i];
? final double top = sh - data * sh / (gap *num);
? final double left = i *barWidth + (i * barGap) + barGap /2;
? paint.color = ColorUtils.getColor("#EEEEEE");
? final allRect =Rect.fromLTWH(left, 0, barWidth, sh);//這個(gè)就是用來繪制灰度底色的全高的柱狀圖
? canvas.drawRect(allRect, paint);
? paint.color = ColorUtils.getColor("#305FFF");
? final rect =Rect.fromLTWH(left, top, barWidth, data * sh / (gap *num));
? canvas.drawRect(rect, paint);
}
到這里基本上整個(gè)柱狀圖的計(jì)算代碼就完成了(沒有x、y軸)。
使用起來就方便很多了,放在CustomPaint中使用
全部代碼:同時(shí)也歡迎指正和更好的方法。
import 'package:app_nh/utils/ColorUtils.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class CustomChartextends StatefulWidget {
final Listdatas;
? final ListxAxis;
? final ListyAxis;
? CustomChart(this.datas, this.yAxis, this.xAxis);
? @override
? CustomChartStatecreateState() =>CustomChartState();
}
class CustomChartStateextends State {
@override
? Widgetbuild(BuildContext context) {
ScreenUtil.init(context, width:375, height:667);
? ? return Column(
mainAxisAlignment: MainAxisAlignment.center,
? ? ? children: [
SizedBox(height:24 *ScreenUtil().scaleHeight),
? ? ? ? CustomPaint(
painter:CustomChartPainter(
// 最后向 ColumnChartPainter 傳入 _animations 數(shù)組
? ? ? ? ? ? datas:widget.datas,
? ? ? ? ? ? xAxis:widget.xAxis,
? ? ? ? ? ? barWidth:ScreenUtil().scaleWidth *6.93,
? ? ? ? ? ? gap:50.0,
? ? ? ? ? ? yAxis:widget.yAxis,
? ? ? ? ? ? num:4,
? ? ? ? ? ),
? ? ? ? ? child:Container(
width:ScreenUtil().scaleWidth *300,
? ? ? ? ? ? ? height:ScreenUtil().scaleHeight *160),
? ? ? ? ),
? ? ? ? SizedBox(height:24 *ScreenUtil().scaleHeight),
? ? ? ],
? ? );
? }
}
class CustomChartPainterextends CustomPainter {
final Listdatas;
? final ListxAxis;
? final ListyAxis;
? final barWidth;
? final gap;
? final num;
? CustomChartPainter(
{@required this.datas,
? ? ? @required this.xAxis,
? ? ? @required this.yAxis,
? ? ? @required this.gap,
? ? ? @required this.barWidth,
? ? ? @required this.num});
? @override
? void paint(Canvas canvas, Size size) {
_drawLabels(canvas, size);
? ? _drawBarChart(canvas, size);
? }
void _drawLabels(Canvas canvas, Size size) {
final double sizeH = size.height;
? ? final double sizeW = size.width;
? ? final double itemH = sizeH /num;
? ? yAxis.asMap().forEach((index, value) {
final double _top = sizeH - (itemH * index);
? ? ? final Offset textOffset =Offset(
0 -ScreenUtil().setSp(12),
? ? ? ? _top -ScreenUtil().setSp(12) /2,
? ? ? );
? ? ? TextPainter(
text:TextSpan(
text: value,
? ? ? ? ? style:TextStyle(
fontSize:ScreenUtil().setSp(12),
? ? ? ? ? ? ? color: ColorUtils.getColor("#C2C2C2")),
? ? ? ? ),
? ? ? ? textAlign: TextAlign.right,
? ? ? ? textDirection: TextDirection.ltr,
? ? ? ? textWidthBasis: TextWidthBasis.longestLine,
? ? ? )
..layout(minWidth:0, maxWidth:ScreenUtil().scaleWidth *40)
..paint(canvas, textOffset);
? ? });
? }
void _drawBarChart(Canvas canvas, Size size) {
final sh = size.height;
? ? final paint =Paint()..style = PaintingStyle.fill;
? ? final itemW = size.width / (xAxis.length -1);
? ? for (var i =0; i
final xData =xAxis[i];
? ? ? final xOffset =Offset(itemW * i, sh +12);
? ? ? // 繪制橫軸標(biāo)識(shí)
? ? ? TextPainter(
textAlign: TextAlign.center,
? ? ? ? text:TextSpan(
text:'$xData',
? ? ? ? ? style:TextStyle(
fontSize:ScreenUtil().setSp(14),
? ? ? ? ? ? ? color: ColorUtils.getColor("#C2C2C2")),
? ? ? ? ),
? ? ? ? textDirection: TextDirection.ltr,
? ? ? )
..layout(
minWidth:0,
? ? ? ? ? maxWidth: size.width,
? ? ? ? )
..paint(canvas, xOffset);
? ? }
final barGap = (size.width -barWidth *xAxis.length) / (xAxis.length -1);
? ? for (int i =0; i
final double data =datas[i];
? ? ? final double top = sh - data * sh / (gap *num);
? ? ? final double left = i *barWidth + (i * barGap) + barGap /2;
? ? ? paint.color = ColorUtils.getColor("#EEEEEE");
? ? ? final allRect =Rect.fromLTWH(left, 0, barWidth, sh);
? ? ? canvas.drawRect(allRect, paint);
? ? ? paint.color = ColorUtils.getColor("#305FFF");
? ? ? final rect =Rect.fromLTWH(left, top, barWidth, data * sh / (gap *num));
? ? ? canvas.drawRect(rect, paint);
? ? }
}
@override
? boolshouldRepaint(CustomPainter oldDelegate) =>true;
? @override
? boolshouldRebuildSemantics(CustomPainter oldDelegate) =>false;
}