Flutter 隱式動畫

Flutter程式設計開發發表於2020-06-21

Flutter 隱式動畫

Flutter 隱式動畫

官方文件: flutter.dev/docs/codela…

一、什麼是隱式動畫

我們平時使用的 AnimatonController 來控制的動畫,需要我們指定動畫的執行時間,動畫的運動曲線,並手動控制動畫的開始和結束,這其實是顯式動畫,與之對應的就是隱式動畫。顧名思義,隱式動畫其實不需要我們對動畫做太多的干預,直接使用 Flutter 內部定義好的動畫元件,就可以實現簡單的動畫效果。

常見的隱式動畫元件有 AnimatedOpacity , AnimatedContainer, AnimatedPadding, AnimatedPositioned, AnimatedSwitcher 以及 AnimatedAlign 等,通過這些元件的名字也能看出來這些元件的作用,接下來簡單介紹一下如何使用隱式動畫元件。

二、AnimatedOpacity 實現漸隱效果

1、AnimatedOpacity 介紹

AnimatedOpacity 這個 Widget 可以實現透明度變化的動畫效果。

先看一下 AnimatedOpacity 的建構函式。

  const AnimatedOpacity({
    Key key,
    this.child,
    @required this.opacity,
    Curve curve = Curves.linear,
    @required Duration duration,
    VoidCallback onEnd,
    this.alwaysIncludeSemantics = false,
  }) : assert(opacity != null && opacity >= 0.0 && opacity <= 1.0),
       super(key: key, curve: curve, duration: duration, onEnd: onEnd);
複製程式碼

這些引數基本上看一眼就知道是幹嘛的了。

  • child : 子控制元件,也就是動畫的作用物件
  • opacity : 指定透明度
  • curve: 動畫的變換曲線,預設是線性變換
  • duration: 動畫時間
  • onEnd : 結束回撥

可以看得出,和我們平時正常使用 Widget 其實沒有任何區別,但是卻大大簡化的動畫的使用。

2、示例

class FadeInDemo extends StatefulWidget {
  _FadeInDemoState createState() => _FadeInDemoState();
}

class _FadeInDemoState extends State<FadeInDemo> {
  double opacity = 0.0;
  @override
  Widget build(BuildContext context) {
    return Column(
        crossAxisAlignment: CrossAxisAlignment.center,
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
      Image.network(
        'https://picsum.photos/250?image=9',
      ),
      MaterialButton(
        child: Text(
          'Show Details',
          style: TextStyle(color: Colors.blueAccent),
        ),
        onPressed: () => setState(() {
          opacity = 1;
        }),
      ),
      AnimatedOpacity(
        duration: Duration(seconds: 3),
        opacity: opacity,
        child: Column(
          children: <Widget>[
            Text('Type: Owl'),
            Text('Age: 39'),
            Text('Employment: None'),
          ],
        ),
      )
    ]);
  }
}

複製程式碼

使用 AnimatedOpcacity 需要指定初始的透明度值, 0 為不可見,當點選按鈕,設定為 1,同時 setState 將會觸發動畫效果。duration 指定了動畫時間為 3s 。

效果:

Flutter 隱式動畫

三、 AnimatedContainer 實現多屬性改變動畫

1、AnimatedContainer 介紹

AnimatedContainer 這個 Widget 和 Container 一樣,是同一個可以控制多個屬性(如 margin、borderRaidu、color)的 widget ,同時還能加上動畫。

建構函式如下:

  AnimatedContainer({
    Key key,
    this.alignment,
    this.padding,
    Color color,
    Decoration decoration,
    this.foregroundDecoration,
    double width,
    double height,
    BoxConstraints constraints,
    this.margin,
    this.transform,
    this.child,
    Curve curve = Curves.linear,
    @required Duration duration,
    VoidCallback onEnd,
  }) : assert(margin == null || margin.isNonNegative),
       assert(padding == null || padding.isNonNegative),
       assert(decoration == null || decoration.debugAssertIsValid()),
       assert(constraints == null || constraints.debugAssertIsValid()),
       assert(color == null || decoration == null,
         'Cannot provide both a color and a decoration\n'
         'The color argument is just a shorthand for "decoration: BoxDecoration(color: color)".'
       ),
       decoration = decoration ?? (color != null ? BoxDecoration(color: color) : null),
       constraints =
        (width != null || height != null)
          ? constraints?.tighten(width: width, height: height)
            ?? BoxConstraints.tightFor(width: width, height: height)
          : constraints,
       super(key: key, curve: curve, duration: duration, onEnd: onEnd);
複製程式碼

和正常的 Container 使用沒有什麼區別,就是加上了動畫相關的幾個屬性。

2、示例


const _duration = Duration(milliseconds: 400);

double randomBorderRadius() {
  return Random().nextDouble() * 64;
}

double randomMargin() {
  return Random().nextDouble() * 64;
}

Color randomColor() {
  return Color(0xFFFFFFFF & Random().nextInt(0xFFFFFFFF));
}

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

class _AnimatedContainerDemoState extends State<AnimatedContainerDemo> {
  Color color;
  double borderRadius;
  double margin;

  @override
  void initState() {
    super.initState();
    color = Colors.deepPurple;
    borderRadius = randomBorderRadius();
    margin = randomMargin();
  }

  void change() {
    setState(() {
      color = randomColor();
      borderRadius = randomBorderRadius();
      margin = randomMargin();
    });
  }
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            SizedBox(
              width: 128,
              height: 128,
              child: AnimatedContainer(
                margin: EdgeInsets.all(margin),
                decoration: BoxDecoration(
                  color: color,
                  borderRadius: BorderRadius.circular(borderRadius),
                ),
                duration: _duration,
                curve: Curves.easeInOutBack,
              ),
            ),
            MaterialButton(
              color: Theme.of(context).primaryColor,
              child: Text(
                'change',
                style: TextStyle(color: Colors.white),
              ),
              onPressed: () => change(),
            ),
          ],
        ),
      ),
    );
  }
}

複製程式碼

上面示例的程式碼可以動態的改變 Container 的 margin,borderRaidus 以及 color 屬性。 效果:

Flutter 隱式動畫

四、AnimatedSwitcher 實現切換動畫效果

AnimatedSwitcher 的詳細的介紹和原理可以參考 Flutter 中文網—通用“動畫切換”元件

1、AnimatedSwitcher 介紹

AnimatedSwitcher 可以同時對其新、舊子元素新增顯示、隱藏動畫。也就是說在AnimatedSwitcher的子元素髮生變化時,會對其舊元素和新元素。 其建構函式如下:

const AnimatedSwitcher({
  Key key,
  this.child,
  @required this.duration, // 新child顯示動畫時長
  this.reverseDuration,// 舊child隱藏的動畫時長
  this.switchInCurve = Curves.linear, // 新child顯示的動畫曲線
  this.switchOutCurve = Curves.linear,// 舊child隱藏的動畫曲線
  this.transitionBuilder = AnimatedSwitcher.defaultTransitionBuilder, // 動畫構建器
  this.layoutBuilder = AnimatedSwitcher.defaultLayoutBuilder, //佈局構建器
})
複製程式碼

當AnimatedSwitcher的child發生變化時(型別或Key不同),舊child會執行隱藏動畫,新child會執行執行顯示動畫。究竟執行何種動畫效果則由transitionBuilder引數決定,該引數接受一個AnimatedSwitcherTransitionBuilder型別的builder,定義如下:

typedef AnimatedSwitcherTransitionBuilder =
  Widget Function(Widget child, Animation<double> animation);
複製程式碼

該builder在AnimatedSwitcher的child切換時會分別對新、舊child繫結動畫:

對舊child,繫結的動畫會反向執行(reverse) 對新child,繫結的動畫會正向指向(forward) 這樣一下,便實現了對新、舊child的動畫繫結。AnimatedSwitcher的預設值是AnimatedSwitcher.defaultTransitionBuilder :

Widget defaultTransitionBuilder(Widget child, Animation<double> animation) {
  return FadeTransition(
    opacity: animation,
    child: child,
  );
}
複製程式碼

可以看到,返回了FadeTransition物件,也就是說預設情況,AnimatedSwitcher會對新舊child執行“漸隱”和“漸顯”動畫。

2、示例


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

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

class _AnimatedSwitcherCounterRouteState extends State<AnimatedSwitcherCounterRoute> {
  int _count = 0;

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          AnimatedSwitcher(
            duration: const Duration(milliseconds: 500),
            transitionBuilder: (Widget child, Animation<double> animation) {

              ///縮放動畫
              return ScaleTransition(child: child, scale: animation);

              ///上下左右平移動畫
//              return SlideTransitionX(
//                child: child,
//                direction: AxisDirection.up, //上入下出
//                position: animation,
//              );

            },
            child: Text(
              '$_count',
              //顯示指定key,不同的key會被認為是不同的Text,這樣才能執行動畫
              key: ValueKey<int>(_count),
              style: Theme.of(context).textTheme.display1,
            ),
          ),


          RaisedButton(
            child: const Text('+1',),
            onPressed: () {
              setState(() {
                _count += 1;
              });
            },
          ),


        ],
      ),
    );
  }
}
複製程式碼

上面的 transitionBuilder 通過指定不同的返回值型別,可以控制不同的動畫效果。

縮放:

Flutter 隱式動畫

平移:

Flutter 隱式動畫


github

推薦閱讀

Flutter 動畫簡易教程

Flutter 3D 動畫

Flutter Path 基礎

Flutter 進階


歡迎關注「Flutter 程式設計開發」微信公眾號 。

Flutter 隱式動畫

相關文章