🐦 Flutter

[Flutter] InheritedWidget 에 대한 고찰 1 - BuildContext.dependentOnInhheritedWidgetOfExactType

ji-hyun 2023. 10. 14. 11:42

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 일반적인 구현 >

 

  1. InheritedWidget 을 extends 하여 사용
  2. of 메서드 존재
  3. 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