继续创作,加速生长!这是我参加「日新方案 10 月更文挑战」的第 11 天,点击检查活动概况
前语
上一篇 《Flutter 制作探究 | 扇形区域与点击校验》 中,咱们现已完成了 扇形区域途径
的生成,和校验点击手势的功用:
本篇,将依据扇形区域,完成最基本的饼图制作作用,以及简略的点触激活作用:
1. 饼图根底制作
一个 SectorShape
对象对应着界面上的一个扇形区域。如下所示,是 startAngle
为 -90
,扫描 108
的图形:
SectorShape shape = SectorShape(
center: Offset(size.width / 2, size.height / 2),
innerRadius: innerRadius,
outRadius: 80,
startAngle: -pi/2,
sweepAngle: 0.3* 2 * pi,
);
对于一个饼图而言,便是若干个不同颜色的扇形区域顺次排放的作用。而 数据
决议扇形区域个数和扫描视点。接下来完成一下依据数值列表,烘托对于的饼图:
final List<double> data = [0.3,0.3,0.15,0.1,0.15];
下面是通过数据列表映射出 SectorShape
列表的办法,主要逻辑是遍历数据,依据是数据决议其实视点和扫描视点:
List<SectorShape> mapShapeByData(List<double> data, Size size) {
List<SectorShape> shapes = [];
double cursor = 0;
final Offset center = Offset(size.width / 2, size.height / 2);
for (int i = 0; i < data.length; i++) {
SectorShape shape = SectorShape(
center: center,
innerRadius: 40,
outRadius: 80,
startAngle: -pi / 2 + cursor * 2*pi,
sweepAngle: data[i] * 2 * pi,
);
shapes.add(shape);
cursor += data[i];
}
return shapes;
}
然后在制作是遍历制作即可,这样不同的数据就能有不同的展现作用,这便是数据的可视化
展现。
final List<double> data = [0.2, 0.3, 0.15, 0.2, 0.15];
List<SectorShape> shapes = mapShapeByData(data, size);
for (int i = 0; i < shapes.length; i++) {
paint.color = colors[i];
canvas.drawPath(shapes[i].formPath(), paint);
}
在创立 SectorShape
对象时,能够通过 innerRadius
的巨细确认空泛的巨细,比方下面是 innerRadius = 0
的作用,即实心饼图:
2. 对一组数据的处理
上面的 data
数据必须是表明的还是占比,必须之和为 1
。那如何依据一组数据,来展现饼图呢?比方下面是七本小册的销量,想看一下占比状况:
final List<double> data = [944, 1141, 910, 1094, 1589,1423,2956];
这道题应该小学生都会做,便是算份额呗。只要将数据与处理一下即可:
List<double> preprocess(List<num> data){
num sum =0;
data.forEach((e) => sum+=e);
return data.map((e) => e/sum).toList();
}
这样就能够展现出七本小册的销量状况,但是目前图中无法反响哪本是哪个部分。所以咱们需要处理一下图例。
3. 图例处理
在处理图例之前,先封装一下数据,如下 SaleData
中存储称号和销量数据:
class SaleData {
final String name;
final int value;
const SaleData({
required this.name,
required this.value,
});
}
然后预备一下测试数据:
static const List<SaleData> test = [
SaleData(name: "Flutter 言语根底 - 梦始之地",value: 944),
SaleData(name: "Flutter 烘托机制 - 聚沙成塔",value: 910),
SaleData(name: "Flutter 布局探究 - 薪火相传",value: 1094),
SaleData(name: "Flutter 滑动探究 - 珠联璧合",value: 1141),
SaleData(name: "Flutter 动画探究 - 流光幻影",value: 1589),
SaleData(name: "Flutter 手势探究 - 执掌全国",value: 1423),
SaleData(name: "Flutter 制作指南 - 妙笔生花",value: 2956),
];
需要完成的作用如下:这儿右侧的图例暂时通过 Flutter
组件完成。遍历数据,通过 Wrap
组件竖向包裹 LegendItem
即可。
class LegendItem extends StatelessWidget {
final SaleData data;
final Color color;
const LegendItem({Key? key,required this.data,required this.color}) : super(key: key);
@override
Widget build(BuildContext context) {
return Wrap(
crossAxisAlignment: WrapCrossAlignment.center,
children: [
CircleAvatar(
backgroundColor: color,
radius: 6,
),
const SizedBox(width: 6,),
Text(data.name,),
],
);
}
}
4. 饼图的点击事情
如下所示,点击扇形区域时,对应的扇形会 沿角平分线
移动,达到 弹出
的作用。
有了上一篇 SectorShape
中完成的 contains
办法校验点是否在矩形区域内,点击激活的作用就很简单完成。如下所示,在生成 SectorShape
时,校验触点是否在区域内,如果在,只要沿角平分线将 center
值平移即可:
List<SectorShape> mapShapeByData(List<double> data, Size size) {
_activeIndex = -1;
//略同...
double rad = shape.startAngle + data[i] * 2 * pi / 2;
if (shape.contains(p)) {
_activeIndex = i;
shape.center = shape.center.translate(5 * cos(rad), 5 * sin(rad));
}
//略同...
}
return shapes;
}
点击时,也能够让对应扇形区域的大圆半径增加,达到 凸出
的作用,如下图所示:
int _activeIndex = -1;
List<SectorShape> mapShapeByData(List<double> data, Size size) {
_activeIndex = -1;
//略同...
double rad = shape.startAngle + data[i] * 2 * pi / 2;
if (shape.contains(p)) {
_activeIndex = i;
shape.outRadius += 5;
}
//略同...
}
return shapes;
}
这儿用 _activeIndex
记录了激活索引,依据索引在制作时能够依据途径来制作阴影:
if (i == _activeIndex) {
canvas.drawShadow(path, Colors.grey, 2, false);
}
到这儿,饼图
的基本制作和点击事情就完成了。但这只是最根底的东西,另外还有很多细节需要考虑,比方动画、图例的事情、结构的封装等,想要做好一个通用的图表库,还有很长的路要走。现在把根底的逻辑打通,也有利于之后的整合。下一篇,将看一下 饼图
中动画的完成,那本文就到这儿,谢谢观看 ~
更多 Flutter 制作技巧,欢迎重视《Flutter 制作探究》专栏。
@张风捷特烈 2022.10.28 未允禁转
我的 公众号: 编程之王
-
我的 github 主页
: toly1994328