import 'package:flutter/material.dart';
class CounterInheritedWidget extends InheritedWidget {
const CounterInheritedWidget({
super.key,
required this.counter,
required this.incrementCounter,
required super.child,
});
final int counter;
final VoidCallback incrementCounter;
static CounterInheritedWidget of(BuildContext context) {
final result =
context.dependOnInheritedWidgetOfExactType<CounterInheritedWidget>();
assert(result != null, 'CounterInheritedWidget를 찾을 수 없습니다');
return result!;
}
bool updateShouldNotify(CounterInheritedWidget oldWidget) {
return oldWidget.counter != counter;
}
}
InheritedWidget의 인스턴스를 찾아 반환하는 정적(static) 메소드
static CounterInheritedWidget of(BuildContext context) {
final result =
context.dependOnInheritedWidgetOfExactType<CounterInheritedWidget>();
assert(result != null, 'CounterInheritedWidget를 찾을 수 없습니다');
return result!;
}
값이 다른 경우에만 true를 반환하여 불필요한 리빌드를 방지하는 메소드
updateShouldNotify(CounterInheritedWidget oldWidget) {
return oldWidget.counter != counter;
}
bool
이전 위젯의 count 변수와 현재 counter 변수를 비교해서 변경이 될때만 리빌드하게 하는 메소드
상태를 내려주고 싶은 상단 위젯에 CounterInheritedWiget으로 감싸준다.
class _InheritedCounterPageState extends State<InheritedCounterPage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
Widget build(BuildContext context) {
return CounterInheritedWidget(
counter: _counter,
incrementCounter: _incrementCounter,
child: Scaffold(
appBar: AppBar(
title: const Text('Inherited Widget 카운터'),
),
body: const Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Inherited Widget으로 구현한 카운터:'),
InheritedCounterDisplay(),
InheritedCounterButton(),
],
),
),
),
);
}
}
class InheritedCounterDisplay extends StatelessWidget {
const InheritedCounterDisplay({super.key});
Widget build(BuildContext context) {
final counter = CounterInheritedWidget.of(context).counter;
return Text(
'$counter',
style: Theme.of(context).textTheme.headlineMedium,
);
}
}
즉 Inherited Widget의 단점을 보완하고 전역적으로 상태를 쓰고 읽을 수 있는 기능을 제공하는 것
결국 inheritedWidget의 특성을 이용한다!
// 1. 앱 최상위에 ProviderScope 배치
void main() {
runApp(
ProviderScope( // 여기서 ProviderContainer 생성
child: MyApp(),
),
);
}
// 2. ProviderScope의 내부 구현 (단순화된 버전)
class ProviderScope extends StatefulWidget {
State<ProviderScope> createState() => _ProviderScopeState();
}
class _ProviderScopeState extends State<ProviderScope> {
// ProviderContainer 인스턴스 생성
late final container = ProviderContainer();
Widget build(BuildContext context) {
// UncontrolledProviderScope는 InheritedWidget을 상속
return UncontrolledProviderScope(
container: container,
child: widget.child,
);
}
}
// 3. ConsumerWidget에서 접근
class MyWidget extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
// ref는 내부적으로 BuildContext를 사용하여
// 가장 가까운 UncontrolledProviderScope를 찾아
// 그 안의 ProviderContainer에 접근
final value = ref.watch(someProvider);
return Text('$value');
}
}
// UncontrolledProviderScope는 InheritedWidget을 상속받음
class UncontrolledProviderScope extends InheritedWidget {
const UncontrolledProviderScope({
required this.container,
required Widget child,
Key? key,
}) : super(key: key, child: child);
// ProviderContainer 인스턴스를 보관
final ProviderContainer container;
// InheritedWidget의 필수 구현 메서드
bool updateShouldNotify(UncontrolledProviderScope oldWidget) {
return container != oldWidget.container;
}
// of 메서드를 통해 ProviderContainer에 접근
static ProviderContainer containerOf(BuildContext context) {
final scope = context.dependOnInheritedWidgetOfExactType<UncontrolledProviderScope>();
return scope!.container;
}
}
즉 결국은 inheritedWidget의 기능을 추상화해서 만든 라이브러리라고 볼 수 있다.
https://github.com/ds-k/inherited_widget_sample