이 코드는 Flutter의 provider
패키지를 사용하여 상태 관리를 수행하는 방법을 보여줍니다. 특히, context
매개변수를 활용하여 상태 객체에 접근하는 여러 방법을 사용합니다. provider
패키지는 데이터를 효율적으로 관리하고 앱 전체에 걸쳐 쉽게 접근할 수 있도록 돕습니다. 코드 내 각 부분에 대한 설명과 주석을 추가해 보겠습니다.
기존에는 Provider.of<T>(context)
를 사용하여 context에서 원하는 데이터 타입의 객체를 검색했습니다. listen: false
매개변수를 추가하면 위젯 트리가 해당 데이터의 변화에 따라 재빌드되지 않도록 설정할 수 있었습니다.
final myModel = Provider.of<MyModel>(context, listen: false);
provider
4.1 이후부터는 BuildContext
의 extension methods를 사용하여 더 간단하게 데이터에 접근할 수 있습니다. 예를 들어, context.read<T>()
는 Provider.of<T>(context, listen: false)
와 동일한 역할을 하지만, 더 간결하게 표현됩니다. read<T>()
는 데이터를 읽을 때 사용되며, 데이터의 변경을 감지하여 위젯을 재빌드하지 않습니다.
read<T>()
사용 예시final myModel = context.read<MyModel>();
이 방식은 특히 이벤트 핸들러나 생명주기 메서드 내에서 상태를 변경할 때 유용하며, UI를 재빌드할 필요가 없을 때 사용됩니다.
watch<T>()
사용 예시데이터의 변화를 감지하고 해당 변화에 반응하여 위젯을 재빌드해야 할 때는 watch<T>()
를 사용할 수 있습니다. 이는 기존의 Provider.of<T>(context)
호출과 유사하지만, listen: true
를 기본값으로 가지며 더 간결한 문법을 제공합니다.
final myModel = context.watch<MyModel>();
provider
4.1 이상에서 도입된 BuildContext
의 extension methods는 코드를 더 간결하고 읽기 쉽게 만들어 줍니다. read<T>()
와 watch<T>()
메서드는 각각 상태를 읽거나 상태 변화에 따른 리액션을 구현할 때 사용되며, 기존 방식에 비해 더 효율적이고 직관적인 코드 작성을 가능하게 합니다. 이러한 변화는 Flutter 개발자들이 더 깔끔하고 세련된 방식으로 상태 관리를 할 수 있도록 돕습니다.에서는 Dog
클래스의 인스턴스를 앱 전체에서 접근 가능하게 만듭니다.
context.watch<T>()
context.watch<T>()
는 주어진 타입 T
의 객체가 변경될 때마다 위젯을 다시 빌드하도록 합니다. 이 방법은 데이터의 변화를 UI에 실시간으로 반영하고자 할 때 유용합니다.Provider.of<T>(context)
와 동일하다.
context.select<T, R>(R Function(T) selector)
context.select<T, R>()
메서드는 특정 타입 T
의 객체에서 R
타입의 값을 선택하여 사용합니다. 이 메서드는 객체의 일부분만 필요할 때 유용하며, 선택된 부분이 변경될 때만 위젯을 다시 빌드합니다. 이는 성능 최적화에 도움이 됩니다.context.read<T>()
context.read<T>()
메서드는 타입 T
의 객체를 읽어오되, 위젯의 재빌드를 트리거하지 않습니다. 주로 이벤트 핸들러 내에서 상태를 변경할 때 사용됩니다.Provider.of<T>(context, listen: false)
와 동일하다.
아래는 코드에 추가한 주석입니다:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'models/dog.dart'; // Dog 모델을 포함하는 외부 파일을 임포트합니다.
void main() {
runApp(const MyApp()); // 애플리케이션의 시작점입니다.
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return ChangeNotifierProvider<Dog>(
create: (context) => Dog(name: 'dog05', breed: 'breed05', age: 3), // Dog 객체를 생성하고 제공합니다.
child: MaterialApp(
title: 'Provider 05',
debugShowCheckedModeBanner: false, // 디버그 배너를 비활성화합니다.
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(), // 앱의 홈 화면을 설정합니다.
),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Widget build(BuildContext context) {
// watch를 사용하여 Dog 객체의 name 속성에 변경이 있을 때마다 위젯을 다시 빌드합니다.
return Scaffold(
appBar: AppBar(
title: Text('Provider 05'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
Text(
'- name: ${context.watch<Dog>().name}',
style: TextStyle(fontSize: 20.0),
),
SizedBox(height: 10.0),
BreedAndAge(),
],
),
),
);
}
}
class BreedAndAge extends StatelessWidget {
const BreedAndAge({
Key? key,
}) : super(key: key);
Widget build(BuildContext context) {
// select를 사용하여 Dog 객체의 breed 속성만을 대상으로 변경이 있을 때만 위젯을 다시 빌드합니다.
return Column(
children: [
Text(
'- breed: ${context.select<Dog, String>((Dog dog) => dog.breed)}',
style: TextStyle(fontSize: 20.0),
),
SizedBox(height: 10.0),
Age(),
],
);
}
}
class Age extends StatelessWidget {
const Age({
Key? key,
}) : super(key: key);
Widget build(BuildContext context) {
// select를 사용하여 Dog 객체의 age 속성만을 대상으로 변경이 있을 때만 위젯을 다시 빌드합니다.
return Column(
children: [
Text(
'- age: ${context.select<Dog, int>((Dog dog) => dog.age)}',
style: TextStyle(fontSize: 20.0),
),
SizedBox(height: 20.0),
ElevatedButton(
// read를 사용하여 Dog 객체에 접근하고, 이벤트 핸들러 내에서 grow 메서드를 호출합니다. 이 경우 위젯은 재빌드되지 않습니다.
onPressed: () => context.read<Dog>().grow(),
child: Text(
'Grow',
style: TextStyle(fontSize: 20.0),
),
),
],
);
}
}
이 코드는 provider
패키지의 watch
, select
, 그리고 read
메서드를 사용하여 Flutter 앱에서 상태 관리를 어떻게 수행하는지 보여줍니다. watch
와 select
는 위젯을 조건부로 다시 빌드하는 데 사용되며, read
는 상태 변경 시 위젯의 재빌드 없이 상태를 읽거나 변경하는 데 사용됩니다. 이러한 방법은 효율적인 상태 관리와 성능 최적화를 위해 중요합니다.
context.select<T, R>(R Function(T) selector)
context.select<T, R>(R Function(T) selector)
메서드는 Flutter 앱에서 특정 Provider가 제공하는 데이터의 일부분만 관심이 있을 때 사용합니다. 즉, 전체 데이터 모델 중에서 특정 값이나 상태의 변화만을 감지하고 싶을 때 유용합니다. 이 방식은 필요한 데이터 부분만 구독함으로써 리소스 사용을 최적화하고, 앱의 성능을 향상시킬 수 있습니다.
Dog
모델을 고려해 보겠습니다. 이 모델에는 name
, breed
, age
등의 필드가 있을 수 있습니다. 만약 우리가 Dog
의 age
필드 값의 변화만을 감지하고 싶다면, context.select()
메서드를 사용할 수 있습니다.
class Dog with ChangeNotifier {
String name;
String breed;
int age;
Dog({this.name = '', this.breed = '', this.age = 0});
void grow() {
age += 1;
notifyListeners(); // 상태 변경을 알림
}
// 여기에 다른 메서드들이 있을 수 있습니다.
}
우리가 Dog
객체의 age
필드만 관심이 있고, 이 필드가 변할 때만 UI를 업데이트하고 싶다고 가정해 봅시다. 아래 예시 코드는 context.select()
를 사용하여 이를 어떻게 할 수 있는지 보여줍니다.
class DogAgeWidget extends StatelessWidget {
Widget build(BuildContext context) {
// context.select를 사용하여 Dog 객체의 age 필드 값의 변화만 감지합니다.
final int dogAge = context.select<Dog, int>((Dog dog) => dog.age);
return Text('Dog age: $dogAge');
}
}
위의 DogAgeWidget
은 Dog
객체의 age
필드 값이 변경될 때만 다시 빌드됩니다. 만약 Dog
객체의 다른 필드(name
이나 breed
)가 변경되어도, DogAgeWidget
은 영향을 받지 않고 재빌드되지 않습니다. 이 방식은 특정 데이터 변화에만 반응하는 UI 부분을 효과적으로 만들 때 매우 유용합니다.
context.select<T, R>(R Function(T) selector)
는 특정 데이터의 변화만을 감지하고 싶을 때 사용하는 메서드입니다. 이는 앱의 성능을 최적화하는 데 도움을 줍니다. 전체 모델의 변화에 반응하여 모든 위젯을 다시 빌드하는 것이 아니라, 정말 필요한 부분의 변화에만 반응할 수 있게 해주기 때문입니다.
context.watch<Dog>().name
와 context.select<Dog, String>((Dog dog) => dog.name)
는 비슷해 보일 수 있지만, 그들이 작동하는 방식과 성능 최적화 측면에서 중요한 차이가 있습니다. 둘 다 Dog
객체의 name
속성의 변경을 감시하여 해당 변경이 있을 때 위젯을 다시 빌드하도록 합니다. 그러나, 내부적으로 Flutter 프레임워크에 의해 처리되는 방식이 다릅니다.
context.watch<T>()
context.watch<Dog>().name
을 사용할 때, watch<T>()
메서드는 Dog
타입의 전체 객체에 대한 변경을 감시합니다. 즉, Dog
객체 내의 어떤 속성이든 변경되면 관련 위젯이 다시 빌드됩니다. 여기서 .name
은 단지 Dog
객체에서 특정 필드를 접근하는 것이며, watch
의 범위를 한정하지 않습니다.
Dog
객체의 여러 속성 중 하나 또는 여러 개에 의존하고 있고, 이 중 어떤 것이든 변경될 때마다 위젯을 업데이트해야 하는 경우에 적합합니다.context.select<T, R>(R Function(T) selector)
반면, context.select<Dog, String>((Dog dog) => dog.name)
은 Dog
객체 내의 name
속성의 변경만을 감시합니다. select<T, R>(R Function(T) selector)
메서드는 특정 필드나 값의 변화에만 반응하여 위젯을 다시 빌드하도록 합니다. 이는 다른 속성의 변경이 있어도, name
속성이 변경되지 않는 한 위젯이 재빌드되지 않음을 의미합니다.
Dog
객체 내의 name
속성의 변경에만 반응하면 충분한 경우에 이상적입니다. 이 방식은 필요하지 않은 위젯의 재빌드를 줄여 성능을 향상시킵니다.context.select<T, R>(...)
는 성능 최적화 측면에서 context.watch<T>()
보다 더 세밀한 제어를 가능하게 합니다. select
를 사용하면 관심 있는 특정 데이터의 변경에만 반응하여 불필요한 위젯의 재빌드를 방지할 수 있습니다. 이는 특히 대규모 애플리케이션 또는 상태가 자주 변경되는 애플리케이션에서 성능을 크게 향상시킬 수 있습니다.
결론적으로, context.watch<T>()
와 context.select<T, R>(R Function(T) selector)
는 유사한 결과를 제공할 수 있지만, select
는 특정 필드의 변경에만 반응하는 더 세밀한 방식을 제공하여 성능 최적화에 유리합니다. 따라서 개발자는 각 위젯의 필요와 애플리케이션의 성능 요구 사항에 따라 적절한 메서드를 선택하여 사용해야 합니다.