123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208 |
- import 'package:flutter/material.dart';
- import 'package:widgets/my_load_image.dart';
- class RatingWidget extends StatefulWidget {
- /// 星星数量
- final int count;
- /// 最大评分值
- final double maxRating;
- /// 当前选中的评分值
- final double value;
- /// 没一颗星的大小
- final double size;
- /// 每个星之间的间距
- final double padding;
- /// 空星的图片资源
- final String nomalImage;
- /// 满星的图片资源
- final String selectImage;
- /// 是否可以操作选择
- final bool selectAble;
- /// 选中评分的回调
- final ValueChanged<String> onRatingUpdate;
- /// 是否只显示整数的分数
- final bool integerOnly;
- RatingWidget({
- required this.nomalImage,
- required this.selectImage,
- required this.onRatingUpdate,
- this.count = 5,
- this.value = 5.0,
- this.maxRating = 5.0,
- this.size = 20,
- this.padding = 0,
- this.selectAble = false,
- this.integerOnly = false,
- });
- @override
- _RatingWidgetState createState() => _RatingWidgetState();
- }
- class _RatingWidgetState extends State<RatingWidget> {
- late double value;
- @override
- Widget build(BuildContext context) {
- return Listener(
- child: buildRowRating(),
- onPointerDown: (PointerDownEvent event) {
- double x = event.localPosition.dx;
- if (x < 0) x = 0;
- pointValue(x);
- },
- onPointerMove: (PointerMoveEvent event) {
- double x = event.localPosition.dx;
- if (x < 0) x = 0;
- pointValue(x);
- },
- onPointerUp: (_) {},
- behavior: HitTestBehavior.deferToChild,
- );
- }
- pointValue(double dx) {
- if (!widget.selectAble) {
- return;
- }
- if (dx >= widget.size * widget.count + widget.padding * (widget.count - 1)) {
- value = widget.maxRating;
- } else {
- for (double i = 1; i < widget.count + 1; i++) {
- if (dx > widget.size * i + widget.padding * (i - 1) && dx < widget.size * i + widget.padding * i) {
- value = i * (widget.maxRating / widget.count);
- break;
- } else if (dx > widget.size * (i - 1) + widget.padding * (i - 1) && dx < widget.size * i + widget.padding * i) {
- value = (dx - widget.padding * (i - 1)) / (widget.size * widget.count) * widget.maxRating;
- break;
- }
- }
- }
- if (widget.integerOnly) {
- value = value.ceilToDouble(); // 直接向上取整
- }
- setState(() {
- widget.onRatingUpdate(value.toStringAsFixed(1));
- });
- }
- int fullStars() {
- if (value != null) {
- return (value / (widget.maxRating / widget.count)).ceil(); // 使用向上取整
- }
- return 0;
- }
- double star() {
- if (widget.integerOnly) {
- return 0;
- }
- if (value != null) {
- if (widget.count / fullStars() == widget.maxRating / value) {
- return 0;
- }
- return (value % (widget.maxRating / widget.count)) / (widget.maxRating / widget.count);
- }
- return 0;
- }
- List<Widget> buildRow() {
- int full = fullStars();
- List<Widget> children = [];
- for (int i = 0; i < full; i++) {
- children.add(MyAssetImage(
- widget.selectImage,
- width: widget.size,
- height: widget.size,
- ));
- if (i < widget.count - 1) {
- children.add(
- SizedBox(
- width: widget.padding,
- ),
- );
- }
- }
- if (full < widget.count) {
- children.add(ClipRect(
- clipper: SMClipper(rating: star() * widget.size),
- child: MyAssetImage(
- widget.selectImage,
- width: widget.size,
- height: widget.size,
- ),
- ));
- }
- return children;
- }
- List<Widget> buildNomalRow() {
- List<Widget> children = [];
- for (int i = 0; i < widget.count; i++) {
- children.add(MyAssetImage(
- widget.nomalImage,
- width: widget.size,
- height: widget.size,
- ));
- if (i < widget.count - 1) {
- children.add(SizedBox(
- width: widget.padding,
- ));
- }
- }
- return children;
- }
- Widget buildRowRating() {
- return Container(
- child: Stack(
- children: <Widget>[
- Row(
- children: buildNomalRow(),
- ),
- Row(
- children: buildRow(),
- )
- ],
- ),
- );
- }
- @override
- void initState() {
- super.initState();
- value = widget.value;
- }
- }
- class SMClipper extends CustomClipper<Rect> {
- final double rating;
- SMClipper({required this.rating});
- @override
- Rect getClip(Size size) {
- return Rect.fromLTRB(0.0, 0.0, rating, size.height);
- }
- @override
- bool shouldReclip(SMClipper oldClipper) {
- return rating != oldClipper.rating;
- }
- }
|