InheritedModel<T> class
전체 모델의 한 부분이나 "측면"에만 종속되는 모델의 기본 클래스로 사용되도록 의도된 InheritedWidget 입니다.
상속된 위젯의 종속 항목은 상속된 위젯이 InheritedWidget.updateShouldNotify 에 따라 변경될 때 무조건 다시 빌드됩니다. 이 위젯은 종속 항목이 무조건 다시 빌드되지 않는다는 점을 제외하면 유사합니다.
InheritedModel 에 의존하는 위젯은 자신이 의존하는 모델의 "측면"을 나타내는 값으로 종속성을 한정합니다. 모델이 재빌드되면 종속된 위젯도 재빌드되지만, 제공된 측면에 해당하는 모델에 변경 사항이 있는 경우에만 재빌드됩니다.
유형 매개변수 T는 모델 측면 객체의 유형입니다.
위젯은 정적 메서드인 InheritedModel.inheritFrom을 사용하여 InheritedModel 에 종속성을 생성합니다 . 이 메서드의 매개변수는 모델이 변경될 때 다시 빌드될 하위 트리를 정의합니다. 일반적으로 이 메서드는 모델별 정적 메서드 또는 메서드에서 호출되는데, 이는 많은 Flutter 프레임워크 클래스에서 참조하는 규칙입니다.
공식 문서 코드
InheritedModel 위젯은 여러 위젯이 동일한 상위 상태를 구독하되, 그 상태의 일부 "슬라이스(slice)"에만 관심이 있는 경우 성능 최적화를 위해 사용되는 위젯이다.
사실 이번 소개할 위젯은 내가 처음 접해보는 것이라 설명을 아주 기초 개념부터 할 생각이다. 이 글을 읽을 사람(나)를 위해서.
당연한 소리이지만 Flutter 에서는 모든 것이 위젯으로 이루어져 있다. UI 요소뿐 아니라 상태를 전달하거나 행동을 공유하는 것도 위젯으로 한다.
그렇다면 여기서 상태 전달이란 무엇인가.
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: CounterScreen(),
);
}
}
근데 만약 CounterScreen 안에서 만든 상태(예: int count)를 여러 다른 위젯에 넘겨주고 싶다면? 데이터를 전달하기 위한 구조가 필요할 것이다.
만약 위젯 A에서 만든 데이터를 위젯 Z까지 전달하려면 중간 위젯 B, C, D... 전부 다 데이터를 전달해줘야 하는데 이건 귀찮고 비효율적일 것이다.
그래서 위쪽에서 데이터를 선언하고, 아래쪽에서 바로 접근할 수 있는 InheritedWidget 을 제공하는 것이다.
그렇다면 InheritedWidget 이란 무엇인가?
위젯 트리 아래쪽에 데이터를 전파하려면 일반적으로 InheritedWidget 을 사용하게 된다. 이걸 통해 트리 아래 있는 많은 위젯들이 공유 상태를 쉽게 헏을 수 있는 것이다.
예를 들어 앱 전체에서 접근 할 수 있는 테마나 사용자 정보 같은 걸 저장 할 수 있는 것이다.
class MyData extends InheritedWidget {
final int count;
const MyData({required this.count, required Widget child}) : super(child: child);
static MyData? of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<MyData>();
}
@override
bool updateShouldNotify(MyData oldWidget) => count != oldWidget.count;
}
이건 하나의 값(count)을 기준으로 모든 자식이 리빌드 되는 방식이다.
그렇다면 우리가 알고싶은 InheritedModel 은 뭐가 다른가?
바로 데이터를 분리된 부분(aspect) 으로 나눌 수 있다는 점이다. 위젯이 필요한 부분만 구독 하고 해당 부분이 바뀔 때만 리빌드 되는 방식.
즉 데이터의 일부분만 바뀌었는데 전체 자식 위젯이 다시 그려지는 걸 막아주는 것이다.
그렇다 이걸로도 감이 오겠지만 이건 대규모 앱에서 불필요한 리빌드를 줄이는데 사용하는 위젯인거다.
사용방법을 알아보자.
일단 얘는 우리가 흔히 생각하는 그런 종류의 위젯(?) 은 아니고 생성자 받는 친구다.
const InheritedModel({super.key, required super.child});
필수 메서드 오버라이드
createElement
@override
InheritedModelElement<T> createElement() => InheritedModelElement<T>(this);
InheritedElement의 서브클래스를 사용하여, aspect 추적 기능을 확장한다.
updateShouldNotifyDependent
@protected
bool updateShouldNotifyDependent(
covariant InheritedModel<T> oldWidget,
Set<T> dependencies);
실제 핵심 로직이다. 주어진 aspect 집합 중에 이전 모델과 현재 모델 사이에서 변경된 항목이 있는지 판단한다.
isSupportedAspect
@protected
bool isSupportedAspect(Object aspect) => true;
디폴트로 true 값 반환하고 모든 aspect 를 지원한다. 특정 모델이 일부 aspect 만 지원하도록 제한하고 싶을 때 오버라이드 한다.
정적 메서드
_findModels
static void _findModels<T extends InheritedModel<Object>>(...)
context 위로 올라가면서 T 타입의 모델을 모두 찾고 그중에서 isSupportedAspect(aspect)가 true인 가장 가까운 모델을 최종 결과로 사용한다.
inheritFrom
static T? inheritFrom<T extends InheritedModel<Object>>(BuildContext context, {Object? aspect});
aspect가 없는 경우: 일반적인 InheritedWidget 방식으로 의존성 생성
aspect가 있는 경우:
- _findModels로 관련 모델 탐색
- 각 모델에 대해 dependOnInheritedElement 호출 (의존성 등록)
- 가장 가까운 aspect 지원 모델을 반환
이렇게 한번 InheritedModel 에 대해서 알아보았다. 이번건 나도 계속 공부하면서 이 글을 작성한거라 내가 제대로 이해한게 맞나? 싶기도 하다. 허나 계속 보다보면 언젠간 알게 될꺼라 믿는다. 암튼 도움이 되었길 바라며 마치겠다.