앱바의 Drawer 위젯을 이용하면 상단 메뉴바를 쉽게 만들 수 있다.
size = MediaQuery.of(context).size; // 디스플레이 크기
Scafford(
endDrawer: Container( // 왼쪽에 만들고 싶으면 drawer속성
width: size.width * 0.6,
color: Colors.lightBlue,
),
)
애니메이션을 연습하기 위해 이 위젯을 직접 구현한다면
AnimationController
와 다르게 Container
위젯을 애니메이션화 하는데 사용된다.
class MyHome extends StatefulWidget {
const MyHome({super.key});
State<MyHome> createState() => _MyHomeState();
}
class _MyHomeState extends State<MyHome> {
late Size size;
bool isOpen = false;
void initState() {
super.initState();
}
Widget build(BuildContext context) {
size = MediaQuery.of(context).size;
print("size width: ${size.width}");
return Scaffold(
appBar: AppBar(
actions: [
Padding(
padding: const EdgeInsets.only(right: 12.0),
child: InkWell(
child: Icon(Icons.menu),
onTap: () {
setState(() {
isOpen = !isOpen; // 토글
});
},
),
)
],
),
body: Stack(
children: [
Center(
child: Text("Animation Screen"),
),
AnimatedContainer(
curve: Curves.fastLinearToSlowEaseIn, // 효과
duration: Duration(seconds: 1), // 애니메이션 시간
height: double.infinity,
width: size.width * 0.7,
color: Colors.blue,
transform: Matrix4.translationValues( // 움직이는 좌표설정
isOpen ? size.width * 0.3 : size.width, 0, 0),
)
],
),
);
}
}
fastLinearToSlowEaseIn
외에도 다양한 효과들이 있다.
AnimatedWidget
을 상속한 ScaleTransition
을 이용해서 애니메이션을 만들어보자.
AnimationController
는 애니메이션을 제어하고 Animation
클래스가 애니메이션을 처리한다.
AnimatedWidget
은 Animation
객체를 이용해서 애니메이션을 처리하기 때문에 반드시 필요로 한다.
required Animation<double> scale,
class MyHome2 extends StatefulWidget {
const MyHome2({super.key});
State<MyHome2> createState() => _MyHome2State();
}
class _MyHome2State extends State<MyHome2> with SingleTickerProviderStateMixin {
late AnimationController _animationController; // 메소드에서 필요로 하기 때문에 전역변수
void initState() {
super.initState();
_animationController = AnimationController(
vsync: this, // required
duration: const Duration(seconds: 2),
);
_animationController.repeat(); // 반복되도록 설정
}
AnimationController
는 required TickerProvider vsync,
필요로 하기 때문에 TickerProvider
객체를 얻기 위해서 SingleTickerProviderStateMixin
클래스를 Mixin한다.
build(BuildContext context) {
Animation<double> _animation =
Tween(begin: 0.0, end: 2.0).animate(_animationController);
return Scaffold(
appBar: AppBar(),
body: Center(
child: ScaleTransition(
scale: _animation,
child: Container(
width: 200,
height: 200,
color: Colors.blue,
),
),
),
);
}
}
Widget
animate
는 Tween
클래스와 함께 사용되고 animate
는 부모의 Animation<double>
을 받아서 Animation<double>
타입을 리턴한다.
AnimationController
는 내부적으로 Animation<double>
을 상속하고 있다.
`class AnimationController extends Animation<double>
서서히 나타나거나 사라지게 할경우 사용한다 옵션은 위에서 몇개만 수정하면 된다.
// scale 수정
opacity: _animation,
// Tween의 범위는 0 ~ 1
Tween(begin: 0.0, end: 1.0).animate(_animationController);
마찬가지로 필드에 따라 코드를 수정한다.
// 필요로 하는 필드는
required Animation<RelativeRect> rect,
PositionedTransition
는 부모 위젯으로 Stack
을 필요로 한다.
build(BuildContext context) {
// double screenHeight = MediaQuery.of(context).size.height;
double screenWidth = MediaQuery.of(context).size.width;
Animation<RelativeRect> _animation =RelativeRectTween(
begin: RelativeRect.fromLTRB(screenWidth, 0, 0, 0),
end: RelativeRect.fromLTRB(0, 0, 0, 0),
).animate(CurvedAnimation(
parent: _animationController,
curve: Curves.elasticInOut,
));
return Scaffold(
body: Stack(
children: [ PositionedTransition(
rect: _animation,
child: Container(
color: Colors.blue,
),
)
],
),
);
}
}
Widget
공식문서에 따르면 다음과 같은 애니메이션도 가능하다
void dispose() {
_animationController.dispose(); // 리소스 해제 - 메모리 누수 방지
super.dispose();
}
Widget build(BuildContext context) {
const double smallLogo = 100;
const double bigLogo = 200;
return Scaffold(
backgroundColor: Colors.white,
body: LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
final Size biggest = constraints.biggest;
return Stack(
children: <Widget>[
PositionedTransition(
rect: RelativeRectTween(
begin: RelativeRect.fromSize(
const Rect.fromLTWH(0, 0, smallLogo, smallLogo), biggest),
end: RelativeRect.fromSize(
Rect.fromLTWH(biggest.width - bigLogo,
biggest.height - bigLogo, bigLogo, bigLogo),
biggest),
).animate(CurvedAnimation(
parent: _animationController,
curve: Curves.elasticInOut,
)),
child: const Padding(
padding: EdgeInsets.all(8), child: FlutterLogo()),
),
],
);
},
),
);
}
}
로딩에 사용하면 좋을것 같다.
위 코드에서 Build 만 수정한다.
build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: Center(
child: RotationTransition(
turns: CurvedAnimation(
parent: _animationController,
curve: Curves.ease),
child: Container(
color: Colors.blue,
width: 100,
height: 100,
),
),
),
);
}
Widget
클래스에서 다른 클래스로 위젯을 변환한다.
// state 클래스에 필드추가
Widget mWidget = FirstWidget();
Widget build(BuildContext context) {
return Scaffold(
// backgroundColor: Colors.white,
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
AnimatedSwitcher(
duration: Duration(seconds: 3),
child: mWidget,
),
ElevatedButton(
onPressed: () {
setState(() {
mWidget = SecondWidget();
});
},
child: Text("버튼")),
],
)),
);
}
// 변하는 위젯 - 이런 모양으로 다르게 만들면 된다.
class FirstWidget extends StatelessWidget {
const FirstWidget({
super.key,
});
Widget build(BuildContext context) {
return Container(
color: Colors.blue[300],
width: 100,
height: 100,
);
}
}
3초후 희미해지게 설정해봤다.
몇가지만 수정한다.
// 필드에 상태값 추가
double myOpacity = 1.0;
// initState에 추가 - 3초뒤 change() 호출
Future.delayed(Duration(seconds: 3), () {change();},
void change(){
setState(() {
myOpacity = 0.2; // 3초뒤
});
}
// 변환하고 싶은 위젯을 감싼다
AnimatedOpacity(
duration: Duration(seconds: 3),
opacity: myOpacity,
child: Container(
color: Colors.blue,
width: 200,
height: 200,
),
),
잘봤습니다!