본문 바로가기
flutter/Widget of the Week

Flutter[플러터] / Builder 를 사용하여 새로운 BuilderContext를 생성하여 하위 위젯에 접근하기 (빌더, 빌드콘텍스트, 하위 트리) Builder (Flutter Widget of the Week)

by ch5c 2025. 6. 29.
반응형

Builder class

위젯의 자식을 생성하기 위해 빌더 콜백을 사용하는 빌드 메서드를 가진 상태 비저장 유틸리티 위젯입니다.

https://youtu.be/xXNOkIuSYuA

공식 문서 코드

 


별도의 BuildContext 를 생성하고 싶은 경우가 있는가? 특히 context 로는 접근할 수 없는 상황 말이다.

대부분의 Flutter 위젯은 상위 위젯에 대한 어떠한 추가 정보도 찾지 않고 만들어진다. 하지만 때로는 상위 위젯의 빌드 컨텍스트에  동일한 빌드 메서드로 몇 가지 코드를 작성해야 하는 경우가 생긴다. 이럴 때에 필요한 것이 바로 Builder 위젯이다.

Builder 위젯은 BuildContext 에 직접 접근할 수 있게 도움을 주는 위젯이다. 주로 하위 위젯에서 상위 위젯의 BuildContext 와 분리된 새로운 BuildContext 를 생성할 필요가 있을 때 사용하게 된다. 바로 알아보자.

하위 속성
속성명 타입 기본값 설명
builder WidgetBuilder 새로운 BuildContext에서 위젯을 생성하는 콜백 함수

 

먼저 이 코드를 봐보자.

Widget build(BuildContext context) {
  return Scaffold(
    body: Center(
      child: TextButton(
        onPressed: () {
          print(Scaffold.of(context).hasAppBar);
        },
        child: const Text('hasAppBar'),
      ),
    ),
  );
}
// 동작 안하는 코드임

이 코드는 현재 Scaffold 내에 AppBar 가 사용되고 있는지 출력을 해주는 코드이다.

근데 이 코드를 실행해 보면 내가 원하는 결과인 false 나 true 가 출력이 되는 것이 아닌 오류가 출력이 된다.

Scaffold.of() called with a context that does not contain a Scaffold.

이 오류가 뜨는 상황은 일반적으로 Scaffold, Navigator, ThemeData, MediaQuery 와 같이 상속된 위젯 혹은 상태 관리 메서드에 접근하려는 경우다. 즉 BuildContext 가 Scaffold 위젯보다 더 위에 있거나 별개의 context 로 분리되어 있기 때문이란 것이다.

이 오류를 피해 가기 위해선 하위 위젯 트리를 자체 빌드 메서드로 별도의 독립형 위젯으로 넣으면 해결된다.

class Test extends StatelessWidget {
  const Test({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: const Center(
        child: HasAppBarButton(), // 독립형 위젯
      ),
    );
  }
}

class HasAppBarButton extends StatelessWidget {
  const HasAppBarButton({super.key});

  @override
  Widget build(BuildContext context) {
    return TextButton(
      onPressed: () {
        print(Scaffold.of(context).hasAppBar);
      },
      child: const Text('hasAppBar'),
    );
  }
}

이 코드는 StatelessWidget 된 별도의 위젯을 만들어서 Scaffold 내부에 배치하고 있고 해당 위젯에서 BuildContext 를 안전하게 사용하고 있기 때문에 별 문제는 없어 보인다. 하지만 이러한 방법이 때로는 빌드 메서드 중 하나를 너무 작거나 간단하게 만들어 독립형 위젯으로 나누는 게 의미가 없다고 느껴질 수 있다.

이제 이럴 때 Builder 위젯을 사용해 주면 된다.

Widget build(BuildContext context) {
  return Scaffold(
    body: Builder(
      builder: (BuildContext context) {
        return Center(
          child: TextButton(
            onPressed: () {
              print(Scaffold.of(context).hasAppBar);
            },
            child: const Text('hasAppBar'),
          ),
        );
      },
    ),
  );
}

상위 위젯에 접근하기 위해서는 빌드 컨텍스트가 필요한 하위 위젯에 Builder 써주면 된다. 이러면 끝이다!

다른 종료(closures)들과 마찬가지로 Builder 위젯은 상위 위젯이 먼저 완벽하게 만들어지도록 보장하므로 하위 위젯의 빌드 컨텍스트를 통해 위젯을 찾을 수 있게 된다.

Builder(
  builder: (BuildContext context) {
    return Container(...);
  },
),

하위 위젯트리는 Builder 위젯으로 빌더 함수가 입력될 때 구성된다.

허나 다시 말하자면 독립형 위젯을 만들어서 하위 위젯 트리에 동일한 기능을 할 수 있도록 만들 수도 있다.

이렇게 Builder 위젯을 사용하여 필요할 때 업데이트 된 컨텍스트를 쉽게 가져올 수 있다.

근데 살짝 예외의 경우도 있다.

  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: () {
        Scaffold.of(context).showSnackBar(...); // 오류 발생!
      },
      child: Text('Show Snackbar'),
    );
  }

여기서 오류가 뜨는 이유는 ElevatedButton 의 context 는 Scaffold 보다 위에 있을 수도 있기 때문이다. 즉 Scaffold.of(context) 가 실패하게 된다. 근데 이럴 때는 Builder 로 감싸기보단 ScaffoldMessenger 를 사용해 주면 된다.

ScaffoldMessenger.of(context).showSnackBar(...);

근데 사실 위의 오류 코드는 이제 지원하지 않는 방식이라 그냥 ScaffoldMessenger 를 표준으로 사용해 주면 되겠다.

 

이렇게 Builder 위젯을 알아보았다. 솔직히 이 위젯이 모든 빌더 위젯의 기본이 되는 위젯이기 때문에 언제 가는 꼭 알아봐야 하는 위젯이었다. 근데 뭐 솔직히 빌더함수 붙어있는 위젯은 개나 소나 나 context 를 들고 있어서.. 굳이 Builder 를 사용할까 싶긴 한데 암튼 도움이 되었길 바라며 마치겠다.

 

반응형