Flutter 中的 Animations(一)

官方文檔

必要的概念和類

  • Animation 對象发笔,是 Flutter 動畫庫中的核心類娜睛,插入用于引導動畫的值
  • Animation 對象知道當前動畫的狀態(tài)(如:動畫是否開始,停止娜扇,前進或者后退)越妈,但對屏幕上顯示的內(nèi)容一無所知
  • AnimationController 對象管理著 Animation
  • CurvedAnimation 將動畫定義成非線性運動的動畫
  • Tween 在被動畫對象使用的數(shù)據(jù)范圍之間進行插值季俩。例如,Tween 可能會定義從紅色到藍色或從 0 到 255 的插值
  • 使用 ListenersStatusListeners 來監(jiān)聽動畫狀態(tài)的變化

步驟

  • 初始化一個 AnimationController 對象

    AnimationController controller = AnimationController(duration: const Duration(milliseconds: 500), vsync: this);
    
  • 初始化一個 Animation 對象梅掠, 并將 AnimationController 作為參數(shù)傳遞進去酌住。這里的 Animation 對象是通過 Tween 對象的 animate 方法創(chuàng)建的

    Animation<int> alpha = IntTween(begin: 0, end: 255).animate(controller);
    
  • 調(diào)用 AnimationControllerforward() 方法執(zhí)行動畫

    controller.forward();
    
  • Widgetdispose() 方法中調(diào)用釋放資源

    controller.dispose();
    

案例

下面的案例是在給定的時間內(nèi)改變 widget 的寬高

  • 我們需要實時獲取 Animationvalue 來賦給 widget 的寬高
  • 要改變 widget 的寬高,那么就需要 setState(...) 來讓 widget 重繪

首先我們需要一個可以改變狀態(tài)的 widget阎抒,也就是繼承自 StatefulWidget 酪我,然后按照我們上述的步驟來,代碼如下:

class AnimateApp extends StatefulWidget {

  @override
  State<StatefulWidget> createState() {
    return _AnimateAppState();
  }
}

class _AnimateAppState extends State<AnimateApp> with SingleTickerProviderStateMixin {

  AnimationController controller;
  Animation<double> animation;
  
  @override
  void initState() {
    super.initState();
    // 創(chuàng)建 AnimationController 對象
    controller = AnimationController(
        vsync: this, duration: const Duration(milliseconds: 2000));
    // 通過 Tween 對象 創(chuàng)建 Animation 對象
    animation = Tween(begin: 50.0, end: 200.0).animate(controller)
      ..addListener(() {
        // 注意:這句不能少且叁,否則 widget 不會重繪都哭,也就看不到動畫效果
        setState(() {});
      })
    // 執(zhí)行動畫
    controller.forward();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        title: 'AnimateApp',
        theme: ThemeData(
            primaryColor: Colors.redAccent
        ),
        home: Scaffold(
            appBar: AppBar(
              title: Text('AnimateApp'),
            ),
            body: Center(
              child: Container(
                // 獲取動畫的值賦給 widget 的寬高
                width: animation.value,
                height: animation.value,
                decoration: BoxDecoration(
                    color: Colors.redAccent
                ),
              ),
            )
        )
    );
  }

  @override
  void dispose() {
    // 資源釋放
    controller.dispose();
    super.dispose();
  }
}

效果如下:

image

可以看出上面的動畫是線性運動的,我們可以通過 CurvedAnimation 來實現(xiàn)非線性運動的動畫代碼如下:

controller = AnimationController(
    vsync: this, duration: const Duration(milliseconds: 2000));
// 非線性動畫
final CurvedAnimation curve = CurvedAnimation(
    parent: controller, curve: Curves.elasticOut);
animation = Tween(begin: 50.0, end: 200.0).animate(curve)
  ..addListener(() {
    setState(() {});
  });

效果如下:

image

然后我們還可以給動畫添加狀態(tài)監(jiān)聽逞带,通過給 Animation 添加 addStatusListener(...) 來監(jiān)聽當前動畫的狀態(tài)欺矫,如:動畫是否播放完成。我們可以給上面的例子加一個狀態(tài)監(jiān)聽展氓,讓動畫無限執(zhí)行:

animation = Tween(begin: 50.0, end: 200.0).animate(curve)
      ..addListener(() {
        setState(() {});
      })
      ..addStatusListener((status) {
        if (status == AnimationStatus.completed) {
          controller.reverse();
        } else if (status == AnimationStatus.dismissed) {
          controller.forward();
        }
      });

AnimationStatus.completed 表示動畫在結束時停止的狀態(tài)穆趴,這個時候我們讓動畫反向執(zhí)行(從后往前);AnimationStatus.dismissed 表示動畫在開始時就停止的狀態(tài)遇汞,這個時候我們讓動畫正常執(zhí)行(從前往后)未妹。這樣就可以讓動畫無限執(zhí)行了。

image

Tween 還可以接受 Color 類型的參數(shù)勺疼,實現(xiàn)顏色漸變的效果教寂,下面讓 widget 的顏色從 紅色 漸變到 藍色,部分代碼如下:

controller = AnimationController(
        duration: const Duration(milliseconds: 3000), vsync: this);
    animation = ColorTween(begin: Colors.redAccent, end: Colors.blue).animate(
        controller)
      ..addListener(() {
        setState(() {});
      });
    controller.forward();
    
...

child: Container(
        decoration: BoxDecoration(
            color: animation.value
        ),
        margin: EdgeInsets.symmetric(vertical: 10.0),
        height: 200.0,
        width: 200.0,
      ),

效果如下:


image

小提示

with

我們可以看到 _AnimateAppState 類后面跟了一個 with SingleTickerProviderStateMixin执庐,這個 with 什么意思呢 酪耕? withDart 中的一個關鍵字,可以把它理解為 混入(mixin) 轨淌∮厮福可以看 stackoverflow 上的這個回答。

混入(mixin) 指的是可以將一個或多個類的功能添加到自己的類中递鹉,而無需繼承這些類盟步。混入后可以調(diào)用這些類中的方法躏结。Dart 中沒有多繼承却盘,可以使用 混入(mixin)來避免多重繼承會導致的問題。

上述代碼中 _AnimateAppState 類 繼承了 State 類,但是初始化 AnimationController 的時候需要一個 TickerProvider 類型的 vsync 參數(shù)黄橘,所以我們 混入TickerProvider 的子類 SingleTickerProviderStateMixin

看一個簡單的混入的小例子

void main() {
  var a = new A();
  a.methodB();
  a.methodC();
  a.methodD();

  new E(a);
}

class A extends B with C, D {
}

class B {
  void methodB() {
    print('Class B methodB is call');
  }
}

class C {
  void methodC() {
    print('Class C methodC is call');
  }
}

class D {
  void methodD() {
    print('Class D methodD is call');
  }
}

class E {
  final C c;

  E(this.c) {
    c.methodC();
  }
}

可以看出類 A 繼承了類 B兆览,然后 混入 了類 C 和類 D,然后在 main 方法中可以使用類 A 的實例去調(diào)用 類 B, C, D 中的方法塞关。然后還有類 E抬探,構造方法中需要一個類 C 作為參數(shù),然后因為類 A 混入了類 C帆赢,所以可以把類 A 的實例當作參數(shù)傳入到類 E 的構造方法中小压。

運行輸出如下:

Class B methodB is call
Class C methodC is call
Class D methodD is call
Class C methodC is call

..addListener

在上面的例子中,我們看到了這樣的寫法

animation = Tween(begin: 50.0, end: 200.0).animate(curve)
  ..addListener(() {
    setState(() {});
  });

注意到 addListener 前面的兩個點號 ..了吧椰于,什么意思呢怠益?直接看個小例子吧!

void main() {
  List<String> list = getList()
    ..add("android")
    ..add("flutter")
    ..add("kotlin")
    ..removeAt(0);

  list.forEach((item) {
    print(item);
  });

  // ----------等同于

  print('---------------------------');

  List<String> list2 = getList();
  list2.add("android");
  list2.add("flutter");
  list2.add("kotlin");
  list2.removeAt(0);

  list2.forEach((item) {
    print(item);
  });
}

List<String> getList() {
  return new List();
}

輸入如下:

flutter
kotlin
---------------------------
flutter
kotlin

如有錯誤廉羔,還請指出溉痢,謝謝!

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末憋他,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子髓削,更是在濱河造成了極大的恐慌竹挡,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件立膛,死亡現(xiàn)場離奇詭異揪罕,居然都是意外死亡,警方通過查閱死者的電腦和手機宝泵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進店門好啰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人儿奶,你說我怎么就攤上這事框往。” “怎么了闯捎?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵椰弊,是天一觀的道長。 經(jīng)常有香客問我瓤鼻,道長秉版,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任茬祷,我火速辦了婚禮清焕,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己秸妥,他們只是感情好滚停,可當我...
    茶點故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著筛峭,像睡著了一般铐刘。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上影晓,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天镰吵,我揣著相機與錄音,去河邊找鬼挂签。 笑死疤祭,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的饵婆。 我是一名探鬼主播勺馆,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼侨核!你這毒婦竟也來了草穆?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤搓译,失蹤者是張志新(化名)和其女友劉穎悲柱,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體些己,經(jīng)...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡豌鸡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了段标。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片涯冠。...
    茶點故事閱讀 39,926評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖逼庞,靈堂內(nèi)的尸體忽然破棺而出蛇更,到底是詐尸還是另有隱情,我是刑警寧澤往堡,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布械荷,位于F島的核電站,受9級特大地震影響虑灰,放射性物質(zhì)發(fā)生泄漏吨瞎。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一穆咐、第九天 我趴在偏房一處隱蔽的房頂上張望颤诀。 院中可真熱鬧字旭,春花似錦、人聲如沸崖叫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽心傀。三九已至屈暗,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間脂男,已是汗流浹背养叛。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留宰翅,地道東北人弃甥。 一個月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像汁讼,于是被迫代替她去往敵國和親淆攻。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,871評論 2 354

推薦閱讀更多精彩內(nèi)容