
1. StateNotifierProvider
- Flutter의 provider 패키지에 있는 클래스 중 하나
- Riverpod 패키지에서 제공하는 클래스
- 상태 관리를 위한 Provider 패키지의 대안 중 하나
- Flutter 위젯 트리의 일부에 값을 제공
- 해당 값이 변경될 때 의존하는 위젯을 자동으로 업데이트하는 데 사용
- 일반적으로 StateNotifier와 함께 사용 / 상태 컨테이너와 알림 기능을 결합한 클래스
- 데이터가 변경되고 다시 그림이 그려짐
- watch(이벤트 감지)를 read로 바꾸면 Provider와 같이 사용할 수 있음
2. 실습하기
flutter_riverpod: 2.5.1

import 'package:flutter_riverpod/flutter_riverpod.dart'; //1. 창고 데이터 : 상태값 String data = "사과"; //2. 창고 : 상태 변경(메서드), VIEW에 필요한 데이터 커스터마이징 class FruitVM extends StateNotifier<String> {// 상태가 변경되면 화면에 알려줌 FruitVM(super.state); void changeData(){ // 상태를 변경할 메서드 state = "딸기"; } //3. 창고 관리자 : 창고에 IO(접근)하게 해줌 //창고 이름, 창고 상태 final fruitProvider = StateNotifierProvider<FruitVM, String>((ref) { return FruitVM("사과"); // watch or read 할때 호출됨 });

import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; void main() { runApp( ProviderScope( child: const MyApp(), ), ); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( home: FruitPage(), ); } } class FruitPage extends ConsumerWidget { const FruitPage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { return Scaffold( body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( "data : 사과", style: TextStyle(fontSize: 30), ), ElevatedButton( onPressed: () {}, child: Text("딸기로 상태 변경"), ), ], ), ), ); } }

- 창고에 데이터 넣어야 상태가 됨!!
import 'package:flutter_riverpod/flutter_riverpod.dart'; //1. 창고 데이터 : 상태값 String date = "사과"; //2. 창고 : 상태 변경(메서드), VIEW에 필요한 데이터 커스터마이징 class FruitVM extends StateNotifier<String> { // 상태가 변경되면 화면에 알려줌 FruitVM(super.state); // 상태값을 "사과"로 초기화 void changeData() { // 상태를 변경할 메서드 state = "딸기"; } } //3. 창고 관리자 : 창고에 IO(접근)하게 해줌 //창고 이름, 창고 상태 final fruitProvider = StateNotifierProvider<FruitVM, String>((ref) { return FruitVM(date); });
import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; void main() { runApp( ProviderScope( child: const MyApp(), ), ); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( home: FruitPage(), ); } } class FruitPage extends ConsumerWidget { // 컨슈머 const FruitPage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { return Scaffold( body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( // 상태값 -> 그림이 그려지는 곳 "data : 사과", style: TextStyle(fontSize: 30), ), ElevatedButton( // 상태값을 바꾸는 곳 onPressed: () {}, child: Text("딸기로 상태 변경"), ), ], ), ), ); } }
3. Provier와 다른 점!
- 상태를 바로 리턴해줌
- 창고를 리턴할 때 notifier이 필요함!

- 값만 변경됨
import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod_02/fruit_store.dart'; void main() { runApp( ProviderScope( child: const MyApp(), ), ); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( home: FruitPage(), ); } } class FruitPage extends ConsumerWidget { // 컨슈머 const FruitPage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { // 상태를 바로 리턴하는 방법 String data = ref.read(fruitProvider); // 창고를 리턴하는 방법 FruitVM vm = ref.read(fruitProvider.notifier); return Scaffold( body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( // 상태값 -> 그림이 그려지는 곳 "data : ${data}", style: TextStyle(fontSize: 30), ), ElevatedButton( // 상태값을 바꾸는 곳 onPressed: () { vm.changeData(); print(vm.state); }, child: Text("딸기로 상태 변경"), ), ], ), ), ); } }


- 다시 그려짐
import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod_02/fruit_store.dart'; void main() { runApp( ProviderScope( child: const MyApp(), ), ); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( home: FruitPage(), ); } } class FruitPage extends ConsumerWidget { // 컨슈머 const FruitPage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { // 상태를 바로 리턴하는 방법 String data = ref.read(fruitProvider); // 창고를 리턴하는 방법 FruitVM vm = ref.read(fruitProvider.notifier); return Scaffold( body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( // 상태값 -> 그림이 그려지는 곳 "data : ${data}", style: TextStyle(fontSize: 30), ), ElevatedButton( // 상태값을 바꾸는 곳 onPressed: () { vm.changeData(); print(vm.state); }, child: Text("딸기상태로 변경"), ), ], ), ), ); } }

4. 실습하기2
import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod_02/fruit_store.dart'; void main() { runApp(ProviderScope(child: const MyApp())); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( home: FruitPage(), ); } } class FruitPage extends StatelessWidget { const FruitPage({super.key}); @override Widget build(BuildContext context) { return Scaffold( body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ MyConsumer1(), // 구독 MyConsumer2(), // 구독 MyConsumer3(), // 구독안함 MyPublisher(), // 출판 ], ), ), ); } } class MyPublisher extends ConsumerWidget { const MyPublisher({ super.key, }); @override Widget build(BuildContext context, WidgetRef ref) { print("MyPublisher 빌드됨"); // 1. 출판 FruitVM vm = ref.read(fruitProvider.notifier); return ElevatedButton( onPressed: () { vm.changeData(); }, child: Text("딸기상태로 변경")); } } class MyConsumer1 extends ConsumerWidget { const MyConsumer1({ super.key, }); @override Widget build(BuildContext context, WidgetRef ref) { print("MyConsumer1 빌드됨"); String data = ref.watch(fruitProvider); return Text("data : ${data}", style: TextStyle(fontSize: 30)); } } class MyConsumer2 extends ConsumerWidget { const MyConsumer2({ super.key, }); @override Widget build(BuildContext context, WidgetRef ref) { print("MyConsumer2 빌드됨"); String data = ref.watch(fruitProvider); return Text("data : ${data}", style: TextStyle(fontSize: 30)); } } class MyConsumer3 extends StatelessWidget { const MyConsumer3({ super.key, }); @override Widget build(BuildContext context) { print("MyConsumer3 빌드됨"); return Text("data : 사과", style: TextStyle(fontSize: 30)); } }


5. 실습하기3
import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod_02/fruit_store.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( home: FruitPage(), ); } } class FruitPage extends StatefulWidget { const FruitPage({super.key}); @override State<FruitPage> createState() => _FruitPageState(); } class _FruitPageState extends State<FruitPage> { String data = "사과"; void changeData() { data = "딸기"; setState(() {}); } @override Widget build(BuildContext context) { return Scaffold( body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ MyConsumer1(data), // 구독 MyConsumer2(data), // 구독 MyConsumer3("사과"), // 구독안함 MyPublisher(changeData), // 출판 ], ), ), ); } } class MyPublisher extends StatelessWidget { final changeData; MyPublisher(this.changeData); @override Widget build(BuildContext context) { print("MyPublisher 빌드됨"); return ElevatedButton( onPressed: () { changeData(); }, child: Text("딸기상태로 변경")); } } class MyConsumer1 extends StatelessWidget { final data; MyConsumer1(this.data); @override Widget build(BuildContext context) { print("MyConsumer1 빌드됨"); return Text("data : ${data}", style: TextStyle(fontSize: 30)); } } class MyConsumer2 extends StatelessWidget { final data; MyConsumer2(this.data); @override Widget build(BuildContext context) { print("MyConsumer2 빌드됨"); return Text("data : ${data}", style: TextStyle(fontSize: 30)); } } class MyConsumer3 extends StatelessWidget { final data; MyConsumer3(this.data); @override Widget build(BuildContext context) { print("MyConsumer3 빌드됨"); return Text("data : ${data}", style: TextStyle(fontSize: 30)); } }


Share article