티스토리 뷰
[Flutter] 상태가 변화하지 않았어도 모든 위젯을 강제로 리빌드시키는 법 (모든 위젯을 리빌드)
Dev Ttangkong 2023. 2. 2. 17:19
플러터를 시작하시는 분들은 상태가 변화하였다고 프레임워크에 이를 알리기 위해 setState() 함수를 호출합니다.
모든 위젯의 상위 위젯을 RootPage라고 정의하고 여러 페이지를 이동한 상황에서 사용자가 인위적인 방법으로 테마를 다크로 설정하였다면, 최상위 위젯인 RootPage의 setState() 함수를 호출하면 되지 않나라고 간단하게 생각하는 분이 있을 겁니다.
하지만 이러한 방법은 setState() 함수가 호출된 상태만이 업데이트되었다고 알리는 행위이기 때문에 당연히 실패할겁니다.
(그 하위 위젯들 모두 상태가 변화했다고 정의하는 것이 아닙니다.)
이는 당연히 비효율적인 빌드를 방지하는 매우 효율적인 기법입니다.
그렇다고 해서 생명주기 함수인 didUpdateWidget() 함수를 오버라이드하여 상위 위젯이 변화했을 때 자신도 상태가 변화했다고 정의한다면 이는 매우 비효율적인 행위입니다.
개발자로서 절대 해서는 안되는 행위인 것이죠.
해당 상황에서는 간단하게 그냥 다음 프레임에 재빌드가 필요하다고 정의하는 함수를 호출합니다.
bash
element.markNeedsBuild();
실제로 제가 사용하는 코드를 들고와보도록 하겠습니다.
bash
/// [RootPage]는 모든 위젯의 상위 위젯입니다.
///
/// - [RootPage.rebuild]를 호출하여 모든 위젯을 다시 그릴 수 있습니다.
class RootPage extends StatefulWidget {
const RootPage({super.key});
static late RootPageRebuild rebuild;
@override
State<RootPage> createState() => _RootPageState();
}
class _RootPageState extends State<RootPage> with WidgetsBindingObserver {
/// 모든 자식 위젯을 다시 빌드합니다.
static void rebuildAllChildren(BuildContext context) {
// 해당 위젯의 모든 하위 위젯들을 다음 프레임에 재구성해야할 위젯으로 정의합니다.
void rebuild(Element element) {
// 해당 Element를 다음 프레임에 재구성해야할 위젯으로 정의합니다.
//
// - 한 프레임에 Element를 두 번 빌드하는 것은 비효율적이기 때문에,
// 애플리케이션과 위젯은 빌드 자체가 아니라 프레임이 시작되기 전 이벤트 핸들러 동안에만
// 위젯을 재구성하도록 구조화되어야 합니다.
// (그냥 위젯들을 리빌드시킨 답시고 일일이 한 위젯을 빌드시키지 말라는 뜻입니다
// 한 프레임을 기준으로 빌드를 하라는 뜻입니다)
//
element.markNeedsBuild();
element.visitChildren(rebuild);
}
// 현재 context를 Element로 정의하고 하위 위젯을 참조합니다.
(context as Element).visitChildren(rebuild);
}
@override
void initState() {
super.initState();
RootPage.rebuild = () {
// 현재 화면이 그려지고 있는 상태라면 (렌더링 중이면)
final isFrameRendering = WidgetsBinding.instance.schedulerPhase != SchedulerPhase.idle;
if (isFrameRendering) {
setState(() => rebuildAllChildren(context));
} else {
// 화면이 그려지지 않는 상태라면 해당 작업이 완료된 후 호출됩니다,
// 그렇지 않은 경우라면 호출되지 않습니다 다시 그릴때까지 호출을 보류합니다.
//
// 만약 그리고 있지 않은 상태라면 addPostFrameCallback 함수를 사용하지 마세요.
WidgetsBinding.instance.addPostFrameCallback((_) {
setState(() => rebuildAllChildren(context));
});
}
};
}
@override
Widget build(BuildContext context) {
return ...;
}
}
이해가 되셨을거라고 생각하고 그럼 이만.
관련 패키지
flutter_rebuildable | Flutter package
This package that provides widgets to help rebuild all descendant widgets or all widgets in the entire application.
pub.dev
'플러터 (Flutter)' 카테고리의 다른 글
[Flutter] 위젯이 화면 상에서 사라지거나 위젯 트리 상에 존재하지 않았을 때도 상태를 유지하는 방법 (0) | 2023.02.13 |
---|---|
[Flutter] 자식 위젯의 제스처 또는 포인터 수신을 막는 방법 (0) | 2023.02.12 |
[Flutter] 자식 위젯의 State 또는 부모 위젯의 State를 참조하는 방법 (0) | 2023.01.30 |
[Flutter] 자식 Listener의 트리거가 부모 Listener에게도 전파되는 것을 방지하는 방법 (0) | 2023.01.29 |
[Flutter] 기기 테마가 변경되면 콜백되는 함수 (0) | 2023.01.08 |
- Total
- Today
- Yesterday
- 디자인 패턴
- github
- html custom element
- 커스텀 요소
- 팩토리 메서드
- webpack
- svg
- JavaScript
- pagetransitionsbuilder
- 플러터
- 최적화
- 터치 효과
- Factory Method
- 조건부 타입
- jetpack compose
- 안드로이드
- 타입스크립트
- 전환 애니메이션
- 리플 효과
- 깃허브
- web
- 객체 지향
- android
- pageroute
- TypeScript
- omit
- 객체지향
- 안드로이드 개발
- Reflow
- Flutter
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |