Flutter | 如何實現一個水波紋擴散效果的 Widget

先來看圖:git

咱們在平常使用 APP 當中,確定會遇到這種效果,那麼這種效果是如何實現的呢?github

確認需求

首先仍是老套路,先肯定一下需求,捋一下思路,而後纔好寫代碼:bash

  1. 首先要有一個圓
  2. 這個圓會邊擴散邊消失
  3. 當這個圓擴散到必定程度的時候再繪製一個圓
  4. 有限循環 / 無限循環
  5. 能夠有 / 無 Child

捋好了思路,下面咱們來開始實現。微信

首先要有一個圓

首先有一個圓,這個圓應該怎麼畫?我想到了兩種方案:async

  1. CustomPaint
  2. ClipOver

這兩種方式都很簡單,因此我選擇了後者,由於後者更簡單(23333)。動畫

代碼我就不貼了,不過代碼我已經提交到了github.com/wanglu1209/…,能夠隨時查看。ui

這個圓會邊擴散邊消失

一邊擴散,一邊消失。this

有沒有想起來我上一篇文章提及的箭頭小Demo? --- Flutter | 經過一個小例子帶你認識動畫 Animationlua

沒錯,這裏也是使用這種 evaluate 來計算大小和透明度。spa

代碼以下:

Container(
  width: _radiusTween.evaluate(animation),
  height: _radiusTween.evaluate(animation),
  child: ClipOval(
    child: Opacity(
      opacity: _opacityTween.evaluate(animation),
      child: Container(
        color: color,
      ),
    ),
  ),
)
複製代碼

這樣,咱們只須要設置好該 Tween 的 begin 和 end 就能實現一邊擴散,一邊消失了。

當這個圓擴散到必定程度的時候再繪製一個圓

首先,咱們都知道,在 Flutter 當中,如何把一個 widget 浮在另外一個 widget 上。沒錯,用 Stack

那就要建立一個 List<Widget> 來存放咱們的剛纔定義好的「會擴散消失的圓」。

並且咱們也知道,這個「會擴散消失的圓」須要一個 Animation,那也就是說每個圓都須要一個AnimationAnimationController,那咱們也須要建立一個 List<AnimationController> 來控制每個「會擴散消失的圓」。

而且,在 AnimationStatus == completed 的時候,把該 圓移除,而且把該 controller dispose。

並且在該 Widget dispose 的時候,也應該把全部未清除的 controller 給清除掉。

大體代碼以下:

int i = 0;
while (widget.cycles == null ? true : i < widget.cycles) {
  if (mounted) {
    setState(() {
      AnimationController _animationController;
      Animation<double> _animation;

      _animationController =
        AnimationController(vsync: this, duration: widget.duration);
      _animation = CurvedAnimation(
        parent: _animationController, curve: Curves.linear);

      _animationController.addStatusListener((status) {
        if (status == AnimationStatus.completed) {
          children.removeAt(0);
          controllers.removeAt(0);
          _animationController.dispose();
        }
      });
      controllers.add(_animationController);
      _animationController.forward();

      widget.child != null
        ? children.insert(
        children.length - 1,
        AnimatedSpread(
          animation: _animation,
          radius: widget.radius,
          maxRadius: widget.maxRadius,
          color: widget.spreadColor,
        ))
        : children.add(AnimatedSpread(
          animation: _animation,
          radius: widget.radius,
          maxRadius: widget.maxRadius,
          color: widget.spreadColor,
        ));
    });
  }
  if (widget.cycles != null) i++;
  await Future.delayed(
    Duration(milliseconds: widget.duration.inMilliseconds ~/ 3));
}
複製代碼

每個 animation 是有 duration 的,那麼咱們就能夠根據該持續時間來設置何時出現第二個圓,我這裏寫的是持續時間的 1/3。

這樣看起來效果是不錯的。

有限循環 / 無限循環

在剛纔的代碼裏面其實就有這一部分的邏輯:

while (widget.cycles == null ? true : i < widget.cycles) {
  // ...
}
複製代碼

這裏主要就是控制顯示幾回,畢竟有的需求不是一直顯示波紋效果。

能夠有 / 無 Child

我這裏寫的 child 默認形狀是圓形的,大小被 SizedBox 控制爲 radius 的大小:

ClipOval(
  child: SizedBox(
    width: widget.radius,
    height: widget.radius,
    child: widget.child,
  ),
),
複製代碼

若是有 child 的話如何保證 child 永遠都是在最上面?

只須要在插入圓形的時候使用 List.insert(index, element) 方法就ok了。

這樣一個有水波紋擴散效果的 Widget 就封裝完成了。

總結

這裏我使用了和上篇文章同樣的邏輯,都是使用的 AnimatedWidget

而後用 Stack 來包裝,Future.delayed 來控制下一個圓形出現的時間。

另我我的建立了一個「Flutter 交流羣」,能夠添加我我的微信 「17610912320」來入羣。

推薦閱讀:

Flutter | 經過一個小例子帶你認識動畫 Animation

Flutter | WReorderList 一個能夠指定兩個item互換位置的組件

Dart | 你知道 sync*/async* 是怎麼用的嗎?

img
相關文章
相關標籤/搜索