I am trying to create a star bar for user ratings, the following image is from the play store to illustrate what I am trying to achieve:
I have been able to achieve the following, which is functional as you can see, but when I look at my code, I feel like there must be a smarter way to do it, and the really bugging part is that IconButton
hit area is being shifted up a little bit,so when you touch the actual star it does not register as a touch event (i.e: you have to aim higher than where the button is positioned in order for your touch to be registered, which makes for a bad UX) you can check what I mean by keeping an eye on the splash effect when I click on any of the stars:
var _myColorOne = Colors.grey;
var _myColorTwo = Colors.grey;
var _myColorThree = Colors.grey;
var _myColorFour = Colors.grey;
var _myColorFive = Colors.grey;
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("My Rating"),
),
body: new Center(
child: new Container(
height: 10.0,
width: 500.0,
child: new Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new IconButton(icon: new Icon(Icons.star),
onPressed: ()=>setState((){
_myColorOne=Colors.orange;
_myColorTwo=null;
_myColorThree=null;
_myColorFour=null;
_myColorFive=null;
}),color: _myColorOne,),
new IconButton(icon: new Icon(Icons.star),
onPressed: ()=>setState((){
_myColorOne=Colors.orange;
_myColorTwo=Colors.orange;
_myColorThree=Colors.grey;
_myColorFour=Colors.grey;
_myColorFive=Colors.grey;
}),color: _myColorTwo,),
new IconButton(icon: new Icon(Icons.star), onPressed: ()=>setState((){
_myColorOne=Colors.orange;
_myColorTwo=Colors.orange;
_myColorThree=Colors.orange;
_myColorFour=Colors.grey;
_myColorFive=Colors.grey;
}),color: _myColorThree,),
new IconButton(icon: new Icon(Icons.star), onPressed: ()=>setState((){
_myColorOne=Colors.orange;
_myColorTwo=Colors.orange;
_myColorThree=Colors.orange;
_myColorFour=Colors.orange;
_myColorFive=Colors.grey;
}),color: _myColorFour,),
new IconButton(icon: new Icon(Icons.star), onPressed: ()=>setState((){
_myColorOne=Colors.orange;
_myColorTwo=Colors.orange;
_myColorThree=Colors.orange;
_myColorFour=Colors.orange;
_myColorFive=Colors.orange;
}),color: _myColorFive,),
],
),
),
),
);
}
So is what is the better approach here?
Adding a RatingBar to Your Project First, drag the Column widget from the Layout Elements tab (in the Widget Panel) or add it directly from the widget tree. Set its Cross Axis Alignment to Start. icon) and Height property to 200. Add a Text widget (Inside the Column).
RatingBar is used to get the rating from the app user. A user can simply touch, drag or click on the stars to set the rating value. The value of rating always returns a floating point number which may be 1.0, 2.5, 4.5 etc. In Android, RatingBar is an extension of ProgressBar and SeekBar which shows a rating in stars.
Too much repetition and padding! duh
Anyway, that's how I'd do it. Simple, reusable. You can use it both with and without clicks (no click disable the ripple effect). Half stars too. And use primary color for filled stars if no color is specified.
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)));
}
}
You can then use it like this :
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),
);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With