首頁 > 軟體

Flutter使用AnimatedBuilder實現動效複用

2022-04-02 13:00:39

前言

我們之前講述了動畫構建的兩種方式,Animation 和 AnimationWidget,這兩種構建動畫都是將元件和動畫一起完成的。有些時候,我們只是想動效複用,而不關心元件構建,這個時候就可以使用 AnimatedBuilder 了。

AnimatedBuilder 介紹

根據官方檔案說明,AnimatedBuilder 的使用要點如下:

  • An AnimatedBuilder understands how to render the transition. —— AnimatedBuilder 知道如何渲染轉場動效。
  • An AnimatedBuilder doesn’t know how to render the widget, nor does it manage the Animation object. —— AnimatedBuilder 不知道(或者準確說不應)如何渲染元件,也不管理元件物件。
  • Use AnimatedBuilder to describe an animation as part of a build method for another widget. If you simply want to define a widget with a reusable animation, use an AnimatedWidget. —— 使用 AnimatedBuilder 作為其他元件的動效描述。如果只是想複用一個帶有動效的元件,那麼應該使用 AnimatedWidget。這個可以看我們之前關於 AnimatedWidget 的介紹:Flutter 入門與實戰(九十四):讓你的元件擁有三維動效
  • Examples of AnimatedBuilders in the Flutter API: BottomSheetExpansionTilePopupMenuProgressIndicatorRefreshIndicatorScaffoldSnackBarTabBarTextField. —— 在 Flutter 中,有很多元件使用 AnimatedBuilder 構建動效。

這段話的核心要點就是 AnimatedBuilder 應該只負責動畫效果的管理,而不應該管理元件構建。如果混在一起使用,就失去設計者的初衷了。這就好比我們的狀態管理和介面一樣,一個負責業務邏輯,一個負責介面渲染,從而實現解耦和複用。這個AnimatedBuilder就是專門複製動效管理的,並且應當努力實現複用。AnimatedBuilder的定義如下:

const AnimatedBuilder({
    Key? key,
    required Listenable animation,
    required this.builder,
    this.child,
  }) : assert(animation != null),
       assert(builder != null),
       super(key: key, listenable: animation);

其中關鍵的引數是builderbuilder 用於構建元件的轉變動作,在 builder 裡可以對要渲染的子元件進行轉變操作,然後返回變換後的元件。builder 的定義如下,其中 child 實際就是 AnimatedBuilder 的 child 引數,可以根據需要是否使用。

Widget Function(BuildContext context, Widget? child)

Transform 元件介紹

在 Flutter 中,提供了一個專門用於對子元件進行轉換操作的,定義如下:

const Transform({
    Key? key,
    required this.transform,
    this.origin,
    this.alignment,
    this.transformHitTests = true,
    Widget? child,
  }) : assert(transform != null),
       super(key: key, child: child);

這裡的引數說明如下:

  • transform 是一個Matrix4 物件,用於定義三維空間的變換操作。
  • origin 是一個座標偏移量,實際會加入到 Matrix4 的 translation(平移)中。
  • alignment:即轉變進行的參考方位。
  • child:被轉換的子元件。

我們可以通過 Transform,實現 AnimatedBuilder 的動效管理,也就是在 AnimatedBuilder 中,通過構建 Transform 物件實現動效。

應用

基本概念講清楚了(敲黑板:很多時候大家都是直接簡單看一下檔案就開始用,甚至乾脆複製範例程式碼就上,結果很可能會用得不對),可以開始擼程式碼了。我們來實現下面的動效。

這裡其實是兩個元件,通過 AnimatedBuilder 做了動效轉換。在動效的一半時間是文字“點選按鈕變出小姐姐”,之後的一半將元件更換為了小姐姐的圖片了。使用AnimatedBuilder 的實現程式碼如下:

class RotationSwitchAnimatedBuilder extends StatelessWidget {
  final Widget child1, child2;
  final Animation<double> animation;
  const RotationSwitchAnimatedBuilder(
      {Key? key,
      required this.animation,
      required this.child1,
      required this.child2})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: animation,
      builder: (context, child) {
        if (animation.value < 0.5) {
          return Transform(
            transform: Matrix4.identity()
              ..rotateZ(animation.value * pi)
              ..setEntry(0, 1, -0.003),
            alignment: Alignment.center,
            child: child1,
          );
        } else {
          return Transform(
            transform: Matrix4.identity()
              ..rotateZ(pi)
              ..rotateZ(animation.value * pi)
              ..setEntry(1, 0, 0.003),
            child: child2,
            alignment: Alignment.center,
          );
        }
      },
    );
  }
}

注意第2個元件多轉了180度,是未來保證停止後正好旋轉360度,以免圖片倒過來。另外就是這裡的 child1和 child2也可以修改為使用 WidgetBuilder 函數來在需要的時候再構建元件。使用這個RotationSwitchAnimatedBuilder的元件就十分簡單了,將需要操作的兩個元件作為引數傳過來,然後控制 Animation 物件來重新整理介面就好了,對應的程式碼如下:

class AnimatedBuilderDemo extends StatefulWidget {
  const AnimatedBuilderDemo({Key? key}) : super(key: key);

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

class _AnimatedBuilderDemoState extends State<AnimatedBuilderDemo>
    with SingleTickerProviderStateMixin {
  late Animation<double> animation;
  late AnimationController controller;

  @override
  void initState() {
    super.initState();
    controller =
        AnimationController(duration: const Duration(seconds: 1), vsync: this);
    animation = Tween<double>(begin: 0, end: 1.0).animate(controller);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('AnimatedBuilder 動畫'),
      ),
      body: RotationSwitchAnimatedBuilder(
        animation: animation,
        child1: Center(
          child: Container(
            padding: EdgeInsets.all(10),
            margin: EdgeInsets.all(10),
            constraints: BoxConstraints(minWidth: double.infinity),
            decoration: BoxDecoration(
              borderRadius: BorderRadius.circular(4.0),
              gradient: LinearGradient(
                colors: [
                  Colors.orange,
                  Colors.green,
                ],
              ),
            ),
            child: Text(
              '點選按鈕變出小姐姐',
              style: TextStyle(
                fontSize: 20,
                color: Colors.white,
                fontWeight: FontWeight.bold,
              ),
              textAlign: TextAlign.center,
            ),
          ),
        ),
        child2: Center(
          child: Image.asset('images/beauty.jpeg'),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.play_arrow, color: Colors.white),
        onPressed: () {
          if (controller.status == AnimationStatus.completed) {
            controller.reverse();
          } else {
            controller.forward();
          }
        },
      ),
    );
  }

  @override
  void dispose() {
    controller.dispose();
    super.dispose();
  }
}

複用的話也很容易了,比如我們將一個圓形和一個矩形元件傳過去,一樣可以複用這個動畫效果。

完整原始碼

總結

本篇介紹了 AnimatedBuilder 的概念和應用, Flutter 提供 AnimatedBuilder元件的核心理念是為了建立和管理可複用的動畫效果。在使用的時候,應該將動畫效果和元件構建分離,從而使得AnimatedBuilder構建的動畫效果可以在需要的時候得到複用。

以上就是Flutter使用AnimatedBuilder實現動效複用的詳細內容,更多關於Flutter動效複用的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com