Flutter

Flutter Animation(플러터 애니메이션) - 1 (암시적 애니메이션)

봄석 2020. 3. 31. 23:02

Flutter Animation(플러터 애니메이션) - 1 (암시적 애니메이션)

오랜만에 포스트를  작성하게 되었네요. 이번 포스트에서는 Flutter Animation에 대하여 알아보겠습니다.

 

 

목차

Table of Contents

 

 

 

암시적 애니메이션(Implicit Animation)

Flutter의 애니메이션 라이브러리를 사용하면 UI에서 위젯에 모션을 추가하고 시각 효과를 만들 수 있습니다. 

위 라이브러리에 설정된 하나의 위젯이 애니메이션을 관리합니다.

이러한 위젯을 통칭하여 암시 적 애니메이션 또는 암시 적으로 애니메이션 된 위젯이라고 하며, 

ImplicitlyAnimatedWidget 클래스를 구현합니다

 

✨암시적  애니메이션 위젯들은  자동으로 변경사항을 애니메이션 합니다

 

특징

⚡ 애니메이션을 사용하면 대상 값을 설정하여 위젯 속성에 애니메이션을 적용할 수 있습니다.
⚡ 대상 값이 변경될 때마다 위젯은 이전 값에서 새 값으로 특성에 애니메이션을 적용합니다.
⚡ 컨트롤러 등으로 애니메이션 효과를 관리하지 않습니다.

 

애니메이션 과정

값을 통한 애니메이션으로

old value와 new value의 사이를 보간(Interpolation)합니다

암시적 애니메이션 위젯은 이 보간을 처리합니다.

 

💡보간(Interpolation) 뜻

 

 

여러 가지 암시적 애니메이션  위젯

⭐ AnimatedAlign(Align)

⭐ AnimatedContainer(Container)

⭐ AnimatedDefaultTextStyle(DefaultTextStyle)

⭐ AnimatedOpacity(Opacity)

⭐ AnimatedPadding(Padding)

⭐ AnimatedPhysicalModel(PhysicalModel)

⭐ AnimatedPositioned(Positioned)

⭐ AnimatedThemeSize

⭐ AnimatedSize

⭐ AnimatedCrossFade

⭐ AnimatedSwitcher

 

 

 

AnimatedAlign

 지정된 Align(정렬)이 변경될 때마다 지정된 Duration동안 자동으로 child의 위치를 전환하는 애니메이션 버전의 Align입니다

class AnimatedAlignScreen extends StatefulWidget {
  @override
  _AnimatedAlignScreenState createState() => _AnimatedAlignScreenState();
}

class _AnimatedAlignScreenState extends State<AnimatedAlignScreen> {
  var alignment = Alignment.bottomLeft;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('AnimatedAlign')),
      body: Container(
        padding: EdgeInsets.all(30.0),
        child: Column(
          children: <Widget>[
            Expanded(
              child: AnimatedAlign(
                alignment: alignment,
                duration: Duration(milliseconds: 100),
                child: FlutterLogo(size: 150),
              ),
            ),
            FlatButton(
              color: Colors.blueAccent,
              child: Text('alignment change'),
              onPressed: () {
                setState(() {
                  alignment = alignment == Alignment.bottomLeft
                      ? Alignment.topRight
                      : Alignment.bottomLeft;
                });
              },
            )
          ],
        ),
      ),
    );
  }
}

 

 

AnimatedContainer

AnimatedContainer는 일정 기간 동안 점차적으로 값을 변경하는 Container 애니메이션 버전입니다.

 

📌이전 값과 새로운 값 사이를 자동적으로 애니메이션 합니다. (Curve와 Duration 사용)

📌속성 값이 null인 것은 애니메이션 하지 않습니다.

📌자식은 애니메이션 하지 않습니다.

📌Color, Border, BorderRadii, BackgroundImages, Shadows, Gradients, Shape, Padding, Width, Height, Alignment, Transforms.. 등등을 애니메이션 할 수 있습니다.

 

class AnimatedContainerScreen extends StatefulWidget {
  @override
  _AnimatedContainerScreenState createState() =>
      _AnimatedContainerScreenState();
}

class _AnimatedContainerScreenState extends State<AnimatedContainerScreen> {
  bool selected = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('AnimatedContainer')),
      body: GestureDetector(
        onTap: () {
          setState(() {
            selected = !selected;
          });
        },
        child: Center(
          child: AnimatedContainer(
            width: selected ? 300.0 : 100.0,
            height: selected ? 100.0 : 300.0,
            alignment:
                selected ? Alignment.center : AlignmentDirectional.topCenter,
            duration: Duration(milliseconds: 500),
            decoration: BoxDecoration(
              border: selected
                  ? Border.all(color: Colors.black, width: 3)
                  : Border.all(color: Colors.red, width: 3),
              gradient: new LinearGradient(
                begin: FractionalOffset.topCenter,
                end: FractionalOffset.bottomCenter,
                colors: selected
                    ? [Colors.lightGreen, Colors.redAccent]
                    : [Colors.orange, Colors.deepOrangeAccent],
                stops: [0.0, 1.0],
              ),
              color: selected ? Colors.red : Colors.blue,
            ),
            curve: Curves.fastOutSlowIn,
            child: FlutterLogo(size: 75),
          ),
        ),
      ),
    );
  }
}

 

 

 

AnimatedDefaultTextStyle

AnimatedDefaultTextStyle는 TextSytle이 바뀔 때마다 자동적으로 애니메이션 합니다.

 

📌 textAlign, softWrap, textOverflow, maxLines  속성은 애니메이션 변경할 때 즉시 적용되지 않습니다

 

 

 

class AnimatedDefaultTextStyleScreen extends StatefulWidget {
  @override
  _AnimatedDefaultTextStyleScreenState createState() =>
      _AnimatedDefaultTextStyleScreenState();
}

class _AnimatedDefaultTextStyleScreenState
    extends State<AnimatedDefaultTextStyleScreen> {
  bool selected = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('AnimatedDefaultTextStyle')),
      body: GestureDetector(
        onTap: () {
          setState(() {
            selected = !selected;
          });
        },
        child: Center(
          child: Container(
            height: 120,
            child: AnimatedDefaultTextStyle(
              duration: const Duration(milliseconds: 300),
              style: TextStyle(
                fontSize: 50.0,
                color: selected ? Colors.red : Colors.blueAccent,
                fontWeight: selected ? FontWeight.w100 : FontWeight.bold,
              ),
              child: Text('Flutter'),
            ),
          ),
        ),
      ),
    );
  }
}

 

 

AnimatedOpacity

지정된 opacity가 변경될 때마다 지정된 duration 동안 child의 opacity를 자동으로 애니메이션 합니다.

 

 

class AnimatedOpacityScreen extends StatefulWidget {
  @override
  _AnimatedOpacityScreenState createState() => _AnimatedOpacityScreenState();
}

class _AnimatedOpacityScreenState extends State<AnimatedOpacityScreen> {
  bool selected = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('AnimatedOpacity')),
      body: GestureDetector(
        onTap: () {
          setState(() {
            selected = !selected;
          });
        },
        child: Center(
          child: Container(
            height: 120.0,
            width: 120.0,
            color: Colors.blue[50],
            child: AnimatedOpacity(
              opacity: selected ? 0.0 : 1.0,
              duration: Duration(milliseconds: 500),
              child: FlutterLogo(
                size: 60,
              ),
            ),
          ),
        ),
      ),
    );
  }
}

 

 

AnimatedPadding

패딩이 변경되면  자동으로 애니메이션 합니다. 애니메이션 버전의 Padding입니다.

 

 

class AnimatedPaddingScreen extends StatefulWidget {
  @override
  _AnimatedPaddingScreenState createState() => _AnimatedPaddingScreenState();
}

class _AnimatedPaddingScreenState extends State<AnimatedPaddingScreen> {
  bool selected = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('AnimatedPadding')),
      body: GestureDetector(
        onTap: () {
          setState(() {
            selected = !selected;
          });
        },
        child: Center(
          child: Container(
            height: 300.0,
            width: 300.0,
            child: AnimatedPadding(
              padding: selected
                  ? EdgeInsets.only(top: 100, bottom: 100)
                  : EdgeInsets.only(left: 100, right: 100),
              curve: Curves.ease,
              duration: Duration(seconds: 1),
              child: Container(
                color: Colors.blue,
                child: Center(child: Text('Flutter')),
              ),
            ),
          ),
        ),
      ),
    );
  }
}

 

 

AnimatedPhysicalModel

 borderRadius와 elevation이 변경될 때마다 자동으로 애니메이션 됩니다. PhysicalModel의 애니메이션  버전입니다.

 

📌 shape는 애니메이션 되지 않습니다.

 

 

class AnimatedPhysicalModelScreen extends StatefulWidget {
  @override
  _AnimatedPhysicalModelScreenState createState() =>
      _AnimatedPhysicalModelScreenState();
}

class _AnimatedPhysicalModelScreenState
    extends State<AnimatedPhysicalModelScreen> {
  bool selected = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('AnimatedPhysicalModel')),
      body: GestureDetector(
        onTap: () {
          setState(() {
            selected = !selected;
          });
        },
        child: Center(
          child: AnimatedPhysicalModel(
            duration: const Duration(milliseconds: 500),
            curve: Curves.fastOutSlowIn,
            elevation: selected ? 0 : 6.0,
            shape: BoxShape.rectangle,
            shadowColor: Colors.black,
            color: Colors.white,
            borderRadius: selected
                ? const BorderRadius.all(Radius.circular(0))
                : const BorderRadius.all(Radius.circular(10)),
            child: Container(
              height: 120.0,
              width: 120.0,
              color: Colors.blue[50],
              child: FlutterLogo(
                size: 60,
              ),
            ),
          ),
        ),
      ),
    );
  }
}

 

 

AnimatedPositioned

Stack위젯에서 자식의 위치를 제어하며
지정된 위치가 변경될 때마다 지정된 기간 동안 자녀의 위치를 ​​자동으로 애니메이션 합니다.

 

 

class AnimatedPositionedScreen extends StatefulWidget {
  @override
  _AnimatedPositionedScreenState createState() =>
      _AnimatedPositionedScreenState();
}

class _AnimatedPositionedScreenState extends State<AnimatedPositionedScreen> {
  bool selected = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('AnimatedPositioned')),
      body: GestureDetector(
        onTap: () {
          setState(() {
            selected = !selected;
          });
        },
        child: Stack(
          children: <Widget>[
            AnimatedPositioned(
              duration: const Duration(milliseconds: 500),
              curve: Curves.fastOutSlowIn,
              left: selected ? 10 : 100,
              top: selected ? 70 : 100,
              right: selected ? 10 : 100,
              bottom: selected ? 70 : 100,
              child: Container(
                color: Colors.blue,
              ),
            ),
          ],
        ),
      ),
    );
  }
}

 

 

AnimatedTheme

지정된 Theme이 변경될 때마다 지정된 Duration 동안 색상 등을 자동으로 전환하는 Theme 애니메이션 버전.

 

 

class AnimatedThemeScreen extends StatefulWidget {
  @override
  _AnimatedPositionedDirectionalScreenState createState() =>
      _AnimatedPositionedDirectionalScreenState();
}

class _AnimatedPositionedDirectionalScreenState
    extends State<AnimatedThemeScreen> with SingleTickerProviderStateMixin {
  bool selected = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('AnimatedTheme')),
      body: GestureDetector(
        onTap: () {
          setState(() {
            selected = !selected;
          });
        },
        child: AnimatedTheme(
          data: selected ? ThemeData.light() : ThemeData.dark(),
          duration: const Duration(milliseconds: 500),
          child: Center(
            child: Card(
              child: const Padding(
                padding: EdgeInsets.all(16),
                child: Text(
                  'theme',
                  style: TextStyle(fontSize: 24),
                ),
              ),
            ),
          ),
        ),
      ),
    );
  }
}

 

 

AnimatedSize

주어진 자녀의 크기가 변할 때마다 주어진 기간 동안 자동으로 크기를 전환하는 애니메이션 위젯

 

 

import 'package:flutter/material.dart';

class AnimatedSizeScreen extends StatefulWidget {
  @override
  _AnimatedPositionedDirectionalScreenState createState() =>
      _AnimatedPositionedDirectionalScreenState();
}

class _AnimatedPositionedDirectionalScreenState
    extends State<AnimatedSizeScreen> with SingleTickerProviderStateMixin {
  bool selected = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('AnimatedSize')),
      body: GestureDetector(
        onTap: () {
          setState(() {
            selected = !selected;
          });
        },
        child: Container(
          height: 300,
          child: Center(
            child: AnimatedSize(
              duration: const Duration(milliseconds: 500),
              curve: Curves.fastOutSlowIn,
              child: Container(
                width: selected ? 300 : 200,
                height: selected ? 160 : 200,
                color: Colors.blue,
              ),
              vsync: this,
            ),
          ),
        ),
      ),
    );
  }
}

 

AnimatedCrossFade

두 자식 사이에서만 페이드 되지만 크기를 보강하며 뒤집습니다.

 

 

class AnimatedCrossFadeScreen extends StatefulWidget {
  @override
  _AnimatedCrossFadeScreenState createState() =>
      _AnimatedCrossFadeScreenState();
}

class _AnimatedCrossFadeScreenState extends State<AnimatedCrossFadeScreen> {
  bool selected = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('AnimatedCrossFade')),
      body: GestureDetector(
          onTap: () {
            setState(() {
              selected = !selected;
            });
          },
          child: Center(
            child: AnimatedCrossFade(
              duration: const Duration(milliseconds: 500),
              firstChild: const FlutterLogo(
                  style: FlutterLogoStyle.horizontal, size: 100.0),
              secondChild:
                  const FlutterLogo(style: FlutterLogoStyle.stacked, size: 100.0),
              crossFadeState:
                  selected ? CrossFadeState.showFirst : CrossFadeState.showSecond,
            ),
          )),
    );
  }
}

 

AnimatedSwitcher

기본적으로 새 위젯과, AnimatedSwitcher에서 자식으로 설정 한 위젯 간에 크로스 페이드를 수행하는 위젯입니다.

 

import 'package:flutter/material.dart';

class AnimatedSwitcherScreen extends StatefulWidget {
  @override
  _AnimatedPositionedDirectionalScreenState createState() =>
      _AnimatedPositionedDirectionalScreenState();
}

class _AnimatedPositionedDirectionalScreenState
    extends State<AnimatedSwitcherScreen> {
  int _count = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('AnimatedSwitcher')),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: <Widget>[
          AnimatedSwitcher(
            duration: const Duration(milliseconds: 500),
            transitionBuilder: (Widget child, Animation<double> animation) {
              return ScaleTransition(child: child, scale: animation);
            },
            child: Text(
              '$_count',
              key: ValueKey<int>(_count),
              style: Theme.of(context).textTheme.display1,
            ),
          ),
          Container(
            padding: EdgeInsets.all(30.0),
            child: RaisedButton(
              child: const Text('Increment'),
              onPressed: () {
                setState(() {
                  _count += 1;
                });
              },
            ),
          ),
        ],
      ),
    );
  }
}

 

샘플 코드

https://github.com/qjatjr1108/Flutter-Animation

 

qjatjr1108/Flutter-Animation

Flutter Animation . Contribute to qjatjr1108/Flutter-Animation development by creating an account on GitHub.

github.com