【Flutter 元件集錄】AnimatedContainer | 8 月更文挑戰

張風捷特烈發表於2021-08-15
前言:

這是我參與8月更文挑戰的第 15 天,活動詳情檢視:8月更文挑戰。為應掘金的八月更文挑戰,我準備在本月挑選 31 個以前沒有介紹過的元件,進行全面分析和屬性介紹。這些文章將來會作為 Flutter 元件集錄 的重要素材。希望可以堅持下去,你的支援將是我最大的動力~

本系列元件文章列表
1.NotificationListener2.Dismissible3.Switch
4.Scrollbar5.ClipPath6.CupertinoActivityIndicator
7.Opacity8.FadeTransition9. AnimatedOpacity
10. FadeInImage11. Offstage12. TickerMode
13. Visibility14. Padding15. AnimatedContainer[本文]

一、認識 AnimatedContainer 元件

Container 元件 一文中,我們知道它存在的價值是:整合如下 8 個單子元件,這樣通過一個 Container 完成多種功能。可以避免在使用是層層巢狀,簡化程式碼結構。而 AnimatedContainer 就相當於一個加強版的 Container ,可以讓其中的各個屬性在變化時具有動畫效果


1.AnimatedContainer 基本資訊

AnimatedContainer 是一個隱式動畫元件,和之前介紹過的 AnimatedOpacity 是同類,這種動畫元件,只需要更改屬性 + 重新構建 就可以實現動畫。這類的動畫元件,都有一些共同的屬性,如 duration 表示動畫時長、curve 表示動畫曲線、onEnd 表示動畫結束的回撥。

下面可以對比一下 AnimatedContainerContainer 的屬性,可以看出基本是一模一樣的。也就是說了解 Container 屬性的用法之後,其實 AnimatedContainer 自然也就會了。兩者的區別只在於:Container 在屬性變化重構後,是直接變換,而 AnimatedContainer 是動畫漸變。


2. AnimatedContainer 元件的使用

我們先通過一個簡單的例子看一下 AnimatedContainer 的使用方式。如下,當點選按鈕時,通過 _changeSize 方法改變寬高數值,並重建元件。可以看出 AnimatedContainer 尺寸變化會有動畫效果。想一下,如果這裡使用 Container 元件,那麼將會直接改變到對應數值,就比較生硬。

class AnimatedContainerDemo extends StatefulWidget {
  @override
  _AnimatedContainerDemoState createState() => _AnimatedContainerDemoState();
}

class _AnimatedContainerDemoState extends State<AnimatedContainerDemo> {
  double _width = 180;
  double _height = 120;

  @override
  Widget build(BuildContext context) {
    return Wrap(
      direction: Axis.vertical,
      crossAxisAlignment: WrapCrossAlignment.center,
      children: <Widget>[
        ElevatedButton(
          child: const Text('更新寬高'),
          onPressed: _changeSize,
        ),
        const SizedBox(height: 10,),
        buildAnimatedContainer()
      ],
    );
  }

  Widget buildAnimatedContainer() => AnimatedContainer(
        duration: const Duration(seconds: 1),
        curve: Curves.fastOutSlowIn,
        height: _height,
        color: Colors.black12,
        width: _width,
        onEnd: onEnd,
      );

  void onEnd() {
    print('End');
  }
  
  void _changeSize() {
    setState(() {
      _width = _width == 100 ? 180 : 100;
      _height = _height == 100 ? 120 : 100;
    });
  }
}
複製程式碼

3.AnimatedContainer 元件的其他屬性

AnimatedContainer 的其他某些屬性也可以進行動畫,比如下面的 alignmentcolor 。和寬高一樣,只需要改變屬性值重構元件,就能進行動畫。不需要處理動畫器,這就是隱式動畫的方便之處。

Alignment _alignment = Alignment.topLeft;
Color _color = Colors.orange;

Widget buildAnimatedContainer() => AnimatedContainer(
      duration: const Duration(seconds: 1),
      curve: Curves.fastOutSlowIn,
      height: 100,
      color: _color.withOpacity(0.2),
      width: 100,
      alignment: _alignment,
      onEnd: onEnd,
      child: FlutterLogo(),
    );

void _change() {
  setState(() {
    _alignment =
        _alignment == Alignment.center ? 
      Alignment.topLeft : Alignment.center;
    
    _color = _color == Colors.orange ? 
      Colors.purple : Colors.orange;
  });
}
複製程式碼

我們也可以對 decoration 屬性進行動畫,如下的圓角和陰影裝飾,也是隻需要改變屬性值重構元件 即可。

總的來說 AnimatedContainer 的屬性意義和 Container 一模一樣,只是前者在屬性變化重構時會進行動畫過渡到該值。這樣會使變化顯得不那麼突兀,視覺上更好些。其他的屬性參考 Container 元件 一文,大家可以自己試試改變這些屬性的效果。


二、AnimatedContainer 的原始碼實現

AnimatedContainer 繼承自 ImplicitlyAnimatedWidget ,這也就說明它和 AnimatedOpacity 是同族的,所以他們在原始碼實現上是類似的。


不同點在於AnimatedOpacity 是通過 FadeTransition 元件實現的動畫漸變功能,該元件天生可以監聽 Listenable 物件觸發重繪。而 _AnimatedContainerState 繼承自 AnimatedWidgetBaseState

AnimatedWidgetBaseState 中會監聽動畫控制器 controller ,通過 setState 對子元件進行 區域性更新。其原理和 AnimatedBuilder 是一致的。


從狀態類中維護的 XXXTween 可以看出能夠進行動畫的屬性有哪些。從元件的構建中可以看出,本身還是使用了 Container 實現的。由於之前監聽了動畫,並執行setState ,那麼在動畫的每一幀,都會更新 Container 的屬性配置資訊,進行區域性更新


最後,動畫的開啟和 AnimatedOpacity 一文中介紹的一樣,比較他們都是 ImplicitlyAnimatedWidget 。動畫器的維護都封裝在 ImplicitlyAnimatedWidgetState 中:

StatefulWidget重新構建時,狀態類是不會重新初始化的。而是觸發 State#didUpdateWidget 來通知 Widget 配置的變更,在此可以處理一下元件更新的邏輯。可以看出,如下在 didUpdateWidget 中,會對配置進行對比,發生變化將會更新。最後 _controller 會執行 forward 進行動畫。

AnimatedContainer 的使用方式到這裡就介紹完畢,那本文到這裡就結束了,謝謝觀看,明天見~

相關文章