1. 상태(객체의 상태)
- 모든 객체는 상태를 가질 수 있다
class HomePage extends StatelessWidget {
int num = 1;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Placeholder(),
);
}
}
상태 변경 기본 공부 코드
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: HomePage());
}
}
class HomePage extends StatefulWidget {
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
// 1. 상태
int num = 1;
// 2. 행위
void increase() {
num++;
setState(() {});
}
@override
Widget build(BuildContext context) {
print("rebuild 됨");
return Scaffold(
appBar: AppBar(),
body: Center(child: Text("${num}", style: TextStyle(fontSize: 50))),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: increase,
),
);
}
}
2. 불변 상태, 변하는 상태
Stateless
class HomePage extends StatelessWidget {
int num = 1;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Placeholder(),
);
}
}
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: Placeholder(),
);
}
}
3. 상태가 변할 때 그림이 reload 될 때(stateless, stateful)
- 상태가 있어도 불변이면 상태가 없는 것으로 친다
- 상태가 변할 때 상태가 있는 것으로 친다
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp();
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: HomePage(),
);
}
}
// 1. Page (화면 - Scaffold 가져야 한다)
// 2. 클래스 (오브젝트 - 상태가 있다)
// 3. StatelessWidget -> InMutableWidget(불변위젯)
// -> 자식의 상태를 관리 하지 않는다
class HomePage extends StatefulWidget {
@override
State<HomePage> createState() => _HomePageState(); // 내부적으로 자동으로 createState를 실행한다
}
class _HomePageState extends State<HomePage> {
// 1. 상태
int num = 1;
// 2. 행위
void increase() {
num += 1;
print("num : $num");
setState(() {}); // 다시 _HomePageState를 new 해서 build를 새로 한는 것
}
@override
Widget build(BuildContext context) {
print("빌드됨! : $num");
return Scaffold(
body: Center(child: Text("$num", style: TextStyle(fontSize: 30))),
floatingActionButton: FloatingActionButton(
onPressed: increase,
child: Icon(Icons.add),
),
);
}
}


4. context
- 화면 그 자체, 화면 데이터를 말함
- 도화지
- 각 위젯이 가지는 것

컨텍스트 분리 x
- StatefulWidget 으로 상태를 관리함
- setState() 를 실행해서 다시 build 함
- 상태 변경을 하면 HomePage 전체가 다시 그려짐
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
int num = 1;
@override
Widget build(BuildContext context) {
return Container(
color: Colors.yellow,
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
children: [
Expanded(
child: Container(
color: Colors.red,
child: Align(
child: Text(
"${num}",
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 100,
decoration: TextDecoration.none,
),
),
),
),
),
Expanded(
child: Container(
color: Colors.blue,
child: Align(
child: ElevatedButton(
style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
onPressed: () {
num++;
setState(() {});
},
child: Text(
"증가",
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 100,
),
),
),
),
),
),
],
),
),
);
}
}
컨텍스트 분리o 상태 변경x
- 위젯을 분리 했으나 상태 변경을 할 수 없음
- 상태를 가지는 위젯과 상태를 변경하는 위젯이 분리되어 사용이 불가능함
- 그림을 다시 그릴 수 없음
- 상태를 변경하고 싶으면 상태를 가지는 쪽에 상태를 변경하는 방법이 있어야 함
- 디자인이 망가짐
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: HomePage(),
);
}
}
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
color: Colors.yellow,
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
children: [
Expanded(
child: Header(),
),
Expanded(
child: Bottom(),
),
],
),
),
);
}
}
class Header extends StatefulWidget {
@override
State<Header> createState() => _HeaderState();
}
class _HeaderState extends State<Header> {
// 1. 상태
int num = 1;
@override
Widget build(BuildContext context) {
return Container(
color: Colors.red,
child: Align(
child: Text(
"${num}",
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 100,
decoration: TextDecoration.none,
),
),
),
);
}
}
class Bottom extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
color: Colors.blue,
child: Align(
child: ElevatedButton(
style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
onPressed: () {},
child: Text(
"증가",
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 100,
),
),
),
),
);
}
}
컨텍스트 분리o 상태 변경o 비용x(부모가 다시 그려짐)
- 위젯을 분리했으나 상태 변경을 하려면 부모가 상태 관리를 하고 자식들에게 전달하는 방법을 사용해야 함
- 상태 관리를 하면서 위젯을 분리하는데 성공 함
- 그러나 결국 부모가 다시 build 하기 때문에 비용이 낭비됨
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
int num = 1;
void increase() {
num++;
setState(() {});
}
@override
Widget build(BuildContext context) {
return Container(
color: Colors.yellow,
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
children: [
Expanded(child: HeaderPage(num)),
Expanded(child: MiddlePage(1)),
Expanded(child: BottomPage(increase)),
],
),
),
);
}
}
class HeaderPage extends StatelessWidget {
int num;
HeaderPage(this.num);
@override
Widget build(BuildContext context) {
print("header");
return Container(
color: Colors.red,
child: Align(
child: Text(
"${num}",
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 100,
decoration: TextDecoration.none,
),
),
),
);
}
}
class BottomPage extends StatelessWidget {
Function increase;
BottomPage(this.increase);
@override
Widget build(BuildContext context) {
print("bottom");
return Container(
color: Colors.blue,
child: Align(
child: ElevatedButton(
style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
onPressed: () {
print("버튼 클릭됨");
increase();
},
child: Text(
"증가",
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 100,
),
),
),
),
);
}
}
class MiddlePage extends StatelessWidget {
final num;
const MiddlePage(this.num);
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
);
}
}
const 를 사용해 부분을 다시 build 하지 않게 한다
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
int num = 1;
void increase() {
num++;
setState(() {});
}
@override
Widget build(BuildContext context) {
print("노란색");
return Container(
color: Colors.yellow,
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
children: [
Expanded(child: HeaderPage(num)),
Expanded(child: const MiddlePage(1)),
Expanded(child: const MiddlePage(1)),
Expanded(child: BottomPage(increase)),
],
),
),
);
}
}
class HeaderPage extends StatelessWidget {
int num;
HeaderPage(this.num);
@override
Widget build(BuildContext context) {
print("header");
return Container(
color: Colors.red,
child: Align(
child: Text(
"${num}",
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 100,
decoration: TextDecoration.none,
),
),
),
);
}
}
class BottomPage extends StatelessWidget {
Function increase;
BottomPage(this.increase);
@override
Widget build(BuildContext context) {
print("bottom");
return Container(
color: Colors.blue,
child: Align(
child: ElevatedButton(
style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
onPressed: () {
print("버튼 클릭됨");
increase();
},
child: Text(
"증가",
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 100,
),
),
),
),
);
}
}
class MiddlePage extends StatelessWidget {
final int num;
const MiddlePage(this.num);
@override
Widget build(BuildContext context) {
print("middle $num");
print("middle ${this.hashCode}");
return Container(
color: Colors.white,
);
}
}
5. const
코드
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
int num = 1;
void increase() {
num++;
setState(() {});
}
@override
Widget build(BuildContext context) {
print("노란색");
return Container(
color: Colors.yellow,
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
children: [
Expanded(child: HeaderPage(num)),
Expanded(child: const MiddlePage(1)),
Expanded(child: const MiddlePage(1)),
Expanded(child: BottomPage(increase)),
],
),
),
);
}
}
class HeaderPage extends StatelessWidget {
int num;
HeaderPage(this.num);
@override
Widget build(BuildContext context) {
print("header");
return Container(
color: Colors.red,
child: Align(
child: Text(
"${num}",
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 100,
decoration: TextDecoration.none,
),
),
),
);
}
}
class BottomPage extends StatelessWidget {
Function increase;
BottomPage(this.increase);
@override
Widget build(BuildContext context) {
print("bottom");
return Container(
color: Colors.blue,
child: Align(
child: ElevatedButton(
style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
onPressed: () {
print("버튼 클릭됨");
increase();
},
child: Text(
"증가",
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 100,
),
),
),
),
);
}
}
class MiddlePage extends StatelessWidget {
final int num;
const MiddlePage(this.num);
@override
Widget build(BuildContext context) {
print("middle $num");
print("middle ${this.hashCode}");
return Container(
color: Colors.white,
);
}
}
- 불변이다
- 고정 디자인
- 다시 build 할 때 const 인 위젯은 다시 그리지 않는다
- 동일한 위젯이 존재하면 재사용함 → 플러터는 재사용하지 않고 다시 만듦
- 동일한 위젯을 찾아서 재사용하는 프로그램도 있음
- 플러터는 찾는 과정이 낭비라고 생각해서 새로 만드는 것 같음
사용 방법
@override
Widget build(BuildContext context) {
print("노란색");
return Container(
color: Colors.yellow,
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
children: [
Expanded(child: HeaderPage(num)),
Expanded(child: const MiddlePage(1)),
Expanded(child: const MiddlePage(1)),
Expanded(child: BottomPage(increase)),
],
),
),
);
}
class MiddlePage extends StatelessWidget {
final int num;
const MiddlePage(this.num);
@override
Widget build(BuildContext context) {
print("middle $num");
print("middle ${this.hashCode}");
return Container(
color: Colors.white,
);
}
}
- 변수를 final 로 만들면 처음 초기화 되고 변하지 않는다
6. 위젯 트리와 비용
코드
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: HomePage(),
);
}
}
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
print("홈페이지 빌드");
return Scaffold(
body: Middle(),
);
}
}
// 가짜 부모 << 혼자만 Stateful 이다
class Middle extends StatefulWidget {
const Middle({
super.key,
});
@override
State<Middle> createState() => _MiddleState();
}
class _MiddleState extends State<Middle> {
int num = 1;
void increase() {
num++;
setState(() {});
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Expanded(
child: Top(num),
),
Expanded(
child: Bottom(increase),
),
],
);
}
}
class Bottom extends StatelessWidget {
Function increase;
Bottom(this.increase);
@override
Widget build(BuildContext context) {
return Center(
child: Container(
child: ElevatedButton(
onPressed: () {
increase();
},
child: Icon(Icons.add),
),
),
);
}
}
class Top extends StatelessWidget {
int num;
Top(this.num);
@override
Widget build(BuildContext context) {
return Center(
child: Container(
child: Text("$num", style: TextStyle(fontSize: 50)),
),
);
}
}
- 페이크 트리를 하나 부모로 만들어서 사용
a 에 상태를 표시하고 c 에 상태 변경 행위를 넘겨주면(fake ❌)
- HomePage 가 다시 rebuild 되면서 전체 그림이 다시 그려짐
- 관계 없는 다른 위젯도 다시 그려짐
- 자원 낭비

a 에 상태를 표시하고 c 에 상태 변경 행위를 넘겨주면(fake ⭕)
- fake 부모가 다시 rebuild 되면서 fake, a, c 만 다시 rebuild 됨
- 필요한 부분만 변경 가능함
- 자원 낭비가 줄어듦


- 다음과 같은 트리 구조도 있기 때문에 완벽하게 자원을 사용했다고 할 순 없다
- 그래도 b 트리는 다시 그려지지 않기 때문에 그나마 낫다
7. 상태 관리를 외부에서 하는 법

- 화면을 그리는 프로그램은 모두 상태 관리 라이브러리를 사용한다
- 옵저버 패턴으로 만들어졌다
- 구독자, 제공자(퍼블리셔)
- 퍼블리셔는 상태 관리 라이브러리에게 한번만 요청하면 된다 (초록선)
- 구독자는 상태 관리 라이브러리에게 계속 연결 되어있어야 한다 (파란선)
- 연결 방법
- read → 한번 값을 받고 끝
- watch → 계속 값을 받을 수 있음
riverpod 세팅
import 'package:flutter_riverpod/flutter_riverpod.dart';
// 1. 창고 데이터 타입(int 면 안 만들어도 됨)
// 관리 타입은 1가지만 가능함 여러개면 클래스 타입 안에 넣어서 사용함
// 2. 창고
class HomeVM extends Notifier<int> {
// 창고가 만들어질 때 초기화 메서드(return 하는 값을 창고가 state 로 관리함)
@override
int build() {
return 1;
}
}
// 3. 창고 관리자
final homeProvider = NotifierProvider<HomeVM, int>(() {
return HomeVM();
});
Share article