我正在尝试为用户评分创建一个星形栏,以下图片来自Play商店以说明我想要实现的目标:
我已经能够实现以下功能,正如您所看到的那样功能正常,但是当我查看我的代码时,我觉得必须有一种更聪明的方法来实现它,真正令人讨厌的部分是{{ 1}}命中区域向上移动一点点,所以当你触摸实际的星星时,它不会注册为触摸事件(即:你必须瞄准高于按钮所在的位置,以便你的触摸被注册,这是一个糟糕的用户体验)当你点击任何一个星星时,你可以通过关注飞溅效果来检查我的意思:
IconButton
那么这里更好的方法是什么?
答案 0 :(得分:16)
重复和填充太多了!咄
无论如何,这就是我的表现。简单,可重复使用。 你可以使用和不使用点击(没有点击禁用涟漪效果)。 半星也是。如果没有指定颜色,则使用填充星形的原色。
typedef void RatingChangeCallback(double rating);
class StarRating extends StatelessWidget {
final int starCount;
final double rating;
final RatingChangeCallback onRatingChanged;
final Color color;
StarRating({this.starCount = 5, this.rating = .0, this.onRatingChanged, this.color});
Widget buildStar(BuildContext context, int index) {
Icon icon;
if (index >= rating) {
icon = new Icon(
Icons.star_border,
color: Theme.of(context).buttonColor,
);
}
else if (index > rating - 1 && index < rating) {
icon = new Icon(
Icons.star_half,
color: color ?? Theme.of(context).primaryColor,
);
} else {
icon = new Icon(
Icons.star,
color: color ?? Theme.of(context).primaryColor,
);
}
return new InkResponse(
onTap: onRatingChanged == null ? null : () => onRatingChanged(index + 1.0),
child: icon,
);
}
@override
Widget build(BuildContext context) {
return new Row(children: new List.generate(starCount, (index) => buildStar(context, index)));
}
}
然后您可以像这样使用它:
class Test extends StatefulWidget {
@override
_TestState createState() => new _TestState();
}
class _TestState extends State<Test> {
double rating = 3.5;
@override
Widget build(BuildContext context) {
return new StarRating(
rating: rating,
onRatingChanged: (rating) => setState(() => this.rating = rating),
);
}
}
答案 1 :(得分:2)
这是因为为行小部件指定的高度。仅检测到该高度的手势。删除高度将缩放行以适合子项,从而允许完美地检测IconButton上的点击手势。
我认为我做了一个更好的解决方案。
评级组件代码:
int _rating = 0;
void rate(int rating) {
//Other actions based on rating such as api calls.
setState(() {
_rating = rating;
});
}
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("My Rating"),
),
body: new Center(
child: new Container(
width: 500.0,
child: new Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new GestureDetector(
child: new Icon(
Icons.star,
color: _rating >= 1 ? Colors.orange : Colors.grey,
),
onTap: () => rate(1),
),
new GestureDetector(
child: new Icon(
Icons.star,
color: _rating >= 2 ? Colors.orange : Colors.grey,
),
onTap: () => rate(2),
),
new GestureDetector(
child: new Icon(
Icons.star,
color: _rating >= 3 ? Colors.orange : Colors.grey,
),
onTap: () => rate(3),
),
new GestureDetector(
child: new Icon(
Icons.star,
color: _rating >= 4 ? Colors.orange : Colors.grey,
),
onTap: () => rate(4),
),
new GestureDetector(
child: new Icon(
Icons.star,
color: _rating >= 5 ? Colors.orange : Colors.grey,
),
onTap: () => rate(5),
)
],
),
),
),
);
}
如果您希望在问题中使用代码,那么 替换:
child: new Container(
height: 10.0,
width: 500.0,
child: new Row(
mainAxisAlignment: MainAxisAlignment.center,
使用:
child: new Container(
width: 500.0,
child: new Row(
mainAxisAlignment: MainAxisAlignment.center,
希望这有帮助!
答案 2 :(得分:2)
另一个很好的选择是使用该库: flutter_rating_bar 。它实现良好,优雅且易于配置。
https://pub.dev/packages/flutter_rating_bar
示例:
RatingBar(
initialRating: 3,
minRating: 1,
direction: Axis.horizontal,
allowHalfRating: true,
itemCount: 5,
itemPadding: EdgeInsets.symmetric(horizontal: 4.0),
itemBuilder: (context, _) => Icon(
Icons.star,
color: Colors.amber,
),
onRatingUpdate: (rating) {
print(rating);
},
);
答案 3 :(得分:0)
这是我的
List<bool> ratingStarStatus = <bool>[
true,
false,
false,
false,
false
];
int rating=1;
Widget addImage(String icon){
return new Image.asset(
icon,
height: 25.0,
width: 25.0,
);
}
Widget addRatingImage(int index){
return new InkWell(
onTap: (){
rating=index+1;
reDrawRating(index);
},
child: addImage(ratingStarStatus[index]?Constant.iconStarEnable: Constant.iconStarDisable)
);
}
void reDrawRating(int index){
for(int i=0;i<5;i++){
if(i<=index){
setState(() {
ratingStarStatus[i]=true;
});
}else{
setState(() {
ratingStarStatus[i]=false;
});
}
}
}
Widget addRatingView(){
var stars = <Widget>[];
for (var i = 0; i < 5; i++) {
var star = addRatingImage(i);
var padding = new Padding(padding: const EdgeInsets.fromLTRB(0.0, 0.0, 12.0, 0.0));
stars.add(star);
stars.add(padding);
}
return new Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: stars,
);
}
调用 addRatingView 函数以附加评分视图
答案 4 :(得分:0)
现在,如果您想使用插件。 您可以从flutter的官方插件中使用 flutter_rating 。
答案 5 :(得分:0)
扑通为评级提供了官方 smooth_star_rating。 选中此-> https://pub.dev/packages/smooth_star_rating
完整示例。
import 'package:flutter/material.dart';
import 'package:smooth_star_rating/smooth_star_rating.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
var rating = 0.0;
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Rating bar demo',
theme: ThemeData(
primarySwatch: Colors.green,
),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: SmoothStarRating(
rating: rating,
size: 45,
starCount: 5,
onRatingChanged: (value) {
setState(() {
rating = value;
});
},
)),
),
);
}
}
答案 6 :(得分:-1)
class RatingButtons extends StatefulWidget {
const RatingButtons({
Key key,
@required this.feedbackIcons,
}) : super(key: key);
final List<Widget> feedbackIcons;
//Here you can create a list of icons for rating or
//you can pass the icon list while using this widget
@override
_RatingButtonsState createState() => _RatingButtonsState();
}
class _RatingButtonsState extends State<RatingButtons> {
int rating;
@override
Widget build(BuildContext context) {
return Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [
...widget.feedbackIcons.map((icon) {
return GestureDetector(
child: rating == widget.feedbackIcons.indexOf(icon)
? CircleAvatar(
radius: 30,
backgroundColor: kThemeColor,
child: icon,
)
: icon,
onTap: () {
setState(() {
rating = widget.feedbackIcons.indexOf(icon);
});
},
);
}).toList()
]);
}
}