1.效果图
2.设计思路
- 确定使用Stack叠加效果主件,下面采用空(☆),上面叠加实心的(★).
- 对外提供以下属性让外界设置:
1.选中的颜色
2.未选中的颜色
3.星星总个数
4.平分最大值
5.当前平分值
6.星星的图片
3.代码实现
1.创建MaterialApp
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: "Flutter Demo",
theme:
ThemeData(primaryColor: Colors.blue, splashColor: Colors.transparent),
home: Scaffold(
appBar: AppBar(
title: Text("StarDemo"),
),
body: Center(
child: StarRating(
rating: 6.8,
count: 5,
),
),
),
);
}
}
2.创建Stack控件
class StarRating extends StatefulWidget {
/*外界控制的参数*/
final int count;
final double rating;
final double maxRating;
final double size;
final Color unselectedColor;
final Color selectedColor;
final Widget selectedImage;
final Widget unselectedImage;
/*必传参数和默认值*/
StarRating(
{@required this.rating,
this.count = 5,
this.maxRating = 10,
this.size = 30,
this.unselectedColor = const Color(0xffbbbbbb),
this.selectedColor = const Color(0xffff0000),
Widget unselectedImage,
Widget selectedImage})
: unselectedImage = unselectedImage ??
Icon(Icons.star_border, color: unselectedColor, size: size),
selectedImage =
selectedImage ?? Icon(Icons.star, color: selectedColor, size: size);
@override
_StarRatingState createState() => _StarRatingState();
}
class _StarRatingState extends State {
@override
Widget build(BuildContext context) {
/*stack主件叠加实现*/
return Stack(
children: [
/*选中的🌟和未选中的🌟*/
Row(mainAxisSize: MainAxisSize.min, children: buildUnselectedStar()),
Row(mainAxisSize: MainAxisSize.min, children: buildSelectedStar()),
],
);
}
3.未选中的星星
/*未选中🌟*/
List buildUnselectedStar() {
return List.generate(widget.count, (index) {
return widget.unselectedImage;
});
}
4.选中的星星
/*选中的🌟*/
List buildSelectedStar() {
//1.创建star
List stars = [];
final star = widget.selectedImage;
//构建填充满的star
double oneValue = widget.maxRating / widget.count; //每一个占用几分
int entireCount = (widget.rating / oneValue).floor(); //不满一个向下取整
for (var i = 0; i < entireCount; i++) {
stars.add(star);
}
/*构建部分填充star*/
//个数减去整数个乘以一个宽带(3.5-3)*30
double leftwidth = ((widget.rating / oneValue) - entireCount) * widget.size;
final partStar = ClipRect(
child: star,
clipper: StarClipper(leftwidth),
);
stars.add(partStar);
//如果平分大于上限就裁剪
if (stars.length > widget.count) {
return stars.sublist(0, widget.count);
}
return stars;
}
}
5.裁剪不满一个星星
/*自己裁剪🌟*/
class StarClipper extends CustomClipper {
double width;
StarClipper(this.width);
@override
Rect getClip(Size size) {
return Rect.fromLTRB(0, 0, width, size.height);
}
@override
bool shouldReclip(CustomClipper oldClipper) {
return false;
}
}