InheritedWidget

2026. 1. 22. 00:29·Flutter/Dart

 

StatefulWidget과 StatelessWidget을 통해 화면을 구성하게 되었을 때, 변화가 필요한 위젯이 트리의 끝부분에 있다면 트리의 top → bottom 까지 불필요한 데이터 전달이 일어나게 된다. (위젯 트리 깊이가 깊을수록, 불필요하게 더 오래걸림)

따라서, 바로 Tree 의 Top에 접근하여 바로 데이터를 가져오게 할 수 있도록 하는 것이 바로 Inherited Widget 이다.

 

Inherited Widget은 상태관리 라이브러리의 근간이라 할 수 있는 Provider의 핵심이므로 알아 둘 필요가 있다.

구글에서 이런 Inherited 위젯을 만들긴 했지만 사실 맘에 안들었는지 Provider라는 패키지를 쓰라고 권장한다.

고로 여러분이 이 글에서 얻어가실거는 Inherited Widget이 왜 탄생했냐 정도만 아시면 된다.

 

 

간단한 동작 원리를 알아보자

먼저 InheritedWidget을 extends 하는 class를 만든다.

child 위젯은 반드시 포함해야 한다. InheritedWidget인 FrogColor의 자손이 되는 모든 위젯은, 해당 위젯의 필드에 접근할 수 있다.

class FrogColor extends InheritedWidget {
  const FrogColor({
    super.key,
    required this.color,
    required super.child,
  });

  final Color color;

  static FrogColor? maybeOf(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<FrogColor>();
  }

  static FrogColor of(BuildContext context) {
    final FrogColor? result = maybeOf(context);
    assert(result != null, 'No FrogColor found in context');
    return result!;
  }

  @override
  bool updateShouldNotify(FrogColor oldWidget) => color != oldWidget.color;
}

위의 코드에서, InheritedWidget에서는 bool값을 리턴하는 updateShouldNotify를 override 한다.

여기서 updateShouldNotify 함수는 어떤 역할을 할까?

 

updateShouldNotify는, 상속 받은 위젯이 있을 때 모든 상황에서 다시 빌드 되는걸 방지하기 위해 있다.

빌드가 필요한 경우 true, 빌드가 필요없는 경우 false를 리턴하여, 재빌드되는 상황을 컨트롤한다.

 

다시 본론으로 돌아오자면, 보통 Inherited Widget을 만들 때 내부에 보통은 'of'라는 Static 함수를 선언해서 사용한다.

위에코드에도 있지만 다시 언급하자면 아래와 같은 형태이다.

  static FrogColor of(BuildContext context) {
    final FrogColor? result = maybeOf(context);
    assert(result != null, 'No FrogColor found in context');
    return result!;
  }

 

그리고 이 of 함수는 다른 위젯에서 아래와 같은 형태로 사용된다.

// continuing from previous example...
class MyPage extends StatelessWidget {
  const MyPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: FrogColor( // 여기서 사용
        color: Colors.green,
        child: Builder(
          builder: (BuildContext innerContext) {
            return Text(
              'Hello Frog',
              style: TextStyle(color: FrogColor.of(innerContext).color),
            );
          },
        ),
      ),
    );
  }
}

하지만 구현된 of 코드에 관심을 기울여야 한다.

접근하고 사용하려면 반드시 context에 접근해야 하며, 만약 이 FrogColor 위젯을 찾을 수 없는 경우 바로 오류를 발생시킨다.

이는 사용자가 FrogColor 위젯을 생성하지 않고(선언은 하였지만) 사용할 경우 컴파일타임에 어떤 오류도 발생시키지 못한다는 점이다.

 

 

구체적으로 InheritedWidget을 알아야 하는 이유

context.dependOnInheritedWidgetOfExactType의 동작 이해

// 이 코드를 보면
class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);  // 왜 여기서?
    
    return ElevatedButton(
      onPressed: () {
        // final theme = Theme.of(context);  // 여기서는 안 되나?
        showDialog(...);
      },
      child: Text('Click'),
    );
  }
}

InheritedWidget을 모르면: "context는 어디서든 쓸 수 있는 거 아닌가?"

InheritedWidget을 알면: "아, dependOnInheritedWidgetOfExactType은 위젯 트리를 위로 탐색하는 거구나. build 시점의 context와 콜백 안의 context는 다른 시점이니 조심해야겠다."

→ context 구분 가능

 

 

listen: false의 진짜 의미

class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // 이 두 코드의 차이는?
    final counter1 = Provider.of<CounterModel>(context);  // listen: true (기본)
    final counter2 = Provider.of<CounterModel>(context, listen: false);
    
    return Column(
      children: [
        Text('${counter1.count}'),  // counter 변경 시 rebuild ✅
        ElevatedButton(
          onPressed: () {
            counter2.increment();  // counter 변경 시 rebuild ❌
          },
          child: Text('Increment'),
        ),
      ],
    );
  }
}

InheritedWidget을 모르면: "listen: false가 뭔지는 아는데, 정확히 왜 필요한지 모르겠다."

InheritedWidget을 알면:

  • listen: true → dependOnInheritedWidgetOfExactType 호출 → 구독 관계 형성 → updateShouldNotify가 true면 rebuild
  • listen: false → getInheritedWidgetOfExactType 호출 → 구독 안 함 → 데이터만 가져옴, rebuild 안 됨

"아, 버튼 클릭 핸들러에서는 상태만 변경하고 rebuild는 필요 없으니 listen: false를 쓰는 게 성능에 좋겠구나."

 

 

성능 최적화: 불필요한 rebuild 방지

class ExpensiveWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print('ExpensiveWidget rebuilt!');
    
    return Column(
      children: [
        // ❌ counter 변경 시 전체 ExpensiveWidget rebuild
        Text('Count: ${Provider.of<CounterModel>(context).count}'),
        
        // ✅ Consumer로 필요한 부분만 rebuild
        Consumer<CounterModel>(
          builder: (context, counter, child) {
            return Text('Count: ${counter.count}');
          },
        ),
        
        ExpensiveChildWidget(),  // 얘는 rebuild 안 됨
      ],
    );
  }
}

InheritedWidget을 모르면: "Consumer를 쓰라는데 왜 쓰는지 모르겠다. Provider.of랑 뭐가 다른가?"

InheritedWidget을 알면:

  • Provider.of(context) → 이 전체 위젯이 InheritedWidget 구독
  • Consumer → Consumer 내부만 구독

"InheritedWidget의 updateShouldNotify가 true를 반환하면 구독한 위젯만 rebuild되니까, 최소 범위로 구독하는 게 성능에 좋구나. 그래서 Consumer를 쓰는 거구나."

 

 

정리: 실무에서 마주치는 구체적 상황들

InheritedWidget을 이해하면:

  1. 에러 메시지 해석 가능: "Provider not found", "Scaffold.of() called with a context..." 등의 에러가 왜 발생하는지 정확히 이해
  2. context의 범위 파악: 어느 시점의 context인지, 어떤 InheritedWidget에 접근 가능한지 판단
  3. 성능 최적화: 언제 Consumer를 쓰고, 언제 Provider.of를 쓸지 근거를 가지고 결정
  4. 디버깅 시간 단축: 위젯 트리 구조를 머릿속에 그리면서 문제 원인을 빠르게 찾음
  5. 코드 리뷰: 동료의 코드에서 잠재적 버그나 성능 이슈를 발견

"InheritedWidget을 직접 쓸 일은 없지만, Flutter의 핵심 메커니즘이라서 알아야 한다"는 게 바로 이런 의미입니다.

 

'Flutter > Dart' 카테고리의 다른 글

추상클래스와 인터페이스  (0) 2026.01.21
Extension  (0) 2026.01.20
Mixin 과 보일러 플레이트 코드 제거  (0) 2026.01.17
Flutter 로드맵 - 2025  (0) 2024.12.02
Dart : isolate  (2) 2022.12.13
'Flutter/Dart' 카테고리의 다른 글
  • 추상클래스와 인터페이스
  • Extension
  • Mixin 과 보일러 플레이트 코드 제거
  • Flutter 로드맵 - 2025
정총무
정총무
개발과 투자의 귀재
  • 정총무
    Take a Chance
    정총무
  • 전체
    오늘
    어제
    • 분류 전체보기 (56)
      • 블로깅 리스트 (1)
      • 안드로이드 (8)
        • 코루틴 (0)
        • Kotlin (6)
        • 디자인패턴 (2)
      • Flutter (22)
        • Flutter 개발 생산성 극대화 (1)
        • Dart (7)
        • Skills (9)
        • Flutter 로 Android 앱 출시 (2)
        • Flutter 로 IOS 앱 출시 (0)
      • IOS (0)
      • Node.js (1)
      • 개발 공통 기술 (5)
      • 가끔 끄적임 (4)
      • 알고리즘 (2)
      • 자료구조 (8)
      • 맛집 (1)
      • 웹개발 (3)
        • 타입스크립트 (1)
        • 자바스크립트 (2)
      • Conference (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 미디어로그
    • 위치로그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    playstore
    비즈까페
    흑백요리사
    코드 간소화
    debounce
    제네릭
    토이 프로젝트
    다형성
    사이드 프로젝트
    기본자료형
    Flutter Allience
    mixin
    맛집 탐방
    코틀린
    pagination
    WebP
    Flutter
    github
    앱 출시
    svg
    안드로이드 개발
    인스타그램
    2026
    https
    DART
    Throttle
    gorouter
    Flutter Korea
    android
    primitive type
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.4
정총무
InheritedWidget
상단으로

티스토리툴바