美文网首页Flutter圈子
Flutter星级评选,星级,星级排行

Flutter星级评选,星级,星级排行

作者: 大眠兽 | 来源:发表于2020-03-17 20:41 被阅读0次

    星级评选,星级,星级排行

    整一个豆瓣的星级评选,效果图和原图如下

    自己做的效果.png 豆瓣评分效果.png

    一、开发步骤

    首先,明确一下如何来设计这个东西。
    评分系统,一看图上就有两种不同的星星,一个表层的带颜色的星星,一个底层的灰色星星。
    底层的灰色星星,都是满星,而表层的带颜色的星星,有半颗的。
    这时候我们就知道了,要做一个层叠在一起的两组星星,一组底层的灰色满的,一组表层的可能不是满的。
    

    1.创建一个底层用的空的星星,或者灰色的星星 (☆)
    2.创建一排底层用的星星(☆☆☆☆☆)
    3.创建一个表层用的满的带颜色的星星(★)
    4.创建半个表层用的带颜色的星星(★半颗)
    5.将他们叠在一起

    二、开始开发

    现在开始开发

    • 创建一个底层用的空的星星,或者灰色的星星 (☆)
    Icon unSelectedStar = Icon(Icons.star_border, color: Colors.red, size: 30);
    
    • 创建一排 5个 底层用的星星(☆☆☆☆☆)
    List.generate(5, (index) {
        return Icon(Icons.star_border, color: Colors.red, size: 30);
    })
    
    • 创建一个表层用的满的带颜色的星星(★)
    Icon(Icons.star, color: Colors.red, size: 30);
    
    • 创建半个表层用的带颜色的星星(★半颗)
      半颗星星需要用到剪切,剪切这里,我们需要用到一个东西 ClipRect
    ClipRect(
        clipper: StarCustomClipper(leftWidth),
        child: star,
    );
    

    clipper 我们看到要传一个 CustomClipper

    CustomClipper.png
    CustomClipper 又是一个抽象类,所以我们看看实现过他的子类都是什么东西。一看就发现,有两个是锁着的(🔐)不让我们用。那我们看一下那个没有上锁的,看字面意思,我们猜测他是根据形状来剪切。但是我们需要根据分数的不同直接吃掉右边的,相当于用一个矩形框盖住他了。那我们干脆来自己实现一个 CustomClipper
    class StarCustomClipper extends CustomClipper<Rect>{
        double width;
        StarCustomClipper(this.width);
        @override
        Rect getClip(Size size) {
            // TODO: implement getClip
            return Rect.fromLTRB(0, 0, width, size.height);
        }
        @override
        bool shouldReclip(StarCustomClipper oldClipper) {
            // TODO: implement shouldReclip
            return oldClipper.width != this.width;
        }
    }
    

    CustomClipper 子类中,我们有两个必须要实现的方法getClipshouldReclip
    getClip是用来告诉我们到底要怎么切,shouldReclip是告诉我们要不要切。
    这里我们通过传入切割的位置 Rect.fromLTRB(0, 0, width, size.height),来进行切割。这里的width就是我们切下这一半来保留。
    这时候我们就可以得到半颗星了。

    • 计算一下要排列多少个满的星星和半个的星星
    • 准备工作就做完了,这时候就把他们拼起来
      1.首先我们整个方法根据数量来创建☆
    //创建没有选中的星星
    List <Widget> buildUnseletedStar() {
        return List.generate(widget.maxStarCount, (index) {
            return unSelectedStar;
        });
    }
    

    2.然后我们整个方法根据数量来创建★

    List <Widget> buildSeletedStar() {
            List <Widget> starList = [];
            ///每颗星星多少分
            double oneStarScore = widget.maxRate / widget.maxStarCount;
            //判断几个星星
            double allCount = widget.currentRate / oneStarScore;
            //有没有半颗的
            //判断整颗
            int fulStarCount = allCount.floor();
            //判断半颗
            double partStarCount = allCount - fulStarCount;
            //有没有多的
            if (allCount >= widget.maxStarCount) {
                allCount = widget.maxStarCount;
                partStarCount = 0;
            }
            //添加整颗星星
            starList.addAll(List.generate(fulStarCount, (index){ return widget.seletedStar;}));
            //添加半颗星星
            if (partStarCount > 0) {
                //ClipRect 剪切
                starList.add(ClipRect(
                    child: widget.seletedStar,
                    clipper: LYStarClipper(partStarCount * widget.starSize),
                )
                );
            }
    
            return starList;
        }
    }
    

    3.最后我们把他们拼在一起

    Stack(
        children: [
            Row(mainAxisSize: MainAxisSize.min, children:buildUnseletedStar()),
            Row(mainAxisSize: MainAxisSize.min, children:buildSeletedStar()),
        ],
    );
    

    三、总结

    这个东西难点在怎么做半颗星星,和计算有多少个星星。同时为了保持代码的健壮性,我们要做到在传过来的分数比总分数要高的时候,要么提示开发者出错了,要么就让他显示最大数值。

    四、完整代码

    import 'package:flutter/material.dart';
    
    class LYCustomWidgtStarRating extends StatefulWidget {
        ///当前分数
        final currentRate;
    
        ///最大分数
        final maxRate;
        ///最大星星数量
        final maxStarCount;
    
        ///底部星星样式
        final Widget unseletedStar;
        ///顶部星星样式
        final   Widget seletedStar;
    
        ///星星大小
        final   double starSize;
    
        LYCustomWidgtStarRating({
            @required this.currentRate,
            this.maxRate = 10,
            this.maxStarCount = 5,
            this.starSize = 30,
            unseletedStar,
            seletedStar,
        }): seletedStar = seletedStar ?? Icon(Icons.star,color: Colors.red,size: starSize,),
                    unseletedStar = unseletedStar ?? Icon(Icons.star_border,color: Colors.red,size: starSize,);
    
        @override
      _LYCustomWidgtStarRatingState createState() => _LYCustomWidgtStarRatingState();
    }
    
    class _LYCustomWidgtStarRatingState extends State<LYCustomWidgtStarRating> {
        @override
        Widget build(BuildContext context) {
            return Stack(
                children: [
                    Row(mainAxisSize: MainAxisSize.min, children:buildUnseletedStar()),
                    Row(mainAxisSize: MainAxisSize.min, children:buildSeletedStar()),
                ]
                ,
            );
        }
    
        //创建没有选中的星星
        List <Widget> buildUnseletedStar() {
            return List.generate(widget.maxStarCount, (index) {
                return widget.unseletedStar;
            });
        }
        List <Widget> buildSeletedStar() {
            List <Widget> starList = [];
    
            ///每颗星星多少分
            double oneStarScore = widget.maxRate / widget.maxStarCount;
            //判断几个星星
            double allCount = widget.currentRate / oneStarScore;
            //有没有半颗的
            //判断整颗
            int fulStarCount = allCount.floor();
            //判断半颗
            double partStarCount = allCount - fulStarCount;
            //有没有多的
            if (allCount >= widget.maxStarCount) {
                allCount = widget.maxStarCount;
                partStarCount = 0;
            }
            //添加整颗星星
            starList.addAll(List.generate(fulStarCount, (index){ return widget.seletedStar;}));
            //添加半颗星星
            if (partStarCount > 0) {
                //ClipRect 剪切
                starList.add(ClipRect(
                    child: widget.seletedStar,
                    clipper: LYStarClipper(partStarCount * widget.starSize),
                )
                );
            }
    
            return starList;
        }
    }
    class LYStarClipper extends CustomClipper<Rect> {
        double width;
    
      LYStarClipper(this.width);
      @override
        Rect getClip(Size size) {
        // TODO: implement getClip
        return Rect.fromLTRB(0, 0, width, size.height);
      }
    
      @override
      bool shouldReclip(LYStarClipper oldClipper) {
        // TODO: implement shouldReclip
        return oldClipper.width != width;
      }
    
    }
    
    

    相关文章

      网友评论

        本文标题:Flutter星级评选,星级,星级排行

        本文链接:https://www.haomeiwen.com/subject/xbuxyhtx.html