Flutter에서 앱을 만들다 보면 “왜 버튼을 눌러도 화면이 안 바뀌지?”
혹은 “값은 변경되었는데 UI에 반영이 안 되네?” 같은 경험을 하게 됩니다.
그 이유는 "상태(State)"를 제대로 관리하지 않아서인데요.
이번 글에서는 Flutter의 상태 관리를 시작하는 가장 첫 개념,
StatefulWidget과 StatelessWidget의 차이를 쉽게 설명해드릴게요.
❓ 상태(State)란?
앱이 시간에 따라 변하는 데이터를 의미합니다.
예를 들어:
- 버튼을 누른 횟수
- 체크박스의 선택 여부
- 입력한 텍스트
- 로그인 상태 여부 등
이런 변화는 모두 “상태”이며, 이를 UI에 반영하려면 StatefulWidget이 필요합니다.
🧱 StatelessWidget – 상태가 없는 위젯
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Text('변하지 않는 텍스트');
}
}
@override
Widget build(BuildContext context) {
return Text('변하지 않는 텍스트');
}
}
- 변화가 없는 정적인 UI에 적합
- 버튼 클릭, 값 변경 등 UI 업데이트가 필요 없는 화면에 사용
예시: 로고, 타이틀, 고정된 정보 등
🔄 StatefulWidget – 상태가 있는 위젯
class MyCounter extends StatefulWidget {
@override
_MyCounterState createState() => _MyCounterState();
}
class _MyCounterState extends State<MyCounter> {
int count = 0;
void increase() {
setState(() {
count++;
});
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Text('카운터: $count'),
ElevatedButton(
onPressed: increase,
child: Text('증가'),
),
],
);
}
}
@override
_MyCounterState createState() => _MyCounterState();
}
class _MyCounterState extends State<MyCounter> {
int count = 0;
void increase() {
setState(() {
count++;
});
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Text('카운터: $count'),
ElevatedButton(
onPressed: increase,
child: Text('증가'),
),
],
);
}
}
✅ 핵심 포인트
- 상태 변수는 State 클래스 내부에 선언
- setState()를 호출해야 화면이 다시 그려짐 (rebuild)
- 사용자와 상호작용하며 변화하는 화면에 적합
예시: 카운터, 입력값 실시간 반영, 좋아요 버튼 등
📊 두 위젯 비교 요약
항목StatelessWidgetStatefulWidget
| 상태(State) | 없음 | 있음 |
| UI 변경 | 불가능 | 가능 |
| 용도 | 고정된 UI | 사용자와 상호작용 |
| 예시 | 로고, 타이틀 | 버튼 클릭, 텍스트 입력 |
💻 실습 예제 – 카운터 앱
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: CounterPage(),
);
}
}
class CounterPage extends StatefulWidget {
@override
_CounterPageState createState() => _CounterPageState();
}
class _CounterPageState extends State<CounterPage> {
int count = 0;
void increase() {
setState(() {
count++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('카운터 예제')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('현재 카운트: $count', style: TextStyle(fontSize: 24)),
SizedBox(height: 20),
ElevatedButton(
onPressed: increase,
child: Text('1 증가'),
),
],
),
),
);
}
}
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: CounterPage(),
);
}
}
class CounterPage extends StatefulWidget {
@override
_CounterPageState createState() => _CounterPageState();
}
class _CounterPageState extends State<CounterPage> {
int count = 0;
void increase() {
setState(() {
count++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('카운터 예제')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('현재 카운트: $count', style: TextStyle(fontSize: 24)),
SizedBox(height: 20),
ElevatedButton(
onPressed: increase,
child: Text('1 증가'),
),
],
),
),
);
}
}
📌 setState() 꼭 알아두기!
setState(() {
// 상태 변경 로직
});
// 상태 변경 로직
});
- 호출하면 build 메서드가 다시 실행되어 UI가 갱신됨
- 변경된 값이 화면에 반영되는 핵심 메커니즘
🧠 마무리 요약
개념설명
| StatelessWidget | 변하지 않는 UI |
| StatefulWidget | 사용자 상호작용으로 변하는 UI |
| setState() | 상태를 변경하고 UI를 다시 그림 |
Stateful과 Stateless의 차이를 이해하는 것은
Flutter 상태 관리, 나아가 Provider, Riverpod 같은 고급 기술로 가는 첫걸음입니다.