개요
Flutter에서 setState는 가장 기본적인 상태 관리 방법입니다. StatefulWidget의 상태를 변경하고 UI를 다시 그리도록 하는 메커니즘을 제공합니다. 이번 포스트에서는 setState의 개념과 사용법에 대해 자세히 알아보겠습니다.
setState의 기본 개념
StatefulWidget과 State
// StatefulWidget 예시
class Counter extends StatefulWidget {
const Counter({Key? key}) : super(key: key);
@override
_CounterState createState() => _CounterState();
}
class _CounterState extends State<Counter> {
int _count = 0; // 상태 변수
@override
Widget build(BuildContext context) {
return Text('Count: $_count');
}
}
setState 기본 사용법
class _CounterState extends State<Counter> {
int _count = 0;
void _incrementCounter() {
setState(() {
_count++; // 상태 변경
});
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Text('Count: $_count'),
ElevatedButton(
onPressed: _incrementCounter,
child: Text('Increment'),
),
],
);
}
}
setState의 동작 원리
State 생명주기
class _MyWidgetState extends State<MyWidget> {
@override
void initState() {
super.initState();
// 초기화 코드
}
@override
void dispose() {
// 정리 코드
super.dispose();
}
@override
void setState(VoidCallback fn) {
super.setState(fn);
// setState가 호출되면 build 메서드가 다시 실행됨
}
@override
Widget build(BuildContext context) {
// UI 구성
return Container();
}
}
setState 호출 시 일어나는 일
- setState 함수 호출
- 상태 업데이트
- build 메서드 재실행
- 위젯 트리 재구성
- UI 업데이트
실전 활용 예시
기본적인 카운터 앱
class CounterApp extends StatefulWidget {
const CounterApp({Key? key}) : super(key: key);
@override
_CounterAppState createState() => _CounterAppState();
}
class _CounterAppState extends State<CounterApp> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
void _decrementCounter() {
setState(() {
if (_counter > 0) {
_counter--;
}
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Counter App')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Current Count:',
style: TextStyle(fontSize: 20),
),
Text(
'$_counter',
style: TextStyle(fontSize: 40, fontWeight: FontWeight.bold),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: _decrementCounter,
child: Icon(Icons.remove),
),
SizedBox(width: 20),
ElevatedButton(
onPressed: _incrementCounter,
child: Icon(Icons.add),
),
],
),
],
),
),
);
}
}
폼 데이터 관리
class UserForm extends StatefulWidget {
@override
_UserFormState createState() => _UserFormState();
}
class _UserFormState extends State<UserForm> {
String _name = '';
String _email = '';
bool _subscribed = false;
void _updateName(String value) {
setState(() {
_name = value;
});
}
void _updateEmail(String value) {
setState(() {
_email = value;
});
}
void _toggleSubscription(bool? value) {
setState(() {
_subscribed = value ?? false;
});
}
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.all(16.0),
child: Column(
children: [
TextField(
onChanged: _updateName,
decoration: InputDecoration(
labelText: 'Name',
),
),
TextField(
onChanged: _updateEmail,
decoration: InputDecoration(
labelText: 'Email',
),
),
CheckboxListTile(
title: Text('Subscribe to newsletter'),
value: _subscribed,
onChanged: _toggleSubscription,
),
ElevatedButton(
onPressed: () {
// 폼 제출 로직
print('Name: $_name');
print('Email: $_email');
print('Subscribed: $_subscribed');
},
child: Text('Submit'),
),
],
),
);
}
}
리스트 관리
class TodoList extends StatefulWidget {
@override
_TodoListState createState() => _TodoListState();
}
class _TodoListState extends State<TodoList> {
final List<String> _todos = [];
final TextEditingController _controller = TextEditingController();
void _addTodo() {
if (_controller.text.isNotEmpty) {
setState(() {
_todos.add(_controller.text);
_controller.clear();
});
}
}
void _removeTodo(int index) {
setState(() {
_todos.removeAt(index);
});
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Padding(
padding: EdgeInsets.all(8.0),
child: Row(
children: [
Expanded(
child: TextField(
controller: _controller,
decoration: InputDecoration(
hintText: 'Enter new todo',
),
),
),
IconButton(
icon: Icon(Icons.add),
onPressed: _addTodo,
),
],
),
),
Expanded(
child: ListView.builder(
itemCount: _todos.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(_todos[index]),
trailing: IconButton(
icon: Icon(Icons.delete),
onPressed: () => _removeTodo(index),
),
);
},
),
),
],
);
}
}
setState 사용 시 주의사항
비동기 작업에서의 setState
class _MyWidgetState extends State<MyWidget> {
String _data = '';
// 잘못된 사용
void _fetchData() async {
final response = await API.getData();
if (!mounted) return; // 위젯이 여전히 트리에 있는지 확인
setState(() {
_data = response;
});
}
}
성능 고려사항
// 비효율적인 방법
setState(() {
for (var i = 0; i < 1000; i++) {
// 상태 변경
}
});
// 효율적인 방법
void _updateMultipleStates() {
final updates = List.generate(1000, (index) => index);
setState(() {
// 한 번에 모든 상태 업데이트
_items.addAll(updates);
});
}
setState의 장단점
장점
- 사용이 간단하고 직관적
- 작은 규모의 앱에서 효과적
- Flutter의 기본 제공 기능으로 추가 패키지 불필요
단점
- 복잡한 상태 관리에는 적합하지 않음
- 위젯 트리가 깊어지면 상태 전달이 어려움
- 전역 상태 관리에는 부적합
마치며
setState는 Flutter에서 가장 기본적인 상태 관리 방식입니다. 작은 규모의 앱이나 로컬 상태 관리에는 매우 효과적이지만, 앱이 커지고 복잡해질수록 다른 상태 관리 솔루션을 고려해야 할 수 있습니다. 다음 포스트에서는 Provider나 Riverpod와 같은 더 강력한 상태 관리 솔루션에 대해 알아보도록 하겠습니다.
'Language > Flutter' 카테고리의 다른 글
[Flutter] 위젯 생명주기(Lifecycle) (0) | 2024.11.20 |
---|---|
[Dart] fold() 메소드 (0) | 2024.11.19 |
[Dart] Spread 연산자(...) (0) | 2024.11.17 |
[Dart] Null Safety (0) | 2024.11.16 |
[Dart] var, dynamic, final, late, const 키워드 (0) | 2024.11.15 |