목표
Flutter의 StatelessWidget과 StatefulWidget의 차이점을 알아보고, 동적 UI를 구현합니다.
1.1 소개
Flutter에서 모든 UI는 위젯으로 구성됩니다. 위젯은 크게 StatelessWidget(변하지 않는 UI)과 StatefulWidget(변화하는 UI)으로 나뉩니다. 이번에는 이 둘의 차이점을 알아보고, 상태를 관리하는 방법을 배워 보겠습니다.
1.2 사전 준비
- 이전 강좌에서 만든 프로젝트를 이어서 사용하거나 새 프로젝트를 생성하세요.
- Flutter 개발 환경이 올바르게 설정되어 있어야 합니다.
1.3 핵심 내용
1단계: StatelessWidget
StatelessWidget은 상태가 변하지 않는 정적인 UI를 정의합니다.
새 프로젝트 또는 이전 프로젝트의 소스코드를 다음과 같이 작성합니다.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('StatelessWidget 예제'),
),
body: const Center(
child: Text(
'이 텍스트는 변경되지 않습니다.',
style: TextStyle(fontSize: 20),
),
),
),
);
}
}

결과
- Text 위젯은 변하지 않고 항상 동일한 텍스트를 표시합니다.
2단계: StatefulWidget 이해하기
StatefuilWidget은 상태가 변할 수 있는 동적인 UI를 정의합니다.
다음과 같이 코드를 수정하여 UI가 어떻게 변화하는지 확인해 보세요.
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('StatefulWidget 예제')),
body: const CounterWidget(),
),
);
}
}
class CounterWidget extends StatefulWidget {
const CounterWidget({super.key});
@override
State<CounterWidget> createState() => _CounterWidgetState();
}
class _CounterWidgetState extends State<CounterWidget> {
int _counter = 0;
void _CounterUp() {
setState(() {
_counter++; //상태 업데이트
});
}
void _CounterZero() {
setState(() {
_counter = 0;
});
}
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'현재 값: $_counter',
style: const TextStyle(fontSize: 30),
),
SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
_CounterUp();
},
child: const Text('값 증가'),
),
SizedBox(
width: 15,
),
ElevatedButton(
onPressed: () {
_CounterZero();
},
child: Text('값 초기화'),
),
],
)
],
),
);
}
}

결과
- 버튼을 누를 때마다 _counter 값이 증가하고, UI가 업데이트됩니다.
- ‘값 초기화’ 버튼을 누르면 _counter 값이 0으로 초기화 됩니다.
- setState()를 호출하여 상태를 업데이트하고 화면을 다시 그립니다.
| 구분 | StatelessWidget | StatefulWidget |
| 상태 관리 | 상태를 가지지 않음 | 상태를 가질 수 있음 |
| UI 변경 | 고정된 UI, 변경되지 않음 | 상태 변화에 따라 UI가 동적으로 변경됨 |
| 용도도 | 단순 텍스트, 아이콘 등 | 동적인 UI, 버튼 클릭, 입력 폼 등 |
1.5 실습
1단계: 상태 변화
- StatefulWidget을 사용하여 버튼 클릭 시 배경색이 변겅되도록 구현해보세요.
2단계: ‘값 감소’ 버튼 추가
- 값 감소’ 버튼을 추가하여 _counter 값을 감소 시킬 수 있도록 수정 해보세요.
3단계: 조건 추가
- _counter 값이 10 이상일 경우 “최대값에 도달했습니다.”라는 텍스트를 표시되게 해보세요.
1.6 실습 결과

1.7 전체 소스코드
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('StatefulWidget 예제')),
body: const CounterWidget(),
),
);
}
}
class CounterWidget extends StatefulWidget {
const CounterWidget({super.key});
@override
State<CounterWidget> createState() => _CounterWidgetState();
}
class _CounterWidgetState extends State<CounterWidget> {
int _counter = 0;
String strDisplay = "";
void _CounterUp() {
setState(() {
if (_counter >= 10) {
return;
}
_counter++;
});
}
void _CounterDown() {
setState(() {
if (_counter <= 0) {
return;
}
_counter--;
});
}
void _CounterZero() {
setState(() {
_counter = 0;
});
}
void stateMessage() {
if (_counter >= 10) {
strDisplay = "최대 값에 도달했습니다";
} else if (_counter < 10) {
strDisplay = "";
}
}
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'현재 값: $_counter',
style: const TextStyle(fontSize: 30),
),
SizedBox(height: 5),
Text(
strDisplay,
style: TextStyle(fontSize: 20),
),
SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
_CounterUp();
stateMessage();
},
child: const Text('값 증가'),
),
SizedBox(width: 15),
ElevatedButton(
onPressed: () {
_CounterDown();
stateMessage();
},
child: Text('값 감소'),
),
SizedBox(
width: 15,
),
ElevatedButton(
onPressed: () {
_CounterZero();
},
child: Text('값 초기화'),
),
],
)
],
),
);
}
}
1.8 결론
이번에는 StatelessWidget과 StatefulWidget의 차이점을 알아보고, StatefulWidget을 사용하여 상태를 관리하는 학습했습니다. 이를 통해 Flutter의 동적인 UI를 구현할 수 있습니다.
다음 단계에서는 Flutter에서 사용자 입력 폼을 학습하고, 입력 데이터를 처리해 보겠습니다.
1.9 Q & A
Q: StatefulWidget에서 setState()를 언제 사용해야 하나요?
A: UI가 변경될 필요가 있는 경우에만 setState()를 호출합니다. 불필요한 호출은 성능에 영향을 줄 수 있습니다.
Q: StatelessWidget에서 상태를 변경하려면 어떻게 해야 하나요?
A: 상태를 변경하려면 StatefulWidget으로 전환해야 합니다. StatelessWidget은 상태를 가지지 않으므로 동적 변경이 불가능합니다.
감사합니다.
댓글 남기기