InheritedWidget class
트리 아래로 정보를 효율적으로 전파하는 위젯의 기본 클래스입니다.
공식 문서 코드
InheritedWidget 은 위젯 트리에서 데이터를 자식 위젯들에게 효율적으로 전달하기 위해 존재하는 클래스이다. 이 위젯은 다른 위젯들이 데이터를 자동으로 감지하고 리빌드 할 수 있게 도와준다. 한번 알아보자.
일단은 먼저 이 위젯이 왜 필요한지에 대해서부터 알아야 할 것이다.
필요성
앱에서 데이터(사용자 정보, 테마 설정, 언어 설정 등)를 여러 위젯들이 공유해야 할 때가 생기기 마련이다. 이럴 때 이 데이터들을 일일이 생성자로 전달하는 건 너무나 비효율적일 것이다. 예제 영상에 설명하는 상황은 이런 상황이다.
MyApp(user: user)
-> HomePage(user: user)
-> Profile(user: user)
// 문법 아니고 그냥 예시
이렇게 계속 넘어가는걸 prop drilling 이라고 하는데 이게 InheritedWidget 이 해결해 주는 문제이다.
동작방법
- InheritedWidget 을 만들고 공유할 데이터를 이 위젯 안에 넣는다.
- 이 위젯을 트리에서 최상단 근처에 배치한다.
- 하위 위젯은 of(context) 같은 정적 메서드를 통해 그 데이터를 접근한다.
- 데이터가 바뀌면, 이걸 참조 중인 하위 위젯들이 자동으로 리빌드 된다.
기본 구조 예시를 봐보자. 일단 영상에 나오듯이 IngeritedWidget 을 extends 해서 만들어야 한다.
class MyData extends InheritedWidget {
final int counter;
const MyData({
super.key,
required this.counter,
required super.child,
});
static MyData of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<MyData>()!;
}
@override
bool updateShouldNotify(MyData oldWidget) {
return oldWidget.counter != counter;
}
}
의문이 들 수 있다. '왜 생성자에 child 가 들어가 있지?'라고. 이는 이 위젯 아래에 있는 모든 위젯들이 이 데이터를 의존할 수 있음을 나타내주는 것이다. 암튼 한번 살펴보자면
class MyData extends InheritedWidget {
InheritedWidget 을 상속함으로써 이 위젯은 다른 위젯들에게 데이터를 공유할 수 있는 기능을 갖게 된다. 이 클래스는 크게 세 가지 역할을 할 수 있는데 보면
final int counter;
데이터 보관과
static MyData of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<MyData>()!;
}
of(context) 로 쉽게 접근 가능하게 해 준다. 이는 하위 위젯에서 쉽게 MyData 에 접근할 수 있게 해 준다는 것인데 context.dependOnInheritedWidgetOfExactType<MyData>() 이것은 현재 context 에서 MyData 타입의 InheritedWidget 을 찾아서 리턴해준다는 소리이다.
이 메서드를 호출한 위젯은 MyData 가 바뀌면 자동으로 리빌드 되는 성질을 갖고 있다.
@override
bool updateShouldNotify(MyData oldWidget) {
return oldWidget.counter != counter;
}
또한 이 메서드는 판단할 기준을 알리는 역할을 하는데 true 를 리턴하면 이걸 참조하고 있던 모든 위젯들이 리빌드 된다.
여기선 counter 값이 달라졌을 때만 리빌드 하게 적어놓은 것이다.
이제 만들었으니 이 만든 데이터를 쓰는 하위 위젯에서는
class CounterDisplay extends StatelessWidget {
@override
Widget build(BuildContext context) {
final data = MyData.of(context);
return Text('Counter: ${data.counter}');
}
}
현재 MyData 를 의존하고 있기 때문에 counter 가 바뀌면 자동으로 리빌드 되게 된다.
여기서 of 메서드의 동작이 잘 이해가 가지 않을 수 있다. (내가 그러니까) 한번 of 메서드의 동작 원리와 역할을 짚고 넘어가겠다.
InheritedWidget 은 위젯 트리 어딘가에 존재하는데, 하위 위젯에서 그걸 결국엔 찾아서 사용해야 한다. 근데 context 는 현재 위치부터 부모 방향으로 트리를 탐색할 수 있다. 이때 of 는 이걸 이용해서 위젯에 있는 InheritedWidget 을 찾아주는 도우미 메서드인 것이다.
이 코드에서 봐보면
static MyData of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<MyData>()!;
}
내부 동작은 이렇게 이루어진다.
- context.dependOnInheritedWidgetOfExactType<MyData>() 호출
- 현재 BuildContext 기준으로 위쪽 방향으로 MyData 타입의 위젯을 찾음
- 찾으면 그 인스턴스를 리턴
- 못 찾으면 null 리턴 (여기선 null 처리를 위해 ! 붙임)
여기서 중요 포인트는 이걸 호출한 위젯이 InheritedWidget 에 의존하게 된다는 것이다.
즉 드 위젯이 바뀌면 자동으로 리빌드 되는 것이 여기에 있는 것이다. (이게 dependOn 의 의미)
이렇게 InheritedWidget 에 대해서 알아보았다. 나도 아직 완벽하게 이해를 하지 못해서 공부하는 입장이다만 이 위젯을 알면 결국 Provider 의 동작도 쉽게 이해할 수 있게 되기 때문에 필연적으로 알아야 하는 위젯이 아닌가 싶다. 도움이 되었길 바라며 마치겠다.