본문 바로가기
Flutter&Dart

Flutter InheritedWidget이란, InheritedWidget 사용법

by 밥바비 2023. 1. 17.
반응형

이번 글에선 Flutter InheritedWidget에 대해 알아보겠습니다. InheritedWidget이란 무엇일까요? 

Flutter와 관련된 문서들을 보다 보면 종종 InheritedWidget이 언급되는 것을 보실 수 있습니다. 일반적인 상황에서는 직접 사용할 일이 없어 자세히 알아보지 않고 넘어가게 되죠. 그러나 여러 Flutter 패키지나 Flutter 내부적으로는 InheritedWidget을 자주 사용하기 때문에 한 번쯤 InheritedWidget이 무엇인지, 어떻게 사용하는지 알아볼 필요가 있습니다.

InheritedWidget이 필요한 상황

Flutter로 개발을 하다보면 여러 문제와 고민에 봉착하게 되는데, 그중 하나가 아래와 같은 상황입니다.

일반적인 위젯트리 상황 이미지
자식에서 부모의 데이터에 접근해야하는 경우

자식 위젯에서 상위 부모 위젯이 가지고 있는 데이터를 필요로 하는 경우죠. 이런 케이스를 처리하기 위해 기본적으로 떠올릴 수 있는 구조는 생성자를 통해 필요한 데이터를 parameter로 넘겨주는 방식입니다. 한, 두 단계의 위젯이 중첩된 구조에서는 단순하게 파라미터를 넘기는 방식이 더 효율적일 수 있습니다. 그러나 복잡한 구조의 위젯 트리를 구성할 때, 일일이 parameter를 넘기는 것은 꽤나 번거롭고 위젯 간 의존성을 증가시키는 부작용을 일으킬 수 있습니다.

 

그렇다면 이런 문제를 해결할 수 있는 다른 방법은 무엇일까요?

반응형

InheritedWidget이란 무엇인가

이런 경우를 위해 준비된 것이 바로 InheritedWidget입니다. InheritedWidget은 하위 위젯에서 특정 상위 위젯의 데이터로 바로 접근할 수 있도록 도와주는 위젯입니다. 아주 새로운 개념의 무언가가 튀어나온 것 같아 당황하셨나요? 사실 그렇지 않습니다.

 

Flutter로 개발을 경험해보셨다면 대부분 자신도 모르는 사이 InheritedWidget을 사용했을 가능성이 큽니다. 대표적인 것이 MediaQuery입니다.

var screenWidth = MediaQuery.of(context).size.width;

Flutter에서는 화면 크기를 구하기 위해 MediaQuery를 사용할 수 있는데, MediaQuery는 InheritedWidget을 상속받아 만들어졌습니다.

mediaquery 내부 코드 캡쳐
MediaQuery의 내부 구현 코드

이제 InheritedWidget이 우리와 완전 동떨어진 개념이 아니라는 것을 알았으니 InheritedWidget을 직접 사용해 보며 이해도를 높여보도록 하겠습니다.

InheritedWidget 사용법

전체 소스 코드는 여기서 확인하실 수 있습니다!

 

우선 아래와 같은 상황을 가정해 보겠습니다.

  •  Me 위젯은 GrandParent 위젯이 가지고 있는 color를 자신의 background Color로 사용하고 싶음
  • 트리 구조 : GrandGrandParent Widget => GrandParent Widget => Parent Widget => Me Widget

우선 부모와 자식을 잇는 통로 역할을 하는 InheritedWidget을 생성해 줍니다.

class ChildColor extends InheritedWidget {
  final Color color;

  const ChildColor({
    super.key,
    required this.color,
    required child,
  }) : super(
          child: child,
        );

  Widget build(BuildContext context) {
    return child;
  }

  @override
  bool updateShouldNotify(ChildColor oldWidget) {
    return color != oldWidget.color;
  }

  static ChildColor? of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<ChildColor>();
  }
}

기본적인 구조는 간단합니다. InheritedWidget을 상속받는 클래스를 만들고 updateShouldNotify와 of 메서드를 구현해줍니다. 두 메소드 이외에 자식으로 부터 접근하고자 하는 부모의 데이터(color)와 Widget 타입의 child를 파라미터로 받습니다. InheritedWidget은 단순 통로 역할이기 때문에 추가적인 작업없이 부모로 부터 받은 child 위젯을 build 메소드를 통해 바로 return 해줍니다.

 

이렇게 구현된  InheritedWidget을 사용하는 방식을 간단합니다. 우리는 GrandParent의 데이터를 공유하고 싶기 때문에 GrandParent 위젯에서 자식 위젯을 ChildColor 위젯으로 한 번 감싸 통로만 열어주면 됩니다. 이제 GrandParent 하위의 어디에서든 GrandParent의 color에 바로 접근할 수 있습니다.

inheritedWidget 사용 부분 코드
InheritedWidget(ChildColor)으로 자식 위젯을 감싸준다.

이 통로를 이용하기 위해서는 위에서 InheritedWidget에 구현된 of 메소드를 사용하게 됩니다.

static ChildColor? of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<ChildColor>();
}

of 메소드 사용화면 캡쳐 이미지
of 메소드를 통해 InheritedWidget 내부에 정의된 데이터에 접근한다

of 메소드를 구현할 때 사용된 dependOnInheritedWidgetOfExactType 메서드는 위젯 트리 상에서, 주어진 context와 가장 가까운 곳에 위치한 InheritedWidget을 찾아주는 역할을 합니다. 현재 구성된 위젯 트리는 아래와 같은 형태로 구성되어 있을 것입니다.

GrandGrandParent Widget => GrandParent Widget => ChildColor(InheritedWidget) => Parent Widget => Me Widget

이 구조에서 Me 위젯과 가장 가까운 InheritedWidget은 누구일까요? 바로 부모의 부모에 해당하는 ChildColor 위젯입니다! 이렇게 탐색된 ChildColor 내부엔 GrandParent 위젯으로 부터 넘겨진 color값이 존재한다는 것 기억하시죠?

 

이제 끝났습니다! 꽤 복잡한 과정을 거쳤지만 결론적으로 parameter를 넘기지 않고 자식 위젯에서 부모 위젯 내부에 존재하는 데이터에 접근할 수 있게 됐습니다.

InheritedWidget rebuild

InheritedWidget을 사용하면 단순 데이터 공유 이외에도 부모 위젯 rebuild 시, of 메서드를 통해 부모의 데이터에 접근한 자식 위젯만 rebuild 된다는 이점도 있습니다. 기존, 생성자를 통해 parameter를 넘기는 방식에선 부모로부터 넘겨진 parameter가 변경 됐을 때, 해당 parameter를 받은 모든 자식 위젯은 rebuild 됩니다.

 

실제 위 예시 코드에서 버튼을 눌렀을 때, setState로 GrandParent의 color 값을 변경하면 GrandParent 위젯과 Me 위젯의 build 메서드만 호출됨을 확인할 수 있습니다.

setState 사용 부분 화면 캡쳐
버튼 눌렀을때 setState 실행
이벤트 실행 결과 콘솔 화면 캡쳐 이미지
setState 실행 결과 log


간단한 예제를 통해 InheritedWidget에 대해 살펴보았는데 어떠셨나요? 어렵다고 느낀 분들이 꽤나 많았을 거라고 생각합니다. 그래서 그런지 구글 Flutter 팀에서도 InheritedWidget을 직접 사용하기보단 Provider 패키지를 사용하라고 권장하고 있습니다.

 

개인적으로 Provider는 InheritedWidget을 자세히 이해하지 않고도 쉽게 사용할 수 있도록 만든 패키지가 아닐까 생각합니다. InheritedWidget을 공부하면서 ‘뭐야? 이거 Provider랑 다를게 뭐지?’라는 생각을 했거든요!(Provider 패키지도 내부적으로는 InheritedWidget을 사용하지 않았을까 추측 중입니다…)

 

 

반응형

댓글