- StateProvider와 마친가지로 UI에서 직접적으로 데이터를 변경할 수 있도록 하고 싶을 때 사용한다.
- 복잡한 형태의 데이터 관리가 가능하다.(클래스의 메소드를 이용한 상태관리)
- StateNotifier를 상속한 클래스를 반환한다.
- stateNotifierProvider를 사용하기 전에 몇가지 값들이 들어있는 모델을 생성했다.
class ShoppingItemModel {
  
  final String name;
  
  final int quantity;
  
  final bool hasBought;
  
  final bool isSpacy;
  ShoppingItemModel({
    required this.name,
    required this.quantity,
    required this.hasBought,
    required this.isSpicy,
  });
}
생성하기
- 우선 코드를 작성할 state_notifier_provider.dart파일을 생성한다
- stateNotifierProvider는 class로 시작한다.
- 절대적으로 StateNotifier를 상속받고 무조건 상태 관리할 타입이 어떤 타입인지 지정해 줘야 한다.
- 생성자를 생성하고 super()값에 처음에 어떤 값을 초기화 해줄지 넣어줘야 한다.
class ShoppingListNotifier extends StateNotifier<List<ShoppingItemModel>> {
  ShoppingListNotifier()
      : super([]);
      }
- 앞에서 생성한 model를 참고해서 초기값을 넣어줬다.
- stateProvider와 달리- class안에서 복잡한 상태를 관리하기 때문에 메소드를 사용해서 값들을 변경시키는 작업을 해야한다. 예제에서는 구매한 것을- true로 변경하는 메소드를 작성해서 사용한다.
- ShoppingListNotifier를- Provider로 사용할 때- StateNotifierProvider를 사용한다. 제너릭에는 2가지 값이 들어간다. 첫번째 값은 어떤- Notifier를 사용하는 지에 대한 값이 들어가고 두번째 값은- ShoppingListNotifier가 관리하는- state의 타입이 들어가게 된다. 그리고 반환 값에는- ShoppingListNotifier()를 넣어주면 된다.
final shoppingListProvider =
    StateNotifierProvider<ShoppingListNotifier, List<ShoppingItemModel>>(
  (ref) => ShoppingListNotifier(),
);
class ShoppingListNotifier extends StateNotifier<List<ShoppingItemModel>> {
  ShoppingListNotifier()
      : super(
          [
            ShoppingItemModel(
              name: '김치',
              quantity: 3,
              hasBought: false,
              isSpicy: true,
            ),
            ShoppingItemModel(
              name: '라면',
              quantity: 5,
              hasBought: false,
              isSpicy: true,
            ),
            ShoppingItemModel(
              name: '삼겹살',
              quantity: 10,
              hasBought: false,
              isSpicy: false,
            ),
            ShoppingItemModel(
              name: '수박',
              quantity: 2,
              hasBought: false,
              isSpicy: false,
            ),
            ShoppingItemModel(
              name: '카스테라',
              quantity: 5,
              hasBought: false,
              isSpicy: false,
            ),
          ],
        );
  void toggleHasBought({
    required String name,
  }) {
    state = state 
    
        .map((e) => e.name == name
            ? ShoppingItemModel(
                name: e.name,
                quantity: e.quantity,
                hasBought: !e.hasBought,
                isSpicy: e.isSpacy,
              )
            : e)
        .toList();
  }
}
사용하기
- stateProvider를 사용했던 것과 같이 사용하면 된다.
class StateNotifierProviderScreen extends ConsumerWidget {
  const StateNotifierProviderScreen({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final List<ShoppingItemModel> state = ref.watch(shoppingListProvider);
    
    return DefaultLayout(
      title: 'StateNotifierProvider',
      body: ListView(
        children: state
            .map(
              (e) => CheckboxListTile(
                title: Text(e.name),
                value: e.hasBought,
                onChanged: (value) {
                  ref.read(shoppingListProvider.notifier).toggleHasBought(
                        name: e.name,
                      );
                },
              ),
            )
            .toList(),
      ),
    );
  }
}
- 무언가 액션을 취했을 때 단발적으로 실행하는 것은 ref.read를 사용한다.
- shoppingListProvider에서 값을 업데이트하기 위해서는- .notifier를 해줘야한다.
