Provider를 사용하려면 먼저 전체 앱을 ProviderScope로 감싸줘야 한다.
void main() {
runApp(
ProviderScope( // ProviderScope로 감싸줘야 한다
child: MyApp(),
),
);
}
provider 에서 관리되는 데이터를 읽기 위해 필요한 것
- ConsumerWidget
- WidgetRef
- ref.watch : UI관련 코드에만 사용할것. provider 값이 변경되면 build 함수를 다시 실행시켜줌.
- ref.read : 실행순간 단 1번만 provider 값을 가져온다.(단발성)
// 사용할 Provider
final valueProvider = Provider<int>((ref) {
return 0;
});
// Stateless --> ComsumerWidget
class MyHomePage extends ConsumerWidget {
@override
// BuildContext 파라미터 오른쪽에 WidgetRef 파라미터 추가!!
Widget build(BuildContext context, WidgetRef ref) {
// ref.watch = 데이터를 읽어 들이기 위해 watch 한다.
final value = ref.watch(valueProvider);
return Scaffold(
body: Center(
child: Text(
'Value: $value',
),
),
);
}
}
Provider의 종류 (6가지)
Provider의 기본은 해당 Provider 들은 글로벌(전역)하게 사용된다. = 어디에서든 접근 할수 있다.
Provider
provider.dart
// 구현 형태 예시
final valueProvider = Provider<int>((ref) {
return 0;
});
Provider는 읽기만 가능하며 값을 변경할 수 없다. (값 변경 불가!!)
보통 Provider 안에 Provider 를 넣을 경우에, 많이 사용한다.
StateProvider
state_provider.dart
// <int> 적어줘도 되고, 안해도 되고,,, 자동인식
final numberProvider = StateProvider<int>((ref) => 0);
state_provider_screen.dart
class StateProviderScreen extends ConsumerWidget {
StateProviderScreen({Key? key}) : super(key: key);
final commonStyle = ElevatedButton.styleFrom(
foregroundColor: Colors.white,
backgroundColor: Colors.redAccent,
);
final todoStyle = ElevatedButton.styleFrom(
foregroundColor: Colors.black,
backgroundColor: Colors.greenAccent,
);
@override
Widget build(BuildContext context, WidgetRef ref) {
final provider = ref.watch(numberProvider); /// numberProvider 변수값
return DefaultLayout(
title: 'Basic Provider',
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
provider.toString(),
textAlign: TextAlign.center,
),
ElevatedButton(
style: ElevatedButton.styleFrom(
foregroundColor: Colors.white,
backgroundColor: Colors.redAccent,
),
onPressed: () {
/// 값 변경 방법 : .state
/// 값 변경할려면, Provider 뒤에 notifier 붙힐것
ref.read(numberProvider.notifier).state =
ref.read(numberProvider.notifier).state + 1;
},
child: const Text('UP',),
),
ElevatedButton(
style: ElevatedButton.styleFrom(
foregroundColor: Colors.black,
backgroundColor: Colors.greenAccent,
),
onPressed: () {
/// 값 변경 방법 : .update
ref.read(numberProvider.notifier)
.update(
(state) => state + 1
);
},
child: const Text('Update'),
),
],
),
);
}
}
StateProvider는 상태를 변경할 수 있는 Provider이다.
내부 상태는 state로 접근이 가능한데, 사용하고자 하는 위젯에서 state 값을 직접 변경할 수 있다.
update 방식으로 데이터를 변경할수 있다.
- UI에서 직접 데이터를 변경하고 싶을때 사용
- 단순한 형태의 데이터만 관리
- Map, List 등 복잡한 형태의 데이터는 다루지 않는다.
- 복잡한 로직은 사용하지 않는다. => 간단한 로직만 사용
StateNotifierProvider
제일 많이 사용되는 Provider
stateProvider에서 약간 사용법이 변경된다.
- StateNotifierProvider 생성 시, <Notifier 클래스, 관리하는 상태 타입> 이 추가 된다.
- 그리고 StateNotifier 클래스를 상속받는 클래스를 하나 만들어야 한다.
- StateNotifier 클래스를 상속받는 클래스에서 생성자를 만들어야 한다.
state_notifier_provider.dart
// < Notifier 클래스, 관리하는 상태 타입>
final shoppingListNotifierProvider =
StateNotifierProvider<ShoppingListNotifier, List<ShoppingItemModel>>(
(ref) => ShoppingListNotifier(),
);
/// StateNotifier 를 절대적으로 상속해야 한다.
class ShoppingListNotifier extends StateNotifier<List<ShoppingItemModel>> {
/// 생성자 적용
ShoppingListNotifier()
: super(
[
const ShoppingItemModel(
name: '김치',
quantity: 3,
hasBought: false,
isSpicy: true,
),
const ShoppingItemModel(
name: '라면',
quantity: 5,
hasBought: false,
isSpicy: true,
),
const ShoppingItemModel(
name: '불닭소스',
quantity: 1,
hasBought: false,
isSpicy: true,
),
const ShoppingItemModel(
name: '삼겹살',
quantity: 10,
hasBought: false,
isSpicy: false,
),
const ShoppingItemModel(
name: '수박',
quantity: 2,
hasBought: false,
isSpicy: false,
),
const ShoppingItemModel(
name: '카스테라',
quantity: 5,
hasBought: false,
isSpicy: false,
),
]
);
void toggleTodo({required String name}) {
// state 는 상속받은 StateNotifier 의 state를 의미한다.
state = state.map((e) =>
e.name == name
? e.copyWith(hasBought : !e.hasBought)
: e,
).toList();
}
}
state_notifier_provider_screen.dart
class StateNotifierProviderScreen extends ConsumerWidget {
const StateNotifierProviderScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
List<ShoppingItemModel> items = ref.watch(shoppingListNotifierProvider);
return DefaultLayout(
title: 'StateNotifierProvider',
body: ListView(
children: items
.map(
(e) => CheckboxListTile(
title: Text('${e.name}(${e.quantity})'),
value: e.hasBought,
onChanged: (value) {
/// Provider에서 데이터 읽어 오고 싶을때는 read 사용(단발성 액션엔 read)
/// ref.read(shoppingListNotifierProvider.notifier)
/// 는 ShoppingListNotifier 의 인스턴스를 호출하는 것!
/// 해당 인스턴스를 호출하는 것이니, 메소드를 불러올수 있는것이다.
ref.read(shoppingListNotifierProvider.notifier).toggleTodo(
name: e.name,
);
},
),
)
.toList(),
),
);
}
}
FutureProvider
- Future 타입만 반환 가능
- API 요청의 결과를 반환할때 보통 사용
- 1회성 실행 => 가장 큰 단점.. 반복실행이 필요한 기능에서는 StateNotifierProvider 사용
future_provider.dart
// 구현 형태 예시
final multiplesFutureProvider = FutureProvider<List<int>>((ref) async {
await Future.delayed(
const Duration(
seconds: 2,
)
);
/// 일부러 에러 발생 시키는 코드
// throw Exception('에러');
return [1,2,3,4,5];
});
future_provider_screen.dart
class FutureProviderScreen extends ConsumerWidget {
const FutureProviderScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
/// FutureProvider 는 반환시 AsyncValue 로 반환한다.
final AsyncValue<List<int>> numbers = ref.watch(multiplesFutureProvider);
return DefaultLayout(
title: 'FutureProvider',
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
/// Freezeed
/// 종류1 : .map
/// 종류2 : .when
numbers.when(
// data: 로딩후 데이터가 있을때, 반환받은 값
data: (data) => Text(
data.toString(),
textAlign: TextAlign.center,
),
error: (err, stack) => Text('Error: $err'),
loading: () => const Center(
child: CircularProgressIndicator(),
),
),
],
),
);
}
}
StreamProvider
- Stream 타입만 반환 가능
- socket 을 이용한 데이터 통신에 사용. 보통 실시간 채팅기능 작업 시 사용.
stream_provider.dart
final multiplesStreamProvider = StreamProvider<List<int>>((ref) async*{
for(int i = 0; i < 10; i++){
await Future.delayed(Duration(seconds: 1));
yield List.generate(3, (index) => index * i);
}
});
stream_provider_screen.dart
class StreamProviderScreen extends ConsumerWidget {
const StreamProviderScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
/// StreamProvider 는 반환시 AsyncValue 로 반환한다.
final AsyncValue<List<int>> stream = ref.watch(multiplesStreamProvider);
return DefaultLayout(
title: 'StreamProvider',
body: Center(
child: stream.when(
data: (data) => Text(data.toString()),
error: (error, stack) => Text('Error: $error'),
loading: () => const CircularProgressIndicator(),
),
),
);
}
}
'Flutter' 카테고리의 다른 글
빈 화면 터치. 키보드(자판) 숨기기 (0) | 2024.02.24 |
---|---|
[GoRouter] Go vs Push (0) | 2024.02.14 |