stateless widget, stateful widget 에서 build 메서드를 호출할 때 BuildContext를 인자로 받는다. 이 BuildContext는 어떤 역할을 하는 것일까?
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를 탐색하면 ... 발견!!!
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에 접근할 수 있다.