【问题标题】:How to create rating star bar properly?如何正确创建评分星条?
【发布时间】:2017-10-09 01:06:55
【问题描述】:

我正在尝试为用户评分创建一个星条,下图来自 Play 商店以说明我要实现的目标:

我已经能够实现以下功能,正如您所看到的那样,但是当我查看我的代码时,我觉得必须有一个更聪明的方法来做到这一点,而真正令人讨厌的部分是@987654325 @hit area 稍微上移了一点,所以当你触摸实际的星星时,它不会注册为触摸事件(即:你必须瞄准高于按钮的位置才能注册你的触摸,这导致糟糕的用户体验)你可以通过在我点击任何星星时留意飞溅效果来检查我的意思:

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,),
              ],

          ),
        ),
      ),
    );
  }

那么这里有什么更好的方法吗?

【问题讨论】:

    标签: dart flutter


    【解决方案1】:

    太多的重复和填充!呵呵

    不管怎样,我就是这样做的。简单,可重复使用。 您可以在有点击和无点击的情况下使用它(无点击禁用波纹效果)。 半星也是。如果没有指定颜色,则使用原色填充星星。

    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),
        );
      }
    }
    

    【讨论】:

    • 我不确定应该将onRatingChanged: 设置为什么,以便通过单击启用评级?
    • 大多数时候,(rating) =&gt; setState(() =&gt; this.rating = rating)) 就足够了
    • 你能澄清一下吗?不是onRatingChanged 应该设置为类似onRatingChanged:(RatingChangeCallback)=&gt;....
    • 在原帖上加了一个例子
    • 有人知道如何在 Cupertino 中实现这个吗?因为 InkResponse 取决于 Material。谢谢
    【解决方案2】:

    pub.dev 上有一个包可以轻松做到这一点。

    检查这个 - 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;
            });
          },
          )),
        ),
      );
     }
    }
    

    演示

    【讨论】:

    • @bhargav-rao 我不明白你为什么删除我的答案。我知道它是一个库,但是有一个示例可以帮助您如何使用它。在删除其他帖子之前,请仔细考虑您的行为。
    • 那么您如何将用户选择的星级作为实际评论发送到店面?如果您不能使用星星,则允许用户选择星星是没有意义的。
    • @Bisclavret 当用户选择特定评级时,评级的值会更新。 (onRatingChanged:)。然后您可以将用户选择的评级(评级)发送到任何页面或您的数据库。如果你写一个关于评级的打印声明 (print(rating)),你可以很容易地看到评级发生了变化。谢谢。
    • 我不怀疑结果是正确的,但是以店面可以识别和接受的格式实际将其发送到店面的代码在哪里?
    • 不是Flutter官方给出的。
    【解决方案3】:

    另一个很好的选择是使用这个库: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);
       },
    );
    

    【讨论】:

      【解决方案4】:

      这是因为为行小部件指定的高度。仅检测到该高度的手势。删除高度将缩放行以适合子项,从而可以完美地检测到 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,
      

      希望这有帮助!

      【讨论】:

      • 如何将 rate() 方法内联到 onTap: () => setState(() => _rating = 1)?
      • 内联率方法是一个好主意,如果没有基于评级触发的动作。我觉得内联评级可能会导致 5 次编辑,以防根据评级触发任何操作。在 rate 方法中添加了注释,希望这是有道理的。
      【解决方案5】:

      A cool way of rating

      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()
          ]);
        }
      }
      

      【讨论】:

        【解决方案6】:

        这是我的

        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函数附加评分视图

        【讨论】:

          【解决方案7】:

          如果有人也想显示文本,那么您可以使用这段代码。 该代码适用于 double 或 int 值,我从这里smoothStarRating 将其与 SmoothStarRating 小部件一起使用。这很好:) 如果觉得有用,请点赞这个答案。

          Text(
              rate == 5.0
                  ? 'Its 5'
                  : ((rate < 5.0) & (rate > 3.0))
                      ? 'Its 4'
                      : ((rate < 4.0) & (rate > 2.0))
                          ? 'Its 3'
                          : ((rate < 3.0) & (rate > 1.0))
                              ? 'Its 2'
                              : ((rate < 2.0) & (rate > 0.0))
                                  ? 'Its 1'
                                  : 'Its0',
          ),
          

          【讨论】:

            【解决方案8】:

            我尝试修改教程以创建交互式星级。以下是我使用循环和 onPressed 不需要 InkWell 的方法。

            `class RatingsWidget extends StatefulWidget {
              @override
              _RatingsWidgetState createState() => _RatingsWidgetState();
            }
            
            class _RatingsWidgetState extends State<RatingsWidget> {
              int _rating = 0;
            
              @override
              Widget build(BuildContext context) {
                List<Widget> array = [];
                var filled = Colors.blue;
                var empty = Colors.black;
                for (var i = 1; i <= 5; i++) {
                  array.add(IconButton(
                    icon: Icon(Icons.star),
                    color: (_rating < i ? empty : filled),
                    onPressed: () {
                      setState(() {
                        _rating = i;
                      });
                    },
                  ));
                }
                return Row(
                  children: array,
                  mainAxisAlignment: MainAxisAlignment.center,
                );
              }
            }`
            

            【讨论】:

              猜你喜欢
              • 2016-09-23
              • 2014-01-17
              • 1970-01-01
              • 2021-09-16
              • 2018-03-21
              • 1970-01-01
              • 2023-03-13
              • 2022-11-30
              • 1970-01-01
              相关资源
              最近更新 更多