Flutter 绘制集录 | n 角星
一、组件使用
1、组件地址与功能简介
【pub地址 】 【github地址】
1dependencies:
2 flutter_star: $lastVersion
目标: 使用canvas手工打造,一个完美的星星评分组件。
1---->[StarScore 星星显示组件]----
2[1] 比如显示4.2: 会有5颗星, 前四颗填满,后一刻填充20%
3StarScore 为 Stateless组件,仅负责显示的需求
4
5---->[CustomRating 星星评分组件]----
6[2] 可指定最大值,也就是显示多少个星星
7[3] 点击时会改变状态,进行评分,支持半星评分
8[4] 支持评分回调
9
10---->[StarWidget组件]----
11[5]. 可定义星星的显示进度情况 0% ~ 100 % 无死角
12[6]. 可定义星星的角数
13[7]. 可定义星星的颜色、大小
2 、StarScore 属性
分数展示组件
名称 | 类型 | 功能 | 备注 | 默认 |
---|---|---|---|---|
score | double | 分数 | – | 0 |
star | Star | 见 第四点 |
星星属性配置 | Star() |
tail | Widget | 尾部的组件 | – | null |
1StarScore(
2 score: 4.8,
3 star: Star(
4 fillColor: Colors.tealAccent,
5 emptyColor: Colors.grey.withAlpha(88)),
6 tail: Column(
7 children: [
8 Text("综合评分"),
9 Text("4.8"),
10 ],
11 ),
12),
3 、CustomRating 属性
评分组件
名称 | 类型 | 功能 | 备注 | 默认 |
---|---|---|---|---|
max | int | 最大星星数 | – | 5 |
score | double | 分数 | – | 0 |
star | Star | 见 第四点 |
星星属性配置 | Star() |
onRating | Fluction(double) | 点击回调 | @required | null |
1.最简使用
1CustomRating(onRating: (s) {
2 print(s);
3 }),
2.可高度定制
1CustomRating(
2 max: 6,
3 score: 3.0,
4 star: Star(
5 num: 12,
6 fillColor: Colors.orangeAccent,
7 fat: 0.6,
8 emptyColor: Colors.grey.withAlpha(88)),
9 onRating: (s) {
10 print(s);
11 }),
4、Star 组件
星星组件 : 高度可定制的配置类
名称 | 类型 | 功能 | 备注 | 默认 |
---|---|---|---|---|
progress | double | 填充的进度 | [0,1] | 0.0 |
num | int | 星星的角数 | 大于3 | 5 |
fat | double | 星星的胖瘦 | (0,1] | 0.5 |
emptyColor | Color | 星星的色 | – | Colors.grey |
fillColor | Color | 星星的填充色 | – | Colors.yellow |
size | double | 星星的大小 | – | 20 |
1. 进度填充:progress
2. 星星的角数:num
3. 星星的胖瘦:fat
4. 星星的颜色:fillColor和emptyColor
展示结束,下面进入正文
二、如何自定义绘制的组件
1.分析组件的需求,抽离出需要配置的属性
1class Star {
2 final int num;
3 final double progress;
4 final Color emptyColor;
5 final Color fillColor;
6 final double size;
7 final double fat;
8
9 const Star({this.progress = 0,
10 this.fat = 0.5,
11 this.fillColor = Colors.yellow,
12 this.emptyColor = Colors.grey,
13 this.num = 5,
14 this.size = 25});
15}
2. 创建好画板准备开
1class _StarPainter extends CustomPainter {
2 Star star;
3 Paint _paint;
4 Paint _filePaint;
5 Path _path;
6 double _radius;
7
8 _StarPainter(this.star) {
9 _paint = Paint()
10 ..color = (star.emptyColor)
11 ..isAntiAlias = true;
12
13 _filePaint = Paint()
14 ..color = (star.fillColor);
15 _path = Path();
16 _radius = star.size / 2.0;
17
18 }
19 @override
20 void paint(Canvas canvas, Size size) {
21 //TODO 绘制
22 }
23
24 @override
25 bool shouldRepaint(CustomPainter oldDelegate) {
26 return true;
27 }
28}
如果说StarWidget是评分组件的基础,那么绘制的路径就是星星的灵魂
下面的nStarPath是绘制n角星路径的核心方法
1 Path nStarPath(int num, double R, double r, {dx = 0, dy = 0, rotate = 0}) {
2 _path.reset(); //重置路径
3 double perRad = 2 * pi / num; //每份的角度
4 double radA = perRad / 2 / 2 + rotate; //a角
5 double radB = 2 * pi / (num - 1) / 2 - radA / 2 + radA + rotate; //起始b角
6 _path.moveTo(cos(radA) * R + dx, -sin(radA) * R + dy); //移动到起点
7 for (int i = 0; i < num; i++) {
8 //循环生成点,路径连至
9 _path.lineTo(
10 cos(radA + perRad * i) * R + dx, -sin(radA + perRad * i) * R + dy);
11 _path.lineTo(
12 cos(radB + perRad * i) * r + dx, -sin(radB + perRad * i) * r + dy);
13 }
14 _path.close();
15 return _path;
16 }
在初始化的时候为路径赋值
1class _StarPainter extends CustomPainter {
2 ...
3 _StarPainter(this.star) {
4 ...
5 _path = Path();
6 _radius = star.size / 2.0;
7 nStarPath(star.num, _radius, _radius * star.fat);
8 }
绘制也非常简单,其中有个进度问题,可以先画背景的星星,
再画一个填充的星星,再用canvas.clipRect进行裁剪即可。
1 @override
2 void paint(Canvas canvas, Size size) {
3 canvas.translate(_radius, _radius);
4 canvas.drawPath(_path, _paint);
5 canvas.clipRect(Rect.fromLTRB(
6 -_radius, -_radius, _radius * 2 * star.progress - _radius, _radius));
7 canvas.drawPath(_path, _filePaint);
8 }
4.自定义组件
将画板放入CustomPaint中即可
1 class StarWidget extends StatelessWidget {
2 final Star star;
3
4 StarWidget({this.star = const Star()});
5
6 @override
7 Widget build(BuildContext context) {
8 return Container(
9 width: star.size,
10 height: star.size,
11 child: CustomPaint(
12 painter: _StarPainter(star),
13 ),
14 );
15 }
16}
这就是星星组件的所有代码,也不超过百行。
三 、StarScore的实现
该组件的目的是显示评分,可以精确的显示百分进度
其实也没啥,仅是用几个StarWidget拼起来而已,代码也就下面一丢丢
1class StarScore extends StatelessWidget {
2 final Star star;
3 final double score;
4 final Widget tail;
5
6 StarScore({this.star = const Star(), this.score, this.tail});
7
8 @override
9 Widget build(BuildContext context) {
10 var li = [];
11 int count = score.floor();
12 for (int i = 0; i < count; i++) {
13 li.add(StarWidget(star: star.copyWith(progress: 1.0)));
14 }
15 if (score - count > 0) {
16 li.add(StarWidget(star: star.copyWith(progress: score - count)));
17 }
18 return Wrap(
19 crossAxisAlignment: WrapCrossAlignment.center,
20 children: [
21 ...li,
22 SizedBox(
23 width: 10,
24 ),
25 if (tail!=null) tail
26 ],
27 );
28 }
29}
四 、CustomRating 的实现
由于点击需要自己响应进行状态改变,所以使用StatefulWidget 最核心的是GestureDetector进行触点的获取并计算出当前值。其中对.5进行处理,以及越界的处理。
1class CustomRating extends StatefulWidget {
2 final int max;
3 final Star star;
4 final double score;
5 final Function(double) onRating;
6
7 CustomRating(
8 {this.max = 5,
9 this.score = 0,
10 this.star = const Star(),
11 @required this.onRating})
12 : assert(score <= max);
13
14 @override
15 _CustomRatingState createState() => _CustomRatingState();
16}
17
18class _CustomRatingState extends State<CustomRating> {
19 double _score;
20
21 @override
22 void initState() {
23 _score = widget.score;
24 super.initState();
25 }
26
27 @override
28 Widget build(BuildContext context) {
29 var li = [];
30
31 int count = _score.floor(); //满星
32 for (int i = 0; i < count; i++) {
33 li.add(StarWidget(star: widget.star.copyWith(progress: 1.0)));
34 }
35
36 if (_score != widget.max.toDouble())
37 li.add(StarWidget(
38 star: widget.star.copyWith(progress: _score - count))); //不满星
39
40 var empty = widget.max - count - 1; // 空星
41 for (int i = 0; i < empty; i++) {
42 li.add(StarWidget(star: widget.star.copyWith(progress: 0)));
43 }
44 return GestureDetector(
45 onTapDown: (d) {
46 setState(() {
47 _score = d.localPosition.dx / widget.star.size;
48 if (_score - _score.floor() > 0.5) {
49 _score = _score.floor() + 1.0;
50 } else {
51 _score = _score.floor() + 0.5;
52 }
53
54 if (_score >= widget.max.toDouble()) {
55 _score = widget.max.toDouble();
56 }
57 widget.onRating(_score);
58 });
59 },
60 child: Wrap(
61 children: li,
62 ),
63 );
64 }
65}
五. 发布到pub
需要在 pubspec.yaml
进行一些配置
1name 是名称
2description 是描述 60 ~ 180 之间,太短或太长会扣分
3version 版本
4author 作者信息,会报warning 但我就想写
5homepage 主页
1---->[pubspec.yaml]----
2name: flutter_star
3description: You can create a star easily and decide how many angle or color of the star, even the fat and progress of the star.
4version: 0.1.2
5author: 张风捷特烈<1981462002@qq.com>
6homepage: https://juejin.im/user/149189281194766/collections
2.最好写个example
不然会扣分
它会在这里,给使用者看
3.发布到pub
1flutter packages pub publish --server=https://pub.dartlang.org
然后需要权限验证,记得 全部复制在浏览器打开,不用在控制台点链接
,由于控制台的换行而导致url不全。
然后就看你的网给不给力了。 flutter_star
欢迎使用