10

Flutter 绘制集录 | n 角星

 3 years ago
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

3 、CustomRating 属性

评分组件

名称 类型 功能 备注 默认 max int 最大星星数 - 5 score double 分数 - 0 star Star 见 第四点 星星属性配置 Star() onRating Fluction(double) 点击回调 @required null

1.最简使用

veAVVju.gif!mobile

1CustomRating(onRating: (s) {
2   print(s);
3 }),

2.可高度定制

2YfaQrv.gif!mobile

1CustomRating(
2     max6,
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

Uryeyy6.png!mobile

2. 星星的角数:num

RrYFRfq.png!mobile

3. 星星的胖瘦:fat

r67JRfA.png!mobile

4. 星星的颜色:fillColor和emptyColor

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.
4version0.1.2
5author: 张风捷特烈<1981462002@qq.com>
6homepage: https://juejin.im/user/149189281194766/collections

2.最好写个example

不然会扣分

aqa2IbB.png!mobile

它会在这里,给使用者看

URvaqyA.png!mobile

3.发布到pub

1flutter packages pub publish --server=https://pub.dartlang.org

然后需要权限验证,记得 全部复制在浏览器打开,不用在控制台点链接 ,由于控制台的换行而导致url不全。

然后就看你的网给不给力了。 flutter_star 欢迎使用


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK