![](/style/images/good.png)
![](/style/images/bad.png)
Flutter 绘制集录 | n 角星
source link: https://mp.weixin.qq.com/s?__biz=MzI0NjU3MDA4NQ%3D%3D&%3Bmid=2247484622&%3Bidx=1&%3Bsn=55212fdd94614cd96d3bcfbbf249446a
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
一、组件使用
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: <Widget>[
8 Text("综合评分"),
9 Text("4.8"),
10 ],
11 ),
12),
![RfEn6ju.png!mobile](https://img0.tuicool.com/RfEn6ju.png!mobile)
3 、CustomRating 属性
评分组件
名称 类型 功能 备注 默认 max int 最大星星数 - 5 score double 分数 - 0 star Star 见第四点
星星属性配置
Star()
onRating
Fluction(double)
点击回调
@required
null
1.最简使用
![veAVVju.gif!mobile](https://img2.tuicool.com/veAVVju.gif!mobile)
1CustomRating(onRating: (s) {
2 print(s);
3 }),
2.可高度定制
![2YfaQrv.gif!mobile](https://img2.tuicool.com/2YfaQrv.gif!mobile)
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 星星的大小 - 201. 进度填充:progress
![Uryeyy6.png!mobile](https://img2.tuicool.com/Uryeyy6.png!mobile)
2. 星星的角数:num
![RrYFRfq.png!mobile](https://img2.tuicool.com/RrYFRfq.png!mobile)
3. 星星的胖瘦:fat
![r67JRfA.png!mobile](https://img0.tuicool.com/r67JRfA.png!mobile)
4. 星星的颜色:fillColor和emptyColor
![UfI7jif.png!mobile](https://img1.tuicool.com/UfI7jif.png!mobile)
展示结束,下面进入正文
二、如何自定义绘制的组件
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 = <StarWidget>[];
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 = <StarWidget>[];
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
不然会扣分
![aqa2IbB.png!mobile](https://img2.tuicool.com/aqa2IbB.png!mobile)
它会在这里,给使用者看
![URvaqyA.png!mobile](https://img1.tuicool.com/URvaqyA.png!mobile)
3.发布到pub
1flutter packages pub publish --server=https://pub.dartlang.org
然后需要权限验证,记得 全部复制在浏览器打开,不用在控制台点链接
,由于控制台的换行而导致url不全。
然后就看你的网给不给力了。 flutter_star
欢迎使用
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK