Flutter - WidgetsBinding.instance.addPostFrameCallback, initState에서 위젯 호출하기

반응형

WidgetsBinding.instance.addPostFrameCallback

  • 앱의 위젯 트리가 빌드된 후 실행되는 콜백 함수를 등록하는 메서드
  • 이를 통해 위젯이 빌드되고 화면에 렌더링 된 후 어떤 작업을 수행하고자 할 때 사용

쉽게 말해서 코드를 예로 들면...

initState 내에 위젯을 반환하면 오류가 발생하지만,

콜백 함수를 정의해서 위젯을 반환하면 initState 내에서도 위젯을 반환할 수 있다.

WidgetsBinding.instance.addPostFrameCallback((_) {
      // 코드 작성
    });

세부 설명

WidgetsBinding

  • 앱의 라이프사이클 및 이벤트를 관리하는 클래스

instance

  • 현재 실행 중인 앱의 인스턴스에 접근하는 방법
  • WidgetsBinding.instance : 실행중인 WidgetsBinding 인스턴스에 접근

addPostFrameCallback

  • 빌드된 위젯 트리가 렌더링 된 후 실핼될 콜백 함수 정의하는 기능
  • 위젯 렌더링이 완료된 후 호출되므로 화면에 렌더링된 위젯의 정보를 사용하여 작업 진행 가능

코딩 및 확인

initState에서 Dialog 팝업 위젯을 띄워보자.

  • 코드
@override
void initState() {
    super.initState();

    // 팝업 위젯 호출
    displayDialog();
}

// 팝업 띄우기
void displayDialog() {
    showDialog(
        context: context,
        builder: (context) => const AlertDialog(
        	actions: <Widget>[Center(child: Text("Hello World!"))],
        ),
    );
}

 

앱을 실행하면 다음과 같은 에러가 출력된다.

Exception has occurred.
FlutterError (dependOnInheritedWidgetOfExactType<_LocalizationsScope>()
or dependOnInheritedElement() was called before _MyWidgetState.initState() completed.
When an inherited widget changes,
for example if the value of Theme.of() changes,
its dependent widgets are rebuilt.
If the dependent widget's reference to the inherited widget is in a constructor or an initState() method,
then the rebuilt dependent widget will not reflect the changes in the inherited widget.
Typically references to inherited widgets should occur in widget build() methods.
Alternatively,
initialization based on inherited widgets can be placed in the didChangeDependencies method,
which is called after initState and whenever the dependencies change thereafter.)

 

빌드 이후에 위젯을 호출할 수 있도록 변경해보자.

  • WidgetsBinding.instance.addPostFrameCallback 함수 이용
class _MyWidgetState extends State<MyWidget> {
  @override
  void initState() {
    super.initState();

    WidgetsBinding.instance.addPostFrameCallback((_) {
      displayDialog();
    });
  }

 

오류없이 팝업이 잘 출력되는 것을 확인할 수 있다.

 

  • 전체 코드
import 'package:flutter/material.dart';

void main() {
  runApp(const MaterialApp(
    home: Scaffold(
      body: MyWidget(),
    ),
  ));
}

class MyWidget extends StatefulWidget {
  const MyWidget({super.key});

  @override
  State<MyWidget> createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> {
  @override
  void initState() {
    super.initState();

    WidgetsBinding.instance.addPostFrameCallback((_) {
      displayDialog();
    });
  }

  void displayDialog() {
    showDialog(
      context: context,
      builder: (context) => const AlertDialog(
        actions: <Widget>[Center(child: Text("Hello World!"))],
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

추가. initState에서 비동기 함수 실행하기

이건 한국 플러터 개발자 모임에서 보았던 질문이다.

 

혹시 initstate 안에 상태 변환을 하는
비동기 함수들을 넣어서 실행 시키고 있는데

비동기 함수간 순서를 주려면 어떻게 해야 될까요

 

이 질문을 나는 위에서 설명한 addPostFrameCallback 함수를 이용하여 해결했는데,

initState에서 비동기 함수를 실행하고 순서를 조정해보자.

(이 것이 정답이라는 것은 아니고, 이런 식으로도 해결할 수 있다는 것이다.)

  @override
  void initState() {
    super.initState();

    print('#####Order 1 // Init');

    WidgetsBinding.instance.addPostFrameCallback((_) async {
      printFuture2();
      printFuture();
      await printAwait();
      printNow();
    });
  }

  void printFuture2() async {
    await Future.delayed(const Duration(seconds: 2));
    print('#####Order 2 // Future : Delayed Second : 2');
  }

  void printFuture() async {
    await Future.delayed(const Duration(seconds: 1));
    print('#####Order 3 // Future : Delayed Second : 1');
  }

  Future<void> printAwait() async {
    print('#####Order 4 // await');
  }

    void printNow() {
    print('#####Order 5 // Now');
  }


/* 콘솔
* I/flutter (11476): #####Order 1 // Init
* I/flutter (11476): #####Order 4 // await
* I/flutter (11476): #####Order 5// Now
* D/EGL_emulation(11476): app_time_stats: avg=1449.68ms min=1449.68ms max=1449.68ms count=1
* I/flutter (11476): #####Order 3 // Future : Delayed Second : 1
* I/flutter (11476): #####Order 2 // Future : Delayed Second : 2
*/

참고

 

반응형