플러터(Flutter)를 배우면서 가장 많이 나오는 개념이 바로 상태(State)입니다.
이번 글에서는 상태가 무엇인지,
StatelessWidget
과 StatefulWidget
의 차이, context
, setState
, 그리고 const
까지 정리해봅니다.1. 상태(State)란?
- 상태(State) = 객체가 가지고 있는 값, 그리고 그 값이 변할 수 있는지 여부
- 모든 객체는 상태를 가진다.
- 예)
int num = 1;
→ 상태는 1
- 중요한 건 상태가 변하는가, 변하지 않는가 이다.
👉 Setter가 없는 객체 = 불변 상태(Immutable)
👉 Setter가 있어 값이 바뀔 수 있는 객체 = 가변 상태(Mutable)
서버의 상태 vs Flutter의 상태
- 서버에서의 상태
- "사용자의 세션, 로그인 정보, DB에 저장된 값" 같은 데이터의 지속성(Persistence)을 의미
- 예) 로그인 세션 유지, 장바구니 상태, DB 레코드 값
- 상태가 서버 메모리나 데이터베이스에 계속 보존됨
- Flutter에서의 상태
- 화면(UI)이 현재 어떤 값을 보여주고 있는지를 의미
- 예)
Counter 앱
에서 숫자 값, 입력창에 적힌 텍스트, 체크박스 선택 여부 - 앱 실행 중 메모리에만 존재하며,
setState()
나 상태관리 툴(Riverpod, Bloc 등)을 통해 UI 리빌드와 직결
정리
서버의 상태는 데이터의 지속성과 관리에 초점이 있고,
Flutter의 상태는 UI가 지금 어떻게 그려져야 하는가에 초점이 있습니다.
2. Stateless vs Stateful
- 상태가 변하지 않는 위젯 →
StatelessWidget
- 상태가 변하는 위젯 →
StatefulWidget
즉, Stateless와 Stateful의 차이는 상태의 변경 가능 여부라고 볼 수 있습니다.
예시
class HomePage extends StatefulWidget {
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
int num = 1;
void increase() {
num++;
setState(() {}); // 상태 변경 후 화면 리빌드
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(child: Text("$num", style: TextStyle(fontSize: 50))),
floatingActionButton: FloatingActionButton(onPressed: increase),
);
}
}
increase()
가 실행되면 num
이 바뀌고, setState()
가 호출되어 화면이 다시 그려집니다.setState()
Stateful에서만 가능하고, createState()를 호출한다(리빌드) 3. Context란?
BuildContext
= 도화지(Canvas) 같은 개념
- 위젯 트리에서 위젯이 어디에 위치하는지 알려주는 좌표 역할을 합니다.
👉 따라서
context
를 통해 부모 위젯, 테마, 미디어쿼리 정보 등에 접근할 수 있습니다.
4. 상태와 행위의 분리 문제
상태와 행위가 서로 다른 위젯에 있으면 문제가 생깁니다.
class Header extends StatefulWidget {
@override
State<Header> createState() => _HeaderState();
}
class _HeaderState extends State<Header> {
int num = 1; // 상태
@override
Widget build(BuildContext context) => Text("$num");
}
class Bottom extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () {}, // 행위
child: Text("증가"),
);
}
}
여기서는 상태는 Header에 있고, 행위(증가 버튼)는 Bottom에 있어서 서로 연결되지 않음 → 동작이 안 됨.
해결 방법
- 부모가
StatefulWidget
이 되고, 상태와 행위를 관리
- 자식 위젯에는 상태 값과 함수(행위)를 props로 전달
class _HomePageState extends State<HomePage> {
int num = 1;
void increase() => setState(() => num++);
@override
Widget build(BuildContext context) {
return Column(
children: [
Header(num),
Bottom(increase),
],
);
}
}

5. const의 의미
const
는 불변 객체를 의미합니다.
- 한 번 만들어지면 리빌드되어도 다시 생성되지 않음 (성능 최적화 효과)
const
를 쓰려면 내부 필드도 반드시final
이어야 함.
class Middle extends StatelessWidget {
final int num;
const Middle(this.num);
@override
Widget build(BuildContext context) {
print("middle $num");
return Container(color: Colors.white);
}
}
👉 같은
const Middle(1)
을 여러 번 호출해도 동일한 인스턴스를 재사용합니다.6. 위젯 트리와 비용

- 가짜 Stateful 위젯을 만든다.
setState()
를 호출하면 해당 위젯의 전체 하위 트리(children가 다시 빌드됩니다.)
- 그래서 상태와 행위는 가급적 묶어두는 것이 비용 관리 측면에서 유리합니다.
- 불변 위젯(
const
)을 적극적으로 사용하면 불필요한 리빌드를 줄일 수 있습니다.
📌 정리
- 모든 객체는 상태를 가진다 → 변하는지, 불변인지가 중요
- Stateless = 불변, Stateful = 변할 수 있음
context
는 위젯의 도화지(위치 정보)
setState()
= 상태 변경 후 리빌드 트리거
- 상태와 행위는 부모에서 묶어 자식에게 전달 (Props)
const
는 성능 최적화의 핵심 → 동일 객체 재사용
- 위젯 트리 전체 리빌드를 줄이려면 구조 설계가 중요
Share article