반응형

setState, ValueNotifier, Provider 기본

 

1. 왜 상태관리가 중요한가

Flutter는 상태(State) 변화에 따라 UI를 다시 그리는 Declarative UI 프레임워크입니다.
사용자의 입력, API 응답, 타이머 등으로 데이터가 변하면 그 변화가 화면에 즉시 반영되어야 합니다.

하지만 규모가 커질수록 “누가”, “언제”, “어떤 데이터”를 갱신해야 하는지가 복잡해집니다.
그래서 ‘상태관리(State Management)’는 Flutter 개발의 핵심이 됩니다.

 

2. 상태의 분류

구분예시관리 방법

Ephemeral (일시적 상태) TextField 입력, 현재 탭 setState, ValueNotifier
Shared (공유 상태) 로그인 정보, 카트 목록 Provider, Riverpod, BLoC
Persisted (지속 상태) DB/캐시/로컬 저장 Hive, Isar, shared_preferences

 

3. setState()

Flutter의 기본 상태관리 방식입니다.

UI 위젯 내에서 setState() 호출 시 해당 위젯이 다시 빌드됩니다.

class CounterPage extends StatefulWidget {
  const CounterPage({super.key});
  @override
  State<CounterPage> createState() => _CounterPageState();
}

class _CounterPageState extends State<CounterPage> {
  int count = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Counter')),
      body: Center(child: Text('$count')),
      floatingActionButton: FloatingActionButton(
        onPressed: () => setState(() => count++),
        child: const Icon(Icons.add),
      ),
    );
  }
}

장점
- 간단하고 빠름
- 러닝커브 없음

단점
- 여러 위젯 간 상태 공유 불가
- 코드 복잡도 증가

 

4. ValueNotifier / ValueListenableBuilder

ValueNotifier는 가벼운 옵저버 패턴입니다.
간단한 UI 상태나 폼 값에 적합합니다.

final counter = ValueNotifier<int>(0);

class CounterVNPage extends StatelessWidget {
  const CounterVNPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: ValueListenableBuilder<int>(
          valueListenable: counter,
          builder: (_, value, __) => Text('$value'),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => counter.value++,
        child: const Icon(Icons.add),
      ),
    );
  }
}

💡 ValueNotifier는 폼 검증, 스크롤 위치, 단순 필터링 등에 이상적입니다.

 

5. Provider / ChangeNotifier

Provider는 Flutter에서 가장 널리 쓰이는 상태관리 도구입니다.

ChangeNotifier 모델을 전역으로 주입해 손쉽게 상태를 공유할 수 있습니다.

예시: 장바구니 모델

class CartModel extends ChangeNotifier {
  final List<String> _items = [];
  List<String> get items => List.unmodifiable(_items);
  int get count => _items.length;

  void add(String item) {
    _items.add(item);
    notifyListeners();
  }

  void remove(String item) {
    _items.remove(item);
    notifyListeners();
  }
}

Provider 등록 및 사용

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (_) => CartModel(),
      child: const MyApp(),
    ),
  );
}

class CartButton extends StatelessWidget {
  const CartButton({super.key});

  @override
  Widget build(BuildContext context) {
    final count = context.watch<CartModel>().count;
    return Stack(children: [
      IconButton(onPressed: () {}, icon: const Icon(Icons.shopping_cart)),
      if (count > 0)
        Positioned(right: 0, child: CircleAvatar(radius: 10, child: Text('$count')))
    ]);
  }
}

장점
- 코드가 간단하고 직관적
- Flutter 공식 문서에서도 적극 추천

단점
- ChangeNotifier의 가변성 문제
- 대형 프로젝트에선 구조적 한계

반응형
Posted by 까칠코더
,