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