【 開源計劃 - 元件包 】 星星也可以如此閃耀 flutter_star

張風捷特烈發表於2020-02-23

pub地址 】 【github地址

dependencies:
  flutter_star: $lastVersion
複製程式碼

一、描述

目標: 使用canvas手工打造,一個完美的星星評分元件。

---->[StarScore 星星顯示元件]----
[1] 比如顯示4.2: 會有5顆星, 前四顆填滿,後一刻填充20%
StarScore 為 Stateless元件,僅負責顯示的需求

---->[CustomRating 星星評分元件]----
[2] 可指定最大值,也就是顯示多少個星星
[3] 點選時會改變狀態,進行評分,支援半星評分
[4] 支援評分回撥

---->[Star元件]----
[5]. 可定義星星的顯示進度情況 0% ~ 100 % 無死角
[6]. 可定義星星的角數
[7]. 可定義星星的顏色、大小 
複製程式碼

二 、StarScore

分數展示元件

名稱 型別 功能 備註 預設
score double 分數 - 0
star Star 第四點 星星屬性配置 Star()
tail Widget 尾部的元件 - null
StarScore(
  score: 4.8,
  star: Star(
      fillColor: Colors.tealAccent,
      emptyColor: Colors.grey.withAlpha(88)),
  tail: Column(
    children: <Widget>[
      Text("綜合評分"),
      Text("4.8"),
    ],
  ),
),
複製程式碼

【  開源計劃 - 元件包 】 星星也可以如此閃耀 flutter_star


三 、CustomRating

評分元件

名稱 型別 功能 備註 預設
max int 最大星星數 - 5
score double 分數 - 0
star Star 第四點 星星屬性配置 Star()
onRating Fluction(double) 點選回撥 @required null
1.最簡使用

【  開源計劃 - 元件包 】 星星也可以如此閃耀 flutter_star

CustomRating(onRating: (s) {
   print(s);
 }),
複製程式碼

2.可高度定製

【  開源計劃 - 元件包 】 星星也可以如此閃耀 flutter_star

CustomRating(
     max: 6,
     score: 3.0,
     star: Star(
         num: 12,
         fillColor: Colors.orangeAccent,
         fat: 0.6,
         emptyColor: Colors.grey.withAlpha(88)),
    onRating: (s) {
       print(s);
     }),
複製程式碼

四 、Star

星星元件 : 高度可定製的配置類

名稱 型別 功能 備註 預設
progress double 填充的進度 [0,1] 0.0
num int 星星的角數 大於3 5
fat double 星星的胖瘦 (0,1] 0.5
emptyColor Color 星星的色 - Colors.grey
fillColor Color 星星的填充色 - Colors.yellow
size double 星星的大小 - 20
1. 進度填充:progress

【  開源計劃 - 元件包 】 星星也可以如此閃耀 flutter_star


2. 星星的角數:num

【  開源計劃 - 元件包 】 星星也可以如此閃耀 flutter_star


3. 星星的胖瘦:fat

【  開源計劃 - 元件包 】 星星也可以如此閃耀 flutter_star


4. 星星的顏色:fillColor和emptyColor

【  開源計劃 - 元件包 】 星星也可以如此閃耀 flutter_star

展示結束,下面進入正文


一 、如何自定義繪製的元件

1.分析元件的需求,抽離出需要配置的屬性
class Star {
  final int num;
  final double progress;
  final Color emptyColor;
  final Color fillColor;
  final double size;
  final double fat;

  const Star({this.progress = 0,
    this.fat = 0.5,
      this.fillColor = Colors.yellow,
    this.emptyColor = Colors.grey,
    this.num = 5,
    this.size = 25});
}
複製程式碼

2. 建立好畫板準備開畫
class _StarPainter extends CustomPainter {
  Star star;
  Paint _paint;
  Paint _filePaint;
  Path _path;
  double _radius;

  _StarPainter(this.star) {
    _paint = Paint()
      ..color = (star.emptyColor)
      ..isAntiAlias = true;

    _filePaint = Paint()
      ..color = (star.fillColor);
    _path = Path();
    _radius = star.size / 2.0;

  }
  @override
  void paint(Canvas canvas, Size size) {
    //TODO 繪製
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return true;
  }
}
複製程式碼

3.開畫

如果說StarWidget是評分元件的基礎,那麼繪製的路徑就是星星的靈魂
下面的nStarPath是繪製n角星路徑的核心方法

  Path nStarPath(int num, double R, double r, {dx = 0, dy = 0, rotate = 0}) {
    _path.reset(); //重置路徑
    double perRad = 2 * pi / num; //每份的角度
    double radA = perRad / 2 / 2 + rotate; //a角
    double radB = 2 * pi / (num - 1) / 2 - radA / 2 + radA + rotate; //起始b角
    _path.moveTo(cos(radA) * R + dx, -sin(radA) * R + dy); //移動到起點
    for (int i = 0; i < num; i++) {
      //迴圈生成點,路徑連至
      _path.lineTo(
          cos(radA + perRad * i) * R + dx, -sin(radA + perRad * i) * R + dy);
      _path.lineTo(
          cos(radB + perRad * i) * r + dx, -sin(radB + perRad * i) * r + dy);
    }
    _path.close();
    return _path;
  }
複製程式碼

在初始化的時候為路徑賦值

class _StarPainter extends CustomPainter {
 ...
  _StarPainter(this.star) {
    ...
    _path = Path();
    _radius = star.size / 2.0;
    nStarPath(star.num, _radius, _radius * star.fat);
  }
複製程式碼

繪製也非常簡單,其中有個進度問題,可以先畫背景的星星, 再畫一個填充的星星,再用canvas.clipRect進行裁剪即可。

  @override
  void paint(Canvas canvas, Size size) {
    canvas.translate(_radius, _radius);
    canvas.drawPath(_path, _paint);
    canvas.clipRect(Rect.fromLTRB(
        -_radius, -_radius, _radius * 2 * star.progress - _radius, _radius));
    canvas.drawPath(_path, _filePaint);
  }
複製程式碼

4.自定義元件

將畫板放入CustomPaint中即可

 class StarWidget extends StatelessWidget {
  final Star star;

  StarWidget({this.star = const Star()});

  @override
  Widget build(BuildContext context) {
    return Container(
      width: star.size,
      height: star.size,
      child: CustomPaint(
        painter: _StarPainter(star),
      ),
    );
  }
}
複製程式碼

這就是星星元件的所有程式碼,也不超過百行。


二 、StarScore的實現

該元件的目的是顯示評分,可以精確的顯示百分進度
其實也沒啥,僅是用幾個StarWidget拼起來而已,程式碼也就下面一丟丟

class StarScore extends StatelessWidget {
  final Star star;
  final double score;
  final Widget tail;

  StarScore({this.star = const Star(), this.score, this.tail});

  @override
  Widget build(BuildContext context) {
    var li = <StarWidget>[];
    int count = score.floor();
    for (int i = 0; i < count; i++) {
      li.add(StarWidget(star: star.copyWith(progress: 1.0)));
    }
    if (score - count > 0) {
      li.add(StarWidget(star: star.copyWith(progress: score - count)));
    }
    return Wrap(
      crossAxisAlignment: WrapCrossAlignment.center,
      children: [
        ...li,
        SizedBox(
          width: 10,
        ),
        if (tail!=null) tail
      ],
    );
  }
}
複製程式碼

三 、StarScore的實現

由於點選需要自己響應進行狀態改變,所以使用StatefulWidget 最核心的是GestureDetector進行觸點的獲取並計算出當前值。其中對.5進行處理,以及越界的處理。

class CustomRating extends StatefulWidget {
  final int max;
  final Star star;
  final double score;
  final Function(double) onRating;

  CustomRating(
      {this.max = 5,
      this.score = 0,
      this.star = const Star(),
      @required this.onRating})
      : assert(score <= max);

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

class _CustomRatingState extends State<CustomRating> {
  double _score;

  @override
  void initState() {
    _score = widget.score;
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    var li = <StarWidget>[];

    int count = _score.floor(); //滿星
    for (int i = 0; i < count; i++) {
      li.add(StarWidget(star: widget.star.copyWith(progress: 1.0)));
    }

    if (_score != widget.max.toDouble())
      li.add(StarWidget(
          star: widget.star.copyWith(progress: _score - count))); //不滿星

    var empty = widget.max - count - 1; // 空星
    for (int i = 0; i < empty; i++) {
      li.add(StarWidget(star: widget.star.copyWith(progress: 0)));
    }
    return GestureDetector(
      onTapDown: (d) {
        setState(() {
          _score = d.localPosition.dx / widget.star.size;
          if (_score - _score.floor() > 0.5) {
            _score = _score.floor() + 1.0;
          } else {
            _score = _score.floor() + 0.5;
          }

          if (_score >= widget.max.toDouble()) {
            _score = widget.max.toDouble();
          }
          widget.onRating(_score);
        });
      },
      child: Wrap(
        children: li,
      ),
    );
  }
}
複製程式碼

四. 釋出到pub

1.配置

需要在pubspec.yaml進行一些配置

name 是名稱
description 是描述 60 ~ 180 之間,太短或太長會扣分
version 版本
author 作者資訊,會報warning 但我就想寫
homepage 主頁
複製程式碼
---->[pubspec.yaml]----
name: flutter_star
description: You can create a star easily and decide how many angle or color of the star, even the fat and progress of the star.
version: 0.1.2
author: 張風捷特烈<1981462002@qq.com>
homepage: https://juejin.im/user/5b42c0656fb9a04fe727eb37/collections
複製程式碼

2.最好寫個example

不然會扣分

【  開源計劃 - 元件包 】 星星也可以如此閃耀 flutter_star

它會在這裡,給使用者看

【  開源計劃 - 元件包 】 星星也可以如此閃耀 flutter_star


3.釋出到pub
flutter packages pub publish --server=https://pub.dartlang.org
複製程式碼

然後需要許可權驗證,記得全部複製在瀏覽器開啟,不用在控制檯點連結,由於控制檯的換行而導致url不全。

然後就看你的網給不給力了。flutter_star 歡迎使用


尾聲

另外本人有一個Flutter微信交流群,歡迎小夥伴加入,共同探討Flutter的問題,期待與你的交流與切磋。

@張風捷特烈 2019.02.23 未允禁轉
我的公眾號:程式設計之王
聯絡我--郵箱:1981462002@qq.com --微信:zdl1994328
~ END ~

相關文章