Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create rating star bar properly?

Tags:

flutter

dart

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:

enter image description here

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:

enter image description here

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?

like image 843
Shady Aziza Avatar asked Oct 09 '17 01:10

Shady Aziza


People also ask

How do I create a rating bar in flutter?

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).

How do you use rating bars?

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.


1 Answers

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.

enter image description here

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),
    );
  }
}
like image 72
Rémi Rousselet Avatar answered Oct 03 '22 00:10

Rémi Rousselet