Flutter動畫 5 - Flutter內建動畫元件

神經騷棟發表於2021-05-14


概述


前幾篇文章我們都是使用Flutter中的AnimationController、Animation以及Tween來實現我們的動畫效果,如果我們只想實現一些簡單動畫,該怎麼辦呢?今天我們就一起了解Flutter中內建的動畫元件.接下來,我們就來分類來看一下Futter內建的動畫元件.



  • 需要外部Animation支援
Animation元件功能
DecoratedBoxTransitionDecoratedBox的動畫版本,可以給它的Decoration不同屬性使用動畫
FadeTransition對透明度使用動畫的widget
PositionedTransitionPositioned的動畫版本,它需要一個特定的動畫來將孩子的位置從動畫的生命週期的起始位置移到結束位置。
RotationTransition對widget使用旋轉動畫
ScaleTransition對widget使用縮放動畫
SizeTransition對widget使用尺寸相關動畫
SlideTransition對相對於其正常位置的某個位置之間使用動畫


  • 不需要外部Animation支援
Animation元件功能
AnimatedPadding在padding發生變化時會執行過渡動畫到新狀態
AnimatedPositioned配合Stack一起使用,當定位狀態發生變化時會執行過渡動畫到新的狀態。
AnimatedOpacityOpacity的動畫版本,在給定的透明度變化時,自動地在給定的一段時間內改變child的Opacity
AnimatedAlign當alignment發生變化時會執行過渡動畫到新的狀態。
AnimatedContainer當Container屬性發生變化時會執行過渡動畫到新的狀態。
AnimatedDefaultTextStyle當字型樣式發生變化時,子元件中繼承了該樣式的文字元件會動態過渡到新樣式。

本篇文章大部分示例直接摘抄官方,具體可去 動畫&Motion Widget 查詢.(PS:寫不動了,著實寫不動了. ? ? ? )


需要外部Animation支援的元件示例



DecoratedBoxTransition示例

final DecorationTween decorationTween = DecorationTween(
  begin: BoxDecoration(
    color: const Color(0xFFFFFFFF),
    border: Border.all(style: BorderStyle.none),
    borderRadius: BorderRadius.circular(60.0),
    shape: BoxShape.rectangle,
    boxShadow: const <BoxShadow>[
      BoxShadow(
        color: Color(0x66666666),
        blurRadius: 10.0,
        spreadRadius: 3.0,
        offset: Offset(0, 6.0),
      )
    ],
  ),
  end: BoxDecoration(
    color: const Color(0xFFFFFFFF),
    border: Border.all(
      style: BorderStyle.none,
    ),
    borderRadius: BorderRadius.zero,
    // No shadow.
  ),
);

late AnimationController _controller = AnimationController(
  vsync: this,
  duration: const Duration(seconds: 3),
)..repeat(reverse: true);

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

 @override
 Widget build(BuildContext context) {
   return Container(
     color: Colors.white,
     child: Center(
       child: DecoratedBoxTransition(
         position: DecorationPosition.background,
         decoration: decorationTween.animate(_controller),
         child: Container(
           width: 200,
           height: 200,
           padding: const EdgeInsets.all(10),
           child: const FlutterLogo(),
         ),
       ),
     ),
   );
 }
複製程式碼

FadeTransition示例

late AnimationController _controller = AnimationController(
  duration: const Duration(seconds: 2),
  vsync: this,
)..repeat(reverse: true);
late Animation<double> _animation = CurvedAnimation(
  parent: _controller,
  curve: Curves.easeIn,
);

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

@override
Widget build(BuildContext context) {
  return Container(
    color: Colors.white,
    child: FadeTransition(
      opacity: _animation,
      child: const Padding(
        padding: EdgeInsets.all(8),
        child: FlutterLogo()
      ),
    ),
  );
}
複製程式碼

PositionedTransition示例

late AnimationController _controller = AnimationController(
  duration: const Duration(seconds: 2),
  vsync: this,
)..repeat(reverse: true);

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

@override
Widget build(BuildContext context) {
  final double smallLogo = 100;
  final double bigLogo = 200;

  return LayoutBuilder(
    builder: (context, constraints) {
      final Size biggest = constraints.biggest;
      return Stack(
        children: [
          PositionedTransition(
            rect: RelativeRectTween(
              begin: RelativeRect.fromSize(Rect.fromLTWH(0, 0, smallLogo, smallLogo), biggest),
              end: RelativeRect.fromSize(Rect.fromLTWH(biggest.width - bigLogo, biggest.height - bigLogo, bigLogo, bigLogo), biggest),
            ).animate(CurvedAnimation(
              parent: _controller,
              curve: Curves.elasticInOut,
            )),
            child: Padding(
              padding: const EdgeInsets.all(8),
              child: FlutterLogo()
            ),
          ),
        ],
      );
    },
  );
}
複製程式碼

RotationTransition示例

late AnimationController _controller = AnimationController(
  duration: const Duration(seconds: 2),
  vsync: this,
)..repeat(reverse: true);
late Animation<double> _animation = CurvedAnimation(
  parent: _controller,
  curve: Curves.elasticOut,
);

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

@override
Widget build(BuildContext context) {
  return Scaffold(
    body: Center(
      child: RotationTransition(
        turns: _animation,
        child: const Padding(
          padding: EdgeInsets.all(8.0),
          child: FlutterLogo(size: 150.0),
        ),
      ),
    ),
  );
}
複製程式碼

ScaleTransition示例

late AnimationController _controller = AnimationController(
  duration: const Duration(seconds: 2),
  vsync: this,
)..repeat(reverse: true);
late Animation<double> _animation = CurvedAnimation(
  parent: _controller,
  curve: Curves.fastOutSlowIn,
);

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

@override
Widget build(BuildContext context) {
  return Scaffold(
    body: Center(
      child: ScaleTransition(
        scale: _animation,
        child: const Padding(
          padding: EdgeInsets.all(8.0),
          child: FlutterLogo(size: 150.0),
        ),
      ),
    ),
  );
}
複製程式碼

SizeTransition示例

late AnimationController _controller = AnimationController(
  duration: const Duration(seconds: 3),
  vsync: this,
)..repeat();
late Animation<double> _animation = CurvedAnimation(
  parent: _controller,
  curve: Curves.fastOutSlowIn,
);

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

@override
Widget build(BuildContext context) {
  return Scaffold(
    body: SizeTransition(
      sizeFactor: _animation,
      axis: Axis.horizontal,
      axisAlignment: -1,
      child: Center(
          child: FlutterLogo(size: 200.0),
      ),
    ),
  );
}
複製程式碼

SlideTransition示例

class _MyStatefulWidgetState extends State<MyStatefulWidget> with SingleTickerProviderStateMixin {
  late AnimationController _controller = AnimationController(
    duration: const Duration(seconds: 2),
    vsync: this,
  )..repeat(reverse: true);
  late Animation<Offset> _offsetAnimation = Tween<Offset>(
    begin: Offset.zero,
    end: const Offset(1.5, 0.0),
  ).animate(CurvedAnimation(
    parent: _controller,
    curve: Curves.elasticIn,
  ));

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

  @override
  Widget build(BuildContext context) {
    return SlideTransition(
      position: _offsetAnimation,
      child: const Padding(
        padding: EdgeInsets.all(8.0),
        child: FlutterLogo(size: 150.0),
      ),
    );
  }
}
複製程式碼

不需要外部Animation支援元件示例



AnimatedPadding示例

double padValue = 0.0;
_updatePadding(double value) {
  setState(() {
    padValue = value;
  });
}

@override
Widget build(BuildContext context) {
  return Column(
    mainAxisAlignment: MainAxisAlignment.center,
    children: [
      AnimatedPadding(
        padding: EdgeInsets.all(padValue),
        duration: const Duration(seconds: 2),
        curve: Curves.easeInOut,
        child: Container(
          width: MediaQuery.of(context).size.width,
          height: MediaQuery.of(context).size.height / 5,
          color: Colors.blue,
        ),
      ),
      Text('Padding: $padValue'),
      ElevatedButton(
        child: Text('Change padding'),
        onPressed: () {
          _updatePadding(padValue == 0.0 ? 100.0 : 0.0);
        }
      ),
    ],
  );
}
複製程式碼

AnimatedPositioned示例

bool selected = false;

@override
Widget build(BuildContext context) {
  return Container(
    width: 200,
    height: 350,
    child: Stack(
      children: [
        AnimatedPositioned(
          width: selected ? 200.0 : 50.0,
          height: selected ? 50.0 : 200.0,
          top: selected ? 50.0 : 150.0,
          duration: Duration(seconds: 2),
          curve: Curves.fastOutSlowIn,
          child: GestureDetector(
            onTap: () {
              setState(() {
                selected = !selected;
              });
            },
            child: Container(
              color: Colors.blue,
              child: Center(child: Text('Tap me')),
            ),
          ),
        ),
      ],
    ),
  );
}
複製程式碼

AnimatedOpacity示例

class LogoFade extends StatefulWidget {
  @override
  createState() => LogoFadeState();
}

class LogoFadeState extends State<LogoFade> {
  double opacityLevel = 1.0;

  void _changeOpacity() {
    setState(() => opacityLevel = opacityLevel == 0 ? 1.0 : 0.0);
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        AnimatedOpacity(
          opacity: opacityLevel,
          duration: Duration(seconds: 3),
          child: FlutterLogo(),
        ),
        ElevatedButton(
          child: Text('Fade Logo'),
          onPressed: _changeOpacity,
        ),
      ],
    );
  }
}
複製程式碼

AnimatedAlign示例

bool selected = false;

@override
Widget build(BuildContext context) {
  return GestureDetector(
    onTap: () {
      setState(() {
        selected = !selected;
      });
    },
    child: Center(
      child: Container(
        width: 250.0,
        height: 250.0,
        color: Colors.red,
        child: AnimatedAlign(
          alignment: selected ? Alignment.topRight : Alignment.bottomLeft,
          duration: const Duration(seconds: 1),
          curve: Curves.fastOutSlowIn,
          child: const FlutterLogo(size: 50.0),
        ),
      ),
    ),
  );
}
複製程式碼

AnimatedContainer示例

bool selected = false;

@override
Widget build(BuildContext context) {
  return GestureDetector(
    onTap: () {
      setState(() {
        selected = !selected;
      });
    },
    child: Center(
      child: AnimatedContainer(
        width: selected ? 200.0 : 100.0,
        height: selected ? 100.0 : 200.0,
        color: selected ? Colors.red : Colors.blue,
        alignment: selected ? Alignment.center : AlignmentDirectional.topCenter,
        duration: Duration(seconds: 2),
        curve: Curves.fastOutSlowIn,
        child: FlutterLogo(size: 75),
      ),
    ),
  );
}
複製程式碼

AnimatedDefaultTextStyle示例

  var duration = Duration(seconds: 5);

  TextStyle _style = TextStyle(color: Colors.black);

  @override
  Widget build(BuildContext context) {
    return AnimatedDefaultTextStyle(
      child: GestureDetector(
        child: Text("hello world"),
        onTap: () {
          setState(() {
            _style = TextStyle(
              color: Colors.blue,
              decorationStyle: TextDecorationStyle.solid,
              decorationColor: Colors.blue,
            );
          });
        },
      ),
      style: _style,
      duration: duration,
    );
  }       
複製程式碼

結語


整體上來說,如果是實現比較簡單的動畫可以直接使用Flutter內建的動畫元件,可以大大減少程式碼量.本篇文章算是一個記錄吧,如果深入,可自行檢視下面的參考內容.

歡迎持續關注騷棟,有任何問題歡迎聯絡騷棟.

動畫&Motion Widget
動畫過渡元件

相關文章