Theme.of(context)의 내부 탐색 과정
Flutter에서 Theme.of(context)는 BuildContext를 이용해 InheritedWidget을 검색하는해쉬테이블 방식으로 동작한다. Theme 위젯은 InheritedWidget을 확장한 형태로 존재한다.
📌 실제 내부 구현 방식 (Theme.of)
static ThemeData of(BuildContext context) {
final Theme? theme = context.dependOnInheritedWidgetOfExactType<Theme>();
return theme?.data ?? ThemeData.fallback();
}
- context.dependOnInheritedWidgetOfExactType<Theme>() 메서드는 현재 BuildContext에서 Theme를 찾는다.
- 만약 Theme 위젯이 없다면 ThemeData.fallback()을 반환한다.
- 만약 현재 build 함수에서 새롭게 Theme 을 정의한다면 BuildContext에서 아직 인식되지 않는다
Theme.of(context)가 현재 build에서 Theme 위젯을 찾지 못하는 이유
Flutter의 위젯 트리는 단순한 트리 구조가 아니라, Element Tree라는 메커니즘을 활용해 렌더링된다. Widget Tree는 불변(Immutable)하며, Element Tree는 이 위젯을 기반으로 동적으로 변한다.
📌 위젯이 렌더링되는 순서
- build 함수가 호출됨.
- 위젯이 Element Tree에 추가되지만, 새로운 Theme 위젯이 아직 등록되지 않음.
- Theme.of(context)가 실행될 때, Element Tree에서 Theme를 찾으려 하지만 아직 BuildContext에 반영되지 않음.
- 부모 MaterialApp.theme를 반환 → 새로운 Theme가 적용되지 않음.
📌 잘못된 예시 (현재 BuildContext에서 Theme가 반영되지 않음)
Theme(
data: ThemeData(primaryColor: Colors.red),
child: Container(
color: Theme.of(context).primaryColor, // ❌ 올바른 primaryColor를 찾을 수 없음
),
);
- Theme.of(context).primaryColor가 Colors.red가 아닌, MaterialApp.theme의 기본 primaryColor를 가져옴.
- 이는 Theme 위젯이 아직 Element Tree에 반영되지 않았기 때문.
✅ 1. Builder를 활용한 새로운 BuildContext 생성
Builder는 새로운 BuildContext를 생성하여, Theme가 적용된 이후에 Theme.of(context)를 실행하도록 도와준다.
📌 Builder를 활용한 해결 코드
Theme(
data: ThemeData(primaryColor: Colors.red),
child: Builder(
builder: (context) { // 새로운 BuildContext 생성
return Container(
color: Theme.of(context).primaryColor, // ✅ 정상적으로 Colors.red를 가져옴
);
},
),
);
- Builder는 새로운 BuildContext를 생성하므로, Theme.of(context)가 Theme을 정상적으로 찾을 수 있음.
- 불필요한 위젯 트리 재생성을 방지하면서, 최적화된 방식으로 Theme을 적용 가능.
✅ 2. 새로운 Stateful/Stateless 위젯으로 분리
Flutter의 StatelessWidget이나 StatefulWidget은 자동으로 새로운 BuildContext를 생성한다.
이를 활용하면 Builder 없이도 Theme.of(context)를 올바르게 사용할 수 있다.
📌 StatelessWidget을 활용한 해결 코드
Theme(
data: ThemeData(primaryColor: Colors.red),
child: MyCustomWidget(), // 새로운 StatelessWidget 사용
);
class MyCustomWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
color: Theme.of(context).primaryColor, // ✅ 정상적으로 Colors.red를 가져옴
);
}
}
- MyCustomWidget이 새로운 BuildContext를 가지므로, Theme.of(context)가 현재 Theme을 정상적으로 찾을 수 있다.
- Builder 없이도 구조적으로 깔끔한 코드 유지가 가능하다.
✅ 3. InheritedWidget을 활용한 확장성 높은 테마 관리
대규모 프로젝트에선 Theme.of(context)를 직접 호출하는 대신, InheritedWidget을 활용해 테마 데이터를 공유하는 것이 효율적이다
📌 커스텀 InheritedWidget을 활용한 Theme 관리
class CustomTheme extends InheritedWidget {
final ThemeData themeData;
const CustomTheme({required this.themeData, required Widget child, Key? key})
: super(key: key, child: child);
static ThemeData of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<CustomTheme>()!.themeData;
}
@override
bool updateShouldNotify(CustomTheme oldWidget) {
return oldWidget.themeData != themeData;
}
}
📌 사용 예시
CustomTheme(
themeData: ThemeData(primaryColor: Colors.red),
child: SomeWidget(),
);
class SomeWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
color: CustomTheme.of(context).primaryColor, // ✅ Colors.red 적용됨
);
}
}
- ThemeData를 효율적으로 공유하면서, Theme.of(context)를 직접 호출하는 의존성을 줄일 수 있음.
- 대규모 프로젝트에서 성능 최적화 및 재사용성을 높이는 패턴.
Theme 활용법 정리
✅ 기본적인 Theme.of(context)의 동작 원리
- Theme.of(context)는 현재 BuildContext의 InheritedWidget을 탐색하여 ThemeData를 반환함.
- 하지만, 현재 build에서 생성된 Theme는 아직 BuildContext에 반영되지 않음.
✅ 실제 대규모 앱에서 Theme.of(context) 문제를 해결하는 방법
- Builder 사용: BuildContext를 새로 생성하여 Theme 위젯을 정상적으로 참조할 수 있도록 함.
- 새로운 StatefulWidget/StatelessWidget 분리: 새로운 BuildContext를 자동으로 생성하여 문제 해결.
- InheritedWidget을 활용한 커스텀 테마 관리: 대규모 애플리케이션에서 테마 적용을 최적화하고 확장성 높임.
'Flutter' 카테고리의 다른 글
이벤트를 특정시간만 기다리고 초과시 에러처리하기 (0) | 2025.02.12 |
---|---|
Freezed 패키지에서 Equatable 패키지로 바꾼 이유 (0) | 2025.02.10 |
조건부 처리 - 삼항 연산자, switch, if 문 중 어떤걸 쓸까 (작성중) (0) | 2025.02.06 |
Flutter에서 Stomp로 소켓통신하기 (2) | 2025.02.05 |
어떻게 플러터는 변경된 위젯만 콕찝어서 다시 페인팅할 수 있는걸까? (0) | 2025.02.02 |