Flutter에서의 위젯 생명 주기
Flutter에서 위젯의 생명 주기는 중요한 개념이다. 특히, StatelessWidget과 StatefulWidget은 동작 방식이 다르기 때문에 각각의 생명 주기를 이해하는 것이 중요하다.
State 생명 주기
- StatelessWidget과 StatefulWidget은 빌드될 때마다 새로 생성된다.
- StatelessWidget은 build 메서드가 호출되면서 한 번만 생성되고 끝난다.
- StatefulWidget은 내부적으로 State 객체를 생성하며, 생성된 State 객체는 메모리에 유지되면서 생명 주기를 가진다.
- 한 번 생성된 State는 재사용되며, 필요할 때만 build 메서드가 다시 호출되어 업데이트된다.
StatefulWidget의 생명 주기
- StatefulWidget의 생명 주기는 다음과 같은 주요 메서드로 동작함
- createState() : StatefulWidget에서는 createState() 메서드를 통해 State 객체를 생성해야 하며, 전체 생명 주기 중, 한 번만 호출됨
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
- initState() : State 객체가 생성된 후 호출되며, 위젯 상태를 초기화하는 데 보통 사용됨. 전체 생명 주기 중, 한 번만 호출됨
- build() : initState() 호출 후, 호출되며, 개발자가 setState()를 호출하면, build() 메서드가 재호출됨.
- 호출 시마다, 변경된 상태를 기반으로 변경된 UI를 표현
- dispose() : 위젯 트리에서 제거될 때 호출되며, State 객체가 영구적으로 제거되고, 사용 자원이 해제됨
코드 확인
import 'package:flutter/material.dart';
void main() {
runApp(MyWidget());
}
class MyWidget extends StatefulWidget {
@override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
int _counter = 0;
@override
void initState() {
super.initState();
print('initState called');
}
@override
Widget build(BuildContext context) {
print('build called');
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('My Widget'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Button tapped $_counter times.'),
Padding(
padding: const EdgeInsets.all(8.0),
child: ElevatedButton(
child: Text('Tap me'),
onPressed: () {
setState(() {
_counter++;
});
},
),
),
],
),
),
),
);
}
@override
void dispose() {
print('dispose called'); // 위젯 제거 시, dispose() 호출됨. 이때 메모리상에 할당된 모든 자원 해제해야 함.
super.dispose();
}
}
애니매이션 코드 사용해보기
import 'package:flutter/material.dart';
import 'dart:math';
void main() {
runApp(MyWidget());
}
class MyWidget extends StatefulWidget {
@override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> with SingleTickerProviderStateMixin {
int _counter = 0; // 1. 버튼이 눌린 횟수를 저장하는 변수입니다.
Color _backgroundColor = Colors.white; // 2. 배경색을 저장하는 변수입니다.
late AnimationController _controller; // 3. 애니메이션의 진행을 제어하는 컨트롤러입니다.
late Animation<double> _animation; // 4. 애니메이션의 스케일 값을 저장하는 변수입니다.
@override
void initState() {
super.initState();
print('initState called'); // initState는 위젯이 처음 생성될 때 한 번 호출됩니다.
// AnimationController 초기화
_controller = AnimationController(
duration: const Duration(milliseconds: 300), // 5. 애니메이션의 지속 시간을 설정합니다.
vsync: this, // 6. vsync는 화면 새로고침 주기에 동기화하여 애니메이션 성능을 최적화합니다.
);
// Tween을 사용하여 애니메이션 범위 정의 (0.8 ~ 1.0)
_animation = Tween<double>(begin: 0.8, end: 1.0).animate(CurvedAnimation(
parent: _controller,
curve: Curves.easeInOut, // 7. 애니메이션이 부드럽게 시작하고 끝나도록 설정합니다.
))
..addListener(() {
setState(() {}); // 8. 애니메이션이 진행될 때마다 화면을 업데이트합니다.
});
}
@override
void dispose() {
_controller.dispose(); // 9. 메모리 누수를 방지하기 위해 애니메이션 컨트롤러를 해제합니다.
print('dispose called'); // dispose는 위젯이 제거될 때 한 번 호출됩니다.
super.dispose();
}
// 버튼을 눌렀을 때 호출되는 함수
void _incrementCounter() {
setState(() {
_counter++; // 10. 버튼이 눌릴 때마다 카운터를 증가시킵니다.
_backgroundColor = _getRandomColor(); // 11. 배경색을 랜덤으로 변경합니다.
});
// 12. 애니메이션을 앞으로 진행한 후, 완료되면 원래 상태로 되돌립니다.
_controller.forward().then((_) {
_controller.reverse();
});
}
// 랜덤 색상을 생성하는 함수
Color _getRandomColor() {
final random = Random();
return Color.fromARGB(
255,
random.nextInt(256), // R (0~255) 랜덤 값
random.nextInt(256), // G (0~255) 랜덤 값
random.nextInt(256), // B (0~255) 랜덤 값
);
}
@override
Widget build(BuildContext context) {
print('build called'); // build는 상태가 변경될 때마다 호출됩니다.
return MaterialApp(
theme: ThemeData(
useMaterial3: true, // 13. Material 3 스타일을 적용합니다.
colorSchemeSeed: Colors.blue, // 14. Material 3 컬러 테마의 기본 색상을 지정합니다.
),
home: Scaffold(
appBar: AppBar(
title: Text('Animated Counter'),
),
backgroundColor: _backgroundColor, // 15. 배경색을 설정합니다.
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'버튼을 누른 횟수 : $_counter 번', // 16. 버튼이 눌린 횟수를 텍스트로 표시합니다.
style: TextStyle(fontSize: 20),
),
SizedBox(height: 20),
// 17. Transform.scale 위젯으로 버튼의 크기를 애니메이션 효과로 조절합니다.
Transform.scale(
scale: _animation.value, // 18. 애니메이션 스케일 값을 적용합니다.
child: ElevatedButton(
style: ElevatedButton.styleFrom(
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 12),
backgroundColor: Colors.blueAccent, // 19. 버튼 배경색 설정
),
child: Text('눌러 보기', style: TextStyle(fontSize: 18)),
onPressed: _incrementCounter, // 20. 버튼을 누르면 _incrementCounter 함수 호출
),
),
],
),
),
),
);
}
}
코드 설명
- AnimationController 초기화: _controller는 애니메이션의 타이밍을 제어하는 객체로, duration을 통해 애니메이션 속도를 조절하고, vsync는 SingleTickerProviderStateMixin을 통해 화면 새로고침 주기에 동기화하여 성능을 최적화한다.
- Tween 객체 설정: Tween<double>(begin: 0.8, end: 1.0)은 애니메이션이 시작될 때는 0.8배의 크기에서 시작해, 1.0배 크기로 변하는 애니메이션 효과를 제공한다.
- Transform.scale 위젯: scale 속성에 애니메이션 값을 적용해 버튼의 크기가 점점 커졌다가 다시 원래대로 돌아오도록 설정한다.
Flutter에서 애니메이션을 효율적으로 관리하기 위해 vsync와 SingleTickerProviderStateMixin을 사용한다
1. vsync: this의 역할
- vsync는 "Vertical Synchronization"의 약자로, 애니메이션이 화면의 새로고침 주기와 동기화되도록 도와준다.
- 목적: 애니메이션이 화면에 보이지 않거나 필요하지 않은 경우, 불필요하게 자원을 사용하지 않도록 애니메이션을 자동으로 멈춰준다.
- 효과: 화면 주기와 맞춰 애니메이션이 실행되므로 CPU와 GPU의 자원 사용을 줄여 효율적으로 동작한다.
2. SingleTickerProviderStateMixin의 역할
- Ticker는 Flutter에서 애니메이션을 위한 핵심 요소로, 화면을 여러 번 새로 그려 애니메이션을 부드럽게 보여준다.
- SingleTickerProviderStateMixin은 Ticker를 한 번만 제공하여, 애니메이션이 필요할 때만 활성화하고 그렇지 않을 때는 비활성화한다.
- 장점: 애니메이션이 없는 상황에서 Ticker가 불필요하게 작동하는 것을 방지해, 메모리와 자원을 절약한다.
왜 SingleTickerProviderStateMixin을 써야 할까요?
애니메이션은 Ticker의 도움으로 화면을 반복해서 새로 그리며 동작합니다. 하지만 필요하지 않은 상황에서도 Ticker가 작동하면 자원을 낭비하게 되죠. SingleTickerProviderStateMixin을 사용하면 필요할 때만 Ticker가 작동하도록 관리할 수 있어, 애니메이션을 효율적으로 실행할 수 있습니다.
728x90
'Flutter' 카테고리의 다른 글
MVVM 패턴과 상태 관리 (0) | 2024.11.15 |
---|---|
플러터 기본기 다지기 - 4 (Flutter의 위젯과 Element 트리 그리고 Key) (1) | 2024.11.15 |
Dio 통신 연습 (0) | 2024.11.14 |
플러터 기본기 다지기 - 3 (TabBar 위젯) (1) | 2024.11.14 |
플러터 기본기 다지기 - 3 (BottomSheet 위젯) (0) | 2024.11.14 |