- 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
를 해줘야한다.
