- StateFul : 별도의 State객체를 통해 위젯에 색깔, 형태 등을 바꿀 수 있는 위젯
- Stateless : 정적으로, 화면에 위젯을 다시 그리려면, 수동으로 화면을 리로드 해야되는 위젯
import 'package:flutter/material.dart';
void main() {
runApp(
MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Stateful , Stateless'),
),
body: Body(),
),
),
);
}
class Body extends StatelessWidget{
const Body({super.key});
@override
Widget build(BuildContext context){
return Column(
children: [
ExampleStateless(),
ExampleStateful(),
],
);
}
}
class ExampleStateless extends StatelessWidget{
const ExampleStateless({super.key});
@override
Widget build(BuildContext context) {
return Container(
color: Colors.red,
);
}
}
class ExampleStateful extends StatefulWidget {
const ExampleStateful({super.key});
@override
State<ExampleStateful> createState() => _ExampleStatefulState();
}
class _ExampleStatefulState extends State<ExampleStateful> {
@override
Widget build(BuildContext context) {
return Container(
color: Colors.blue,
);
}
}
Stateful의 코드는 Stateless와 다르게 stful을 치고 enter를 누르면 자체적인 클래스가 생성이 된다.
하지만 위의 코드대로 했을 때 Column으로 container를 두 개를 나누어 놓고 위의 container에는 red, 아래의 container에는 blue의 색을 설정했다면 그 설정대로 결과가 나와야 겠지만
결과는 이렇다

왜냐하면 container에 사이즈를 저장하지 않았기 때문이다.
이를 해결하려면 저번 시간에 했던것 처럼 container를 Expanded로 감싸야 한다.
class ExampleStateless extends StatelessWidget{
const ExampleStateless({super.key});
@override
Widget build(BuildContext context) {
return Expanded(
child: Container(
color: Colors.red,
),
);
}
}
class ExampleStateful extends StatefulWidget {
const ExampleStateful({super.key});
@override
State<ExampleStateful> createState() => _ExampleStatefulState();
}
class _ExampleStatefulState extends State<ExampleStateful> {
@override
Widget build(BuildContext context) {
return Expanded(
child: Container(
color: Colors.blue,
),
);
}
}
이렇게 container를 Expanded로 감싸 container에 사이즈를 저장한다면

위와 같이 우리가 목표로 했던 설정이 나오게 된다.
class ExampleStateful extends StatefulWidget {
const ExampleStateful({super.key});
@override
State<ExampleStateful> createState() => _ExampleStatefulState();
}
class _ExampleStatefulState extends State<ExampleStateful> {
int index = 0;
@override
Widget build(BuildContext context) {
return Expanded(
flex: 1,
child: GestureDetector(
onTap: () {
if(index==5) {
index = 0;
return;
}
index++;
},
child: Container(
color: Colors.blue,
child: Center(
child: Text('$index'),),
),
),
);
}
}
해당 코드는 위의 코드에서 GestureDetector를 통해 터치를 하면 index가 증가하는 코드를 추가하여 stateful을 이용한 것이다.

보면 나는 5번을 클릭하여 index의 숫자 5가 올라가 있는데 사실 이건 실시간으로 올라간 것이 아니라 클릭한 후 재시작을 하여 이미 클릭된 기록이 추가되어 화면에 보이는 것이다.
이렇게 실시간으로 index의 변화가 보이지 않은 이유는 state가 변했다는 것을 위젯에게 알려줘야될 필요가 있기 때문이다.
그걸 위한 코드가 setState이다.
setstate
- setstate가 선언되면 상위 위젯으로 알려져 위젯이 다시 빌드 된다.
@override
Widget build(BuildContext context) {
return Expanded(
flex: 1,
child: GestureDetector(
onTap: () {
setState(() {
if(index==5) {
index = 0;
return;
}
index++;
});
},
child: Container(
color: Colors.blue,
child: Center(
child: Text('$index'),),
),
),
);
}
}
setState를 추가한 부분만 가져온 코드이다.
위와같이 setstate를 추가한 후 실행시켜주면 클릭할 때마다 index가 오르는게 위젯에서 보이고 최대 5까지 증가하고 5이상에선 0으로 초기화 되는 위젯을 볼 수 있다.
또한 color에 대한 코드를
color: Colors.blue.withOpacity(index/5)
이런식으로 변경한다면 클릭할 때마다 인덱스의 숫자와 그에 맞춰 숫자가 올라갈 때마다 색이 짙어지게 할 수도 있다.
또한 재시작을 했을 때 index의 값이 변하면 안될 때는
class _ExampleStatefulState extends State<ExampleStateful> {
late int index;
@override
void initState() {
super.initState();
index = 5;
}
위와 같이 alt+o를 눌러 initstate를 선택하고 위의 코드처럼 만들어주면 된다.
그러면 재시작하였을 때 initState안에서 설정한 index값으로 실행하게 되고 super.iniState()를 통해 상위 State에도 initState가 같이 적용된다.
Stateful 위젯과 Stateless 위젯의 차이점
- 위젯 안에서 state가 변경됨에 따라 화면이 바뀌는 위젯 : stateful 위젯
- 그렇지 않고 앱 바와 같은 형태로 고정적인 한 색깔(화면)만 보이는 위젯 : stateless 위젯