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

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

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

自己做的效果.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;
  }

}

其他文章

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容