Flutter / 03. BuildContext

presentnow·2023년 7월 31일

stateless widget, stateful widget 에서 build 메서드를 호출할 때 BuildContext를 인자로 받는다. 이 BuildContext는 어떤 역할을 하는 것일까?

1. 예제01. snackbar

버튼을 눌렀을 때 스낵바를 보이도록 한다.


void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});
  final snackBar = const SnackBar(content: Text('yeah! success!'));

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: Column(
            children: [
              // 실패예제
              OutlinedButton(
                onPressed: () {
                  ScaffoldMessenger.of(context).showSnackBar(snackBar);
                },
                child: const Text('show snackbar'),
              ),
              // 성공예제
              Builder(builder: (context) {
                return OutlinedButton(
                  onPressed: () {
                    ScaffoldMessenger.of(context).showSnackBar(snackBar);
                  },
                  child: const Text('show snackbar'),
                );
              }),
            ],
          ),
        ),
      ),
    );
  }
}

실패예제

실패 예제인 버튼을 눌렀을 때는 스낵바가 보이지 않고, "No ScaffoldMessenger widget found." 이라는 문구를 확인할 수 있다.
BuildContext 인 context는 widget tree에서 widget의 위치에 대한 정보를 담고 있다.
ScaffoldMessenger.of(context)에서 context는 Scaffold 의 context가 아닌, MyApp의 context라고 할 수 있다. ScaffoldMessenger.of(context).showSnackBar를 호출 할 때, 주어진 context 를 참고하여 위젯의 위치정보를 파악한 후, 상위의 Scaffold를 탐색하는 과정을 거치게 된다. 그런데 여기에서 사용된 context는 앞서 말했듯, MyApp의 위치정보를 담고있는 context 이므로, MyApp 상위에는 Scaffold 가 발견되지 않아서 이런 문제가 발생하는 것 이다.


성공예제

방법은 바로.... Builder widget을 사용하는 것 이다. Builder widget을 사용하게 되면, 더이상 ScaffoldMessenger.of(context) 에서 인자로 주어지는 context는 MyApp 의 context가 아니다!! 따라서, 주어진 context에서 위젯의 위치정보를 확인한 후, 상위의 Scaffold를 탐색하면 ... 발견!!!



1. 예제02. Navigator

MyApp을 처음 화면으로 하고, 버튼을 클릭하여 다음 페이지로 이동한다.


실패예제⚙💊

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              const Text('first page'),
              const SizedBox(
                height: 30.0,
              ),
              OutlinedButton(
                onPressed: () {
                  Navigator.push(
                    context,
                    MaterialPageRoute(builder: (context) => const SecondPage()),
                  );
                },
                child: const Text('go to second page'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

위 코드에서 버튼을 클릭했을 때 "Navigator operation requested with a context that does not include a Navigator." 이런 exception 문구를 확인할 수 있다.

말 그대로, Navigator 작업이 Navigator가 포함되지 않은 context로 요청되었다는 의미이다.
Navigator를 생성하는 방법은 MaterialApp을 생성하는 것 이다. 그런데, Navigator 작업에 주어진 context는 MyApp의 context 이고, MyApp은 MaterialApp에 포함되어 있지 않다. 따라서 위와 같은 문제가 발생하는 것이다.


성공예제 ✨🙌

위의 snackbar 예제 처럼 Builder widget을 이용해도 기능이 동작한다! 하지만, 이번에는 다른 방법으로 해보겠다.


void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(home: FirstPage());
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text('first page'),
            const SizedBox(
              height: 30.0,
            ),
            OutlinedButton(
              onPressed: () {
                Navigator.push(
                    context,
                    MaterialPageRoute(
                      builder: (context) => const SecondPage(),
                    ));
              },
              child: const Text('go to second page'),
            )
          ],
        ),
      ),
    );
  }
}

MaterialApp 의 home으로 FirstPage 를 생성했다.
이렇게 되면, FirstPage의 MaterialPageRout에서 사용된 context는 FirstPage의 context이다. FirstPage는 MaterialApp의 하위에 있으므로 Navigator에 접근할 수 있다.



출처
Flutter/Navigator
Flutter/Snackbar


0개의 댓글