rating_widget.dart 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. import 'package:flutter/material.dart';
  2. import 'package:widgets/my_load_image.dart';
  3. class RatingWidget extends StatefulWidget {
  4. final int count;
  5. final double maxRating;
  6. final double value;
  7. final double size;
  8. final double padding;
  9. final String nomalImage;
  10. final String selectImage;
  11. final bool selectAble;
  12. final ValueChanged<String> onRatingUpdate;
  13. final bool integerOnly;
  14. RatingWidget({
  15. required this.nomalImage,
  16. required this.selectImage,
  17. required this.onRatingUpdate,
  18. this.count = 5,
  19. this.value = 5.0,
  20. this.maxRating = 5.0,
  21. this.size = 20,
  22. this.padding = 0,
  23. this.selectAble = false,
  24. this.integerOnly = false,
  25. });
  26. @override
  27. _RatingWidgetState createState() => _RatingWidgetState();
  28. }
  29. class _RatingWidgetState extends State<RatingWidget> {
  30. late double value;
  31. @override
  32. void initState() {
  33. super.initState();
  34. value = widget.value;
  35. }
  36. @override
  37. Widget build(BuildContext context) {
  38. return GestureDetector(
  39. onTapDown: widget.selectAble ? _handleTap : null,
  40. onHorizontalDragUpdate: widget.selectAble ? _handleDrag : null,
  41. child: buildRowRating(),
  42. );
  43. }
  44. void _handleTap(TapDownDetails details) {
  45. _updateValue(details.localPosition.dx);
  46. }
  47. void _handleDrag(DragUpdateDetails details) {
  48. _updateValue(details.localPosition.dx);
  49. }
  50. void _updateValue(double dx) {
  51. if (dx < 0) dx = 0;
  52. if (dx >= widget.size * widget.count + widget.padding * (widget.count - 1)) {
  53. value = widget.maxRating;
  54. } else {
  55. for (int i = 1; i <= widget.count; i++) {
  56. if (dx < i * (widget.size + widget.padding)) {
  57. value = i * (widget.maxRating / widget.count);
  58. if (dx < (i - 1) * (widget.size + widget.padding) + widget.size) {
  59. value -= (widget.size - (dx - (i - 1) * (widget.size + widget.padding))) / widget.size * (widget.maxRating / widget.count);
  60. }
  61. break;
  62. }
  63. }
  64. }
  65. if (widget.integerOnly) {
  66. value = value.ceilToDouble();
  67. }
  68. setState(() {
  69. widget.onRatingUpdate(value.toStringAsFixed(1));
  70. });
  71. }
  72. int fullStars() {
  73. return (value / (widget.maxRating / widget.count)).floor();
  74. }
  75. double star() {
  76. if (widget.integerOnly) {
  77. return 0;
  78. }
  79. return (value % (widget.maxRating / widget.count)) / (widget.maxRating / widget.count) * widget.size;
  80. }
  81. List<Widget> buildRow() {
  82. int full = fullStars();
  83. List<Widget> children = [];
  84. for (int i = 0; i < full; i++) {
  85. children.add(MyAssetImage(
  86. widget.selectImage,
  87. width: widget.size,
  88. height: widget.size,
  89. ));
  90. if (i < widget.count - 1) {
  91. children.add(SizedBox(width: widget.padding));
  92. }
  93. }
  94. if (full < widget.count) {
  95. children.add(
  96. Stack(
  97. children: [
  98. MyAssetImage(
  99. widget.nomalImage,
  100. width: widget.size,
  101. height: widget.size,
  102. ),
  103. ClipRect(
  104. clipper: SMClipper(rating: star()),
  105. child: MyAssetImage(
  106. widget.selectImage,
  107. width: widget.size,
  108. height: widget.size,
  109. ),
  110. ),
  111. ],
  112. ),
  113. );
  114. }
  115. return children;
  116. }
  117. List<Widget> buildNomalRow() {
  118. List<Widget> children = [];
  119. for (int i = 0; i < widget.count; i++) {
  120. children.add(MyAssetImage(
  121. widget.nomalImage,
  122. width: widget.size,
  123. height: widget.size,
  124. ));
  125. if (i < widget.count - 1) {
  126. children.add(SizedBox(width: widget.padding));
  127. }
  128. }
  129. return children;
  130. }
  131. Widget buildRowRating() {
  132. return Stack(
  133. children: <Widget>[
  134. Row(children: buildNomalRow()),
  135. Row(children: buildRow()),
  136. ],
  137. );
  138. }
  139. }
  140. class SMClipper extends CustomClipper<Rect> {
  141. final double rating;
  142. SMClipper({required this.rating});
  143. @override
  144. Rect getClip(Size size) {
  145. return Rect.fromLTRB(0.0, 0.0, rating, size.height);
  146. }
  147. @override
  148. bool shouldReclip(SMClipper oldClipper) {
  149. return rating != oldClipper.rating;
  150. }
  151. }