프로젝트를 진행하면서 원하는대로 UI가 업데이트 되지 않는 상황을 몇번 겪었다. 내가 상태관리를 제대로 이해하지 못한 것 같다는 생각이 들었고, ‘상태관리’의 개념부터 정확하게 알고 넘어가야겠다는 생각을 했다. 다행히 플러터 공식 문서에 친절한 설명이 있었다.
요약하자면 상태관리란 ‘앱 데이터와 UI 간의 동기화를 유지하는 작업’이다. 사용자와의 상호작용 등으로 앱의 데이터가 변경 되었을 때, 이에 맞는 UI 업데이트가 이루어지도록 하는 것이다.
아래는 플러터 공식문서 를 읽으면서 정리했던 내용들이다.
일시적인 상태와 앱 상태의 차이점
가장 넓은 의미로 앱이 실행될 때 메모리에 올라가 있는 모든 것을 의미한다. (ex. asset, 변수, font, …) 의미를 조금 더 축소해서 분류하자면 아래 두가지로 나뉜다.
UI를 재구축하는데에 필요한 모든 정보. 이건 개발자가 관리하는 것이 아니고 Flutter FrameWork가 관리한다.
ephemeral state
하나의 위젯에 담아둘 수 있는 상태 (UI state 또는 local state 라고도 불림)
예를 들어 다음과 같은 것들이 일시적 상태다.
PageViewBottomNavigationBarclass MyHomepage extends StatefulWidget {
const MyHomepage({super.key});
@override
State<MyHomepage> createState() => _MyHomepageState();
}
class _MyHomepageState extends State<MyHomepage> {
int _index = 0;
@override
Widget build(BuildContext context) {
return BottomNavigationBar(
currentIndex: _index,
onTap: (newIndex) {
setState(() {
_index = newIndex;
});
},
// ... items ...
);
}
}
번역을 돌려보면 알겠지만... 상태변경을 setState 로 해도 되나 싶었는데, 해당 위젯에서만 사용하는 index 이기 때문에, setState() 와 State 클래스 내부에 필드를 선언하는 것은 괜찮다는 설명이 덧붙여있었다.
일시적이지 않고 앱의 여러 부분에서 공유하며 사용자 세션 간에도 유지하려는 상태를 애플리케이션 상태 (공유 상태라고도 함)
예시
앱 상태 관리에 대해서는 정답이 없다. 어떤 방법을 선택할지는 앱의 복잡성 및 특성, 팀의 이전 경험, 그리고 다른 여러 요소들에 따라 달라질 수 있다는 이야기도 쓰여있다.
For managing app state, you'll want to research your options. Your choice depends on the complexity and nature of your app, your team's previous experience, and many other aspects. Read on.

아래는 provider 를 사용해서 state 를 관리하는 간단한 예제다.
요약하자면 MyCategory 페이지에 있는 카테고리들(MyListItem) 중에 하나를 선택 하고, 그 선택된 카테고리를 Cart 페이지에서 보여주도록 하는 내용의 예제이다. 해야할 일은 아래와 같다.
그렇다면 다른 페이지에 있는 위젯을 어떻게 가져와서 띄우는가?
플러터에서 기본적으로 다른 페이지의 하위 위젯은 접근 불가하다. 하지만 그걸 가능하게 해주는 것이 provider 다. 공식 사이트에 첨부된 아래 이미지를 보면 provider 의 역할에 대해 더 이해하기가 쉽다.

With
provider, you don't need to worry about callbacks orInheritedWidgets. But you do need to understand 3 concepts:
- ChangeNotifier
- ChangeNotifierProvider
- Consumer
provider를 사용하면 콜백이나 InheritedWidgets에 대해 걱정할 필요가 없다. 그러나 위젯을 재빌드하려면 하위 위젯에 context를 전달하는 것 외에도, 위젯이 변경 사항을 알 수 있도록 알려주는 역할이 필요하다. 그 역할을 ChangeNotifier 가 수행한다.
ChangeNotifier 사용은 간단하다. 우선 ChangeNotifier 를 상속받은 CartModel 클래스를 선언하면 된다.
CartModel은 장바구니에 담긴 상품 목록, 각 상품의 수량, 총 가격 등을 관리하며, 상태 변경 시 이를 알리기 위해 ChangeNotifier를 사용한다. 이렇게 Model 클래스를 두면 상태와 UI를 분리해서 유지 관리하기 쉬워진다.
그리고 notifyListeners(); 를 호출하면 위젯에게 ‘model 에 변경 사항이 있으니 재빌드 하라’고 알리게 된다.
import 'package:flutter/material.dart';
class CartModel extends ChangeNotifier {
// 장바구니에 담긴 상품 목록
final List<String> _items = [];
// 장바구니에 있는 상품들을 읽을 수 있는 getter
List<String> get items => _items;
// 상품 추가
void addItem(String item) {
_items.add(item);
notifyListeners(); // 상태가 변경되었음을 알림
}
// 상품 삭제
void removeItem(String item) {
_items.remove(item);
notifyListeners(); // 상태가 변경되었음을 알림
}
// 장바구니 비우기
void clearCart() {
_items.clear();
notifyListeners();
}
}
아까 위에서 Provider 의 역할에 대해서 보았었다. ChangeNotifierProvider 는 이름에서도 알 수 있듯이 위젯 트리에서 ChangeNotifier가 필요할 때 하위 위젯들이 이를 접근할 수 있도록 해준다. 위젯에서 ChangeNotifier 사용을 위해 필수인 셈.
프로바이더는 액세스가 필요한 위젯의 상위에 정의한다.
이 예제에서는 MyCart와 MyCatalog 둘 다 위의 어딘가가 runApp 뿐이라 거기에 둔 것을 볼 수 있다.
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => CartModel(),
child: const MyApp(),
),
);
}
ChangeNotifierProvider가 내부적으로 수행하는 주요 작업은 다음과 같다.
상태 초기화
: create 함수로 전달된 빌더를 호출해 상태를 생성한다.
상태 관리
: 생성된 상태를 구독하며, 상태가 변경되면 UI를 다시 그린다.
리소스 정리
: 상태가 더 이상 필요하지 않으면 dispose()를 호출해 리소스를 해제한다.
그리고 중요한 점!
위에 코드를 보면 ChangeNotifierProvider 는 create 매개변수를 통해 CartModel의 새로운 인스턴스를 생성하는 빌더를 정의하고 있다.
그렇기 때문에 꼭 필요한 경우가 아니면 CartModel을 재구축하지 않는다. 또한 인스턴스가 더 이상 필요하지 않을 때 CartModel에서 dispose()를 자동으로 호출해준다.
정리하자면 필요한 시점에 한 번만 호출되어 상태를 생성한다는 뜻이다.
프로바이더를 통해 전달되었으니 이제 위젯에서 ChangeNotifier 를 사용할 수 있다. 여기서는 Consumer 를 사용해 Provider로 전달된 상태를 UI 위젯에 연결해주었다. 이제 ChangeNotifier가 변경되면 Consumer 위젯의 builder 함수가 다시 호출되어 UI를 업데이트 해준다. 여기까지 하면 예제 끝! 상태가 바뀔 때마다 적절한 UI 를 반영하는 것을 볼 수 있다.
cart.totalPrice 변경
-> notifyListeners() 가 builder 함수 호출
-> UI 업데이트
return Consumer<CartModel>(
builder: (context, cart, child) {
return Text('Total price: ${cart.totalPrice}');
},
);
상태관리 라이브러리를 적용하지 않고 간단한 예제부터 살펴보니 상태관리가 왜 필요하고, 어떻게 이루어지는지 그 흐름에 대해 이해하기가 한결 쉬웠던 것 같다. 물론 실제로 적용하는 것은 또 다른 문제겠지만... 조급해하지 않고 차근차근 해보고 싶다!