Flutter 의 InheritedWidget 에 대해 알아보기 전에
BuildContext.dependentOnInhheritedWidgetOfExactType 에 대해 이해하는 것이 중요하다.
플러터 공식 문서에도 InheritedWidget 의 설명을 보면 BuildContext.dependentOnInheritedWidgetOfExactType 에 대한 설명이 먼저 표기되어 있다. <- 이 뜻은 BuildContext.dependentOnInheritedWidgetOfExactType 에 대한 선 이해가 필요하다는 뜻 아닐까?
MediaQuery.of(context) 의 예시를 참고하며 알아보겠다.
@override
Widget build(BuildContext context) {
// This widget is now bound to the MediaQuery,
final mediaQueryData = MediaQuery.of(context);
return Widgets(...);
}
Flutter 로 개발하다 보면 너비에 따라 위젯을 변경하고 싶을 때 이렇게 쓰는 것을 대부분 보았을 것이다.
위 코드는 위젯을 미디어 쿼리에 연동하게 되고 미디어 쿼리가 변동할 때마다 재생성될 수가 있다.
그렇다면 그동안 살펴보지 않았던 MediaQuery 의 of 메서드를 살펴보자!
static MeidaQueryData of(BuildContext context) {
assert(context != null);
assert(debugCheckHasMediaQuery(context));
return context.dependOnInheritedWidgetOfExactType<MediaQuery>()!.data;
}
위 메서드를 보면 MediaQueryData 의 인스턴스를 리턴하는데 (= 첫 줄)
내부 구현을 보면 context.dependOnInheritedWidgetOfExactType 을 리턴하는 것을 볼 수 있다.
이는 context.dependOnInheritedWidgetOfExactType 메서드가 위젯 트리를 탐색하면서 특정 타입의 인스턴스를 찾는데 (= 타고 타고 올라가면서 가장 가까운 MediaQuery 를 찾는다는 뜻) 이 예시에서는 <MediaQuery> 의 타입의 인스턴스를 찾는 것이다.
그리고 .data 를 써줌으로써 MediaQueryData 를 리턴할 수 있다.
↓
특정 타입의 인스턴스를 찾는다고?
위 설명에서 알 수 있다시피 of 메서드를 사용하기 전에
위젯 트리를 (올라가며) 탐색하면서 특정 타입의 인스턴스를 찾으려면 MediaQuery 는 위젯 트리의 상위에 위치해 있어야 겠다.
근데 MediaQuery 가 어디에 있는거지? 라는 생각이 들었다.
MediaQuery 는 어디에 존재했던 것일까?
MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Home'),
),
),
debugShowCheckedModeBanner: false,
)
MaterialApp 은 일반적으로 앱의 최상위 레벨에 위치한다.
MaterialApp 위젯 자체에는 MediaQuery가 직접 포함되어 있지는 않지만, 여러 내장 위젯들이 MaterialApp 내에서 사용될 때 자동으로 MediaQuery 에 접근하게 된다.
MaterialApp 위젯은 앱의 테마, 로케일, 라우트 등 여러가지 정보와 설정을 포함하는데, 이는 종종 MediaQuery 데이터를 사용하여 설정되기 때문이다.
< 또 다른 예시 >
참고로 우리 프로젝트에서는 주 고객층이 연령대 높으신 분들이 많은데 이 분들은 폰 설정에서 텍스트 크기를 크게 설정하시는 분들이 많았다.
그래서 UI 가 일부 안 보이거나 UI overflow 가 되는 현상이 많은데
사실 이러면 안되지만, 폰 설정에서 텍스트 크기를 크게 설정하여도 우리는 텍스트 크기를 고정시켜 커지지 않도록 설정하였다..
이는 MateralApp 의 Builder 에서 MediaQuery 를 직접 삽입하면 된다.
MaterialApp(
builder: (context, child) {
// 텍스트 사이즈를 고정시키게 하기
return MediaQuery(data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0), child: child!);
}
home: MyHomeWidget(),
);
이는 MaterialApp이 위젯 트리의 최상위에 있기 때문에, 그 아래의 모든 위젯들은 MediaQuery.of(context)를 호출하여 MediaQuery 데이터에 접근할 수 있게 된다.
InheritedWidget 에 대해 본격적으로 알아보자.
설명이 늦었는데 InheritedWidget 은 앞서 살펴 보았던 예시를 참고하게 되면
⭐️ MediaQuery 위젯은 하위 자식 위젯들에게 MediaQueryData 를 제공하기 위해 내부적으로 InheritedWidget 을 사용한다.
보통 InheritedWidget 을 사용하기 위해서는 다음과 같이 선언하는 것을 볼 수 있다.
(실제 MediaQuery 코드는 다를 수 있다. 하지만 기본적인 형태는 동일하다는 것을 참고하자.)
// 보통 InheritedWidget 을 extends 하여 사용한다!!
class MediaQuery extends InheritedWidget {
const MediaQuery({
Key? key,
required this.data,
required Widget child,
}) : super(key: key, child: child);
final MediaQueryData data;
// of 메서드 존재
static MediaQueryData of(BuildContext context) {
final MediaQuery? result = context.dependOnInheritedWidgetOfExactType<MediaQuery>();
assert(() {
...
}());
return result!.data;
}
// updateShouldNotify 는 반드시 구현해야 함
@override
bool updateShouldNotify(MediaQuery oldWidget) {
return data != oldWidget.data;
}
}
전체 코드를 보고 나니 MediaQuery 는 InheritedWidget 을 사용하고 있다는 것을 알게 되었고 of 메서드도 같이 존재함을 알았다.
보통 이것이 InheritedWidget 을 사용하기 위한 일반적인 코드 작성법이라고 보면 된다.
< inheritedWidget 일반적인 구현 >
- InheritedWidget 을 extends 하여 사용
- of 메서드 존재
- updateShouldNotify 메서드 존재
또한 추가적으로 알아두어야 할 것은 InheritedWidget 에서 재빌드를 결정하는 메서드는 updateShouldNotify 이다. 이 메서드는 override 된 것을 볼 수 있는데 즉, 필수적으로 추가 구현이 필요하다는 것을 알 수 있다.
updateShouldNotify 는 InheritedWidget에서 정말 중요한 역할을 하는데.
이 메서드는 위젯이 변경될 때 그 변경이 하위 위젯들에게 전파되어야 하는지를 결정한다. 즉 true 를 반환하게 될 경우 하위 위젯들에게 전파를 허락하게 된다고 보면 된다. (= 재빌드 결정)
다시 기억 상기해야 할 것은..
자식 위젯에서 MediaQuery.of 메서드를 사용할 때 MediaQuery 가 위젯 트리 상위에 존재해야 한다는 것이다.
context.dependOnInheritedWidgetOfExactType 로 상위에 존재하는 MediaQuery 를 탐색한다고 했다.
즉 MediaQuery 는 위젯 트리의 상단에 위치하게 됨으로써 하위 위젯들은 InheritedWidget 의 상태 (= MediaQuery ) 를 접근할 수 있는 것이다.
InheritedWidget 특징을 정리해보자.
InheritedWidget 의 특징을 보면 다음과 같다.
- 상태 공유: InheritedWidget 은 주로 애플리케이션의 상태를 공유하고, 위젯 트리의 하위 레벨에 있는 위젯들이 상태를 접근할 수 있도록 하는 역할을 한다.
- 효율적인 데이터 전파: InheritedWidget 은 위젯 트리의 한 지점에서 다른 지점으로 효율적으로 데이터를 전파한다.
- 컨텍스트의 의존성: InheritedWidget 은 BuildContext 와 함께 작동하여, 어떤 위젯이 InheritedWidget 에 의존하고 있는지 판별한다. 이 의존성이 활성화되면 InheritedWidget 이 업데이트될 때 의존하고 있는 위젯들은 재빌드된다.
context.dependOnInheritedWidgetOfExactType 메서드 이해할 때까지 다시 정리
이 메서드는 현재 context 에서 위로 올라가며 트리를 탐색하면서 지정한 타입의 InheritedWidget 을 찾는다.
(BuildContext 는 현재 위젯의 위젯트리상에서 위치에 관한 정보를 담고 있다.)
그리고 InheritedWidget 에 의존성을 만든다.
따라서 InheritedWidget 이 변경될 때, 이 메서드를 호출하여 의존성을 만든 위젯도 재빌드된다.
https://api.flutter.dev/flutter/widgets/InheritedWidget-class.html
InheritedWidget class - widgets library - Dart API
Base class for widgets that efficiently propagate information down the tree. To obtain the nearest instance of a particular type of inherited widget from a build context, use BuildContext.dependOnInheritedWidgetOfExactType. Inherited widgets, when referenc
api.flutter.dev
'🐦 Flutter' 카테고리의 다른 글
Flutter NavigationController 에서 iOS 로 이동(push)해보기 (2) | 2024.01.13 |
---|---|
Debounce, Throttle (2) | 2023.12.09 |
[Dart] mixin (0) | 2023.04.09 |
AES-256 암호화 (5) | 2023.04.01 |
[Flutter] RenderFlex children have non-zero flex but incoming height constraints are unbounded (1) | 2023.02.03 |