Flutter-五角星評分組件

評分展示: 我們需要根據(jù)不同的評分顯示不同的星級展示,這里我封裝了一個XFStarRating的小Widget來實現(xiàn);

1.XFStarRating效果展示

目的:實現(xiàn)功能展示的同時,提供高度的定制效果

  • rating:必傳參數(shù),告訴Widget當前的評分。
  • maxRating:可選參數(shù),最高評分,根據(jù)它來計算一個比例,默認值為10;
  • size:星星的大小,決定每一個star的大?。?/li>
  • unselectedColor:未選中星星的顏色(該屬性是使用默認的star才有效);
  • selectedColor:選中星星的顏色(該屬性也是使用默認的star才有效);
  • unselectedImage:定制未選中的star;
  • selectedImage:定義選中時的star;
  • count:展示星星的個數(shù);

暫時實現(xiàn)上面的定制,后續(xù)有新的需求繼續(xù)添加新的功能點~


image.png

2. 實現(xiàn)思路分析

理清楚思路后,你會發(fā)現(xiàn)并不是非常復雜,主要就是兩點的展示:

  • 未選中star的展示:根據(jù)個數(shù)和傳入的unselectedImage創(chuàng)建對應個數(shù)的Widget即可;
  • 選中star的展示:
    • 計算出滿star的個數(shù),創(chuàng)建對應的Widget;
    • 計算剩余比例的評分,對最后一個Widget進行裁剪;

問題一:選擇StatelessWidget還是StatefulWidget?
考慮到后面可能會做用戶點擊進行評分或者用戶手指滑動評分的效果,所以這里選擇StatefulWidget

  • 目前還沒有添加這個功能

問題二:如何讓選中的star和未選中的star重疊顯示?

  • 非常簡單,使用Stack即可;
child: Stack(
        children: <Widget>[
          Row(children: getUnselectImage(), mainAxisSize: MainAxisSize.min,),
          Row(children: getSelectImage(), mainAxisSize: MainAxisSize.min,),
        ],
      ),

問題三:如何實現(xiàn)對選中的最后一個star進行裁剪?

  • 可以使用ClipRect定制CustomClipper進行裁剪

定義CustomClipper裁剪規(guī)則:

class MyRectClipper extends CustomClipper<Rect> {
  final double width;

  MyRectClipper({this.width});

  @override
  Rect getClip(Size size) {
    return Rect.fromLTRB(0, 0, width, size.height);
  }

  @override
  bool shouldReclip(MyRectClipper oldClipper) {
    return width != oldClipper.width;
  }
}

使用MyRectClipper進行裁剪:

ClipRect(
      clipper: MyRectClipper(width: leftRatio*widget.size),
      child: widget.selectedImage,
    );

3.XFStarRating代碼實現(xiàn)

import 'package:flutter/material.dart';

class XFStarRating extends StatefulWidget {

  final double rating;
  final double maxRating;
  final Widget unselectedImage;
  final Widget selectedImage;
  final int    count;
  final double size;
  final Color  unselectedColor;
  final Color  selectedColor;

 XFStarRating({
   @required this.rating,
   this.maxRating = 10,
   this.size = 20,
   this.unselectedColor = const Color(0xffbbbbbb),
   this.selectedColor = const Color(0xffe0aa46),
   Widget unselectedImage,
   Widget selectedImage,
   this.count = 5,
}):unselectedImage = unselectedImage ?? Icon(Icons.star,size: size,color: unselectedColor,),
  selectedImage = selectedImage ?? Icon(Icons.star,size: size,color: selectedColor,);

  @override
  _XFStarRatingState createState() => _XFStarRatingState();
}

class _XFStarRatingState extends State<XFStarRating> {
  @override
  Widget build(BuildContext context) {
    return Container(
      child: Stack(
        children: <Widget>[
          Row(children: getUnselectImage(), mainAxisSize: MainAxisSize.min,),
          Row(children: getSelectImage(), mainAxisSize: MainAxisSize.min,),
        ],
      ),
    );
  }


  List<Widget> getUnselectImage() {
    return List.generate(widget.count, (index) {
      return widget.unselectedImage;
    });
  }

  List<Widget> getSelectImage() {
    //1.計算Star個數(shù)和剩余比例等
    double oneVale = widget.maxRating / widget.count;
    int entireCount = (widget.rating / oneVale).floor();
    double leftValue = widget.rating - entireCount * oneVale;
    double leftRatio = leftValue / oneVale;

    //2.獲取Star
    List<Widget> selectedImages = [];
    for (int i = 0; i < entireCount; i++) {
      selectedImages.add(widget.selectedImage);
    }

    //3.計算
    Widget leftStar = ClipRect(
      clipper: MyRectClipper(width: leftRatio*widget.size),
      child: widget.selectedImage,
    );
    selectedImages.add(leftStar);
    return selectedImages;
  }

}

class MyRectClipper extends CustomClipper<Rect> {
  final double width;

  MyRectClipper({this.width});

  @override
  Rect getClip(Size size) {
    return Rect.fromLTRB(0, 0, width, size.height);
  }

  @override
  bool shouldReclip(MyRectClipper oldClipper) {
    return width != oldClipper.width;
  }
}

使用代碼:

 XFStarRating(rating: 3.6,maxRating: 5,size: 40,selectedColor: Colors.red,unselectedColor: Colors.grey,),

學習內(nèi)容來自Flutter從入門到實戰(zhàn)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)自 Q吹個大氣球Q 本文主要介紹了Flutter布局相關(guān)的內(nèi)容,對相關(guān)知識點進行了梳理,并從實際例子觸發(fā),進一步...
    chilim閱讀 2,006評論 0 17
  • 本文主要介紹了Flutter布局相關(guān)的內(nèi)容,對相關(guān)知識點進行了梳理,并從實際例子觸發(fā),進一步講解該如何去進行布局。...
    Q吹個大氣球Q閱讀 10,207評論 6 51
  • 1. 簡介 在介紹Flutter布局之前,我們得先了解Flutter中的一些布局相關(guān)的特性。 1.1 邊界約束(b...
    24K純城閱讀 557評論 0 0
  • 1.前言 本文涵蓋了Widget,State,BuildContext,InheritedWidget等術(shù)語的相關(guān)...
    巧巧的二表哥閱讀 2,717評論 0 5
  • 時代,人是會受時代的影響的,會受周邊環(huán)境周邊和人的影響,誰敢說我們時代沒有缺陷,我們處于時代之中又哪能輕易看清。 ...
    雨語宇閱讀 274評論 0 1

友情鏈接更多精彩內(nèi)容