- Provider는 state management를 위한 tool을 제공한다.
- Provider와 같은 state management를 이용해서 불필요한 rebuild를 방지한다.
state management를 이용하지 않는 경우 사용하지 않는 데이터를 전달만의 목적으로 코드를 작성해야 한다.
state management는 2가지에 관한 것이다.
Object를 Widget tree 상에서 쉽게 access할 수 있게 해주는 것이다.
setState 처럼 data와 UI를 동기화 시키는 것이다.
다른 것들과 다르게 Provider는 state를 다루는 수단만을 제공하고 방법론을 제안하지는 않는다.
Provider는 Widget에 데이터와 메서드를 쉽게 access할 수 있도록 도와주고, 데이터가 변경되었을 때 그 데이터를 필요로하는 Widget에게 rebuild될 수 있도록 한다. (business logic 과 UI 분리)
Provider 자체가 Widget이여서 Widget Tree 어디든지 위치하도록 할 수 있다. 하위 Widget에서만 Provider에 access할 수 있다.
Provider에는 create라는 property가 있다. create에서 Widget이 필요로하는 class의 instance를 생성한다.
create가 return하는 object는 Provider의 하위 widget들이 access할 수 있다.
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return Provider<Dog>(
create: (context) => Dog(
name: 'Sun',
breed: 'Bulldog',
age: 3,
),
child: MaterialApp(
title: 'Provider',
home: const MyHomePage(),
),
);
}
}
Provider에는 of<T>(context) 라는 static 메서드가 있다. of<T>(context) 는 context를 이용하여 상위 Widget들에서 원하는 타입의 instance를 찾아준다.
class MyHomePage extends StatelessWidget {
const MyHomePage({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Provider'),
),
body: Center(
child: Text(
'- name: ${Provider.of<Dog>(context).name}',
),
),
);
}
}
ChangeNotifier에 있는 notifyListeners() 함수를 이용하여 데이터의 변화가 있을때 알려주도록 할수 있다.
ChangeNotifierProvider = ChangeNotifier + Provider
- ChangeNotifier의 instance 생성
- ChangeNotifier를 쉽게 access할 수 있도록 하고, 필요하면 UI를 rebuild할 수 있도록 한다.
class Dog with ChangeNotifier {
final String name;
final String breed;
int age;
Dog({
required this.name,
required this.breed,
this.age = 1,
});
void grow() {
age++;
notifyListeners();
}
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return ChangeNotifierProvider<Dog>(
create: (context) => Dog(
name: 'Sun',
breed: 'Bulldog',
age: 3,
),
child: MaterialApp(
title: 'Provider 02',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(),
),
);
}
}
class Age extends StatelessWidget {
const Age({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return Column(
children: [
Text('- age: ${Provider.of<Dog>(context).age}'),
ElevatedButton(
onPressed: () => Provider.of<Dog>(context, listen: false).grow(),
child: Text('Grow'),
),
],
);
}
}
Provider를 더 간편하게 사용할 수 있는 method들이다.
- context.read<T>()
-> Provider.of<T>(context, listen: false)
- context.watch<T>()
-> Provider.of<T>(context)
- context.select<T, R>(R selector(T value))
e.g. context.select<Dog, String>(Dog dog) => dog.name)
property를 많이 가지고 있는 object의 특정 property의 변화만 listen하고 싶을 때 사용
class Age extends StatelessWidget {
const Age({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return Column(
children: [
Text('- age: ${context.select<Dog, Int>((Dog dog) => dog.age)}'),
ElevatedButton(
onPressed: () => context.read<Dog>().grow(),
child: Text('Grow'),
),
],
);
}
}