Flutter 일기 52번째.
플러터 코드에서 BuildContext는
BuildContext context
요런 형태로 자주 보인다. 그런데 BuildContext가 뭘까..?
Navigator.of(context)
MediaQuery.of(context)
Navigator 사용할 때 항상 써서 궁금했는데, 일기 쓰기를 오래 쉬는 바람에 쓰질 못했다. flutter.dev페이지와 유튜버 코딩셰프님의 강의를 참고했다. 코딩셰프님 BuildContext강의는 2개!
우선 flutter.dev의 설명에 따르면,
위젯트리 내에서 위젯의 위치를 다루는 정보 - 정도가 되겠다.
print(context.widget);
-> context 있는 곳에서 해보면 위젯 이름이 뜬다.
위젯트리는 위 그림과 같은, 플러터 코드의 구조를 말한다. 플러터는 위젯을 층층이 쌓아 화면에 나타내는 구조이다.(이건 flutter create하면 기본으로 나오는 코드의 위젯 트리이다.)
플러터 아이콘이 왼쪽에 그려진 MyApp, MaterialApp...같은 아이들이 전부 위젯이다. 그리고 위젯트리를 보면 build함수로 위젯을 리턴하는 것을 알 수 있다. 옆에 보면 이런 것도 있죠.
(BuildContext context...
그럼 이제 위젯 빌드할 때 BuildContext가 쓰인단 건 알 수 있겠다. 공식 문서의 내용을 좀 더 읽어보자. 일부 문장을 떼어 왔다.
1. BuildContext objects are passed to WidgetBuilder functions (such as StatelessWidget.build).
-> BuildContext객체는 WidgetBuilder 함수(StatelessWidget의 build 같은 거)로 전달된다.
(아래 코드처럼 build함수는 인자로 BuildContext를 받는다.)
build(BuildContext context) {
return Scaffold(...
Widget
2. Each widget has its own BuildContext, which becomes the parent of the widget returned by the StatelessWidget.build or State.build function.
-> 각 위젯은 자신만의 BuildContext를 갖고 있는데, 이 BuildContext는 StatelessWidget.build 또는 State.build 함수(StatefulWidget의 build 함수)에 의해 리턴되는 위젯의 부모가 된다.
3. In particular, this means that within a build method, the build context of the widget of the build method is not the same as the build context of the widgets returned by that build method.
-> 2번 문단이 뭔 말이냐면, build함수를 갖고 있는 위젯의 build context는, 이 위젯의 build함수로 리턴한 위젯의 build context와 다르다는 말.
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
Widget build(BuildContext context) {
return Scaffold(
build함수를 갖고 있는 MyHomePage의 build context는, 리턴된 Scaffold위젯의 build context와는 다르다!
즉, Widget build(BuildContext context) <- 여기의 context는 아래 리턴되는 Scaffold의 context와는 달라요...!
^^ 뭐래는지 대충 알겠지만 어렵다
flutter.dev - material-scaffold-of method
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
Widget build(BuildContext context) {
// here, Scaffold.of(context) returns null -> 왜일까?
return Scaffold(
appBar: AppBar(title: Text('Demo')),
// 여기 Builder는 왜 있을까?
body: Builder(
builder: (BuildContext context) {
return TextButton(
child: const Text('BUTTON'),
onPressed: () {
Scaffold.of(context).showBottomSheet<void>(
(BuildContext context) {
return Container(
alignment: Alignment.center,
height: 200,
color: Colors.amber,
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const Text('BottomSheet'),
ElevatedButton(
child: const Text('Close BottomSheet'),
onPressed: () {
Navigator.pop(context);
...괄호가 너무 많아 생략
}
위 코드를 실행한건데, Scaffold.of(context).showBottomSheet 가 있는 TextButton을 왜 Builder로 감싸주었을까?
이렇게 Builder위젯을 벗겨내고 실행하면 에러가 나기 때문.
Scaffold.of 메소드는 context를 이용해 가장 가까운 ScaffoldState 를 찾는데, ScaffoldState란 Scaffold의 State(상태)를 말한다.
No Scaffold ancestor...! return Scaffold가 있는데도 왜 못찾을까?
Scaffold를 리턴하는 build함수에 인자로 전달되는 context는 Scaffold의 것이 아니기 때문이야... Builder를 이용해 Scaffold의 context를 뱉어줄, Scaffold 아래에 위치하는 새 BuildContext를 만들어서 써야 한다. 그래서 예제 코드에는 Builder 위젯으로 감싸놓은 것! Builder.builder에서 context를 쓰기 때문이다.
body: Builder(
builder: (BuildContext context) {
print로 한번 찍어볼까?
역시나 다른 아이였다.
Scaffold.of(context)에서 쓰인 context는
MyHomePage의 build함수에 인자로 들어간 context가 아니라,
Builder.builder에 전달된 context라는 것을 알 수 있다.
즉, Scaffold의 context는 Builder.builder에 전달된 context...!
코딩셰프님 강의에서는 현재 주어진 context에서 위젯 트리를 거슬러 올라가면서 scaffold state를 찾는다고 했으니, scaffold아래에 있는 context만 있으면 되는 것 같다.공식문서에도 "under" the Scaffold라고 나와있으니..!
아니다. flutter.dev - material-scaffold-of method에서는 다른 방식을 추천함.
// void main 생략
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(
title: Text('Demo'),
),
body: MyScaffoldBody(),
),
);
}
}
class MyScaffoldBody extends StatelessWidget {
const MyScaffoldBody({super.key});
Widget build(BuildContext context) {
return Builder(
builder: (BuildContext context) {
return TextButton(
child: const Text('BUTTON'),
onPressed: () {
Scaffold.of(context).showBottomSheet<void>(
(BuildContext context) {
return Container(
alignment: Alignment.center,
height: 200,
color: Colors.amber,
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const Text('BottomSheet'),
ElevatedButton(
child: const Text('Close BottomSheet'),
onPressed: () {
Navigator.pop(context);
... 괄호 전부 생략
}
이렇게 Scaffold랑 Scaffold.of(context)쓰는 부분을 똑 떼주면 됨. Builder를 쓰는 건 Scaffold 안에서 바로 Scaffold.of(context)를 써야할 때! 이다.
flutter.dev 에서는 그냥 똑 떼는 게 효율적이라고 하네.
일기 쓰는데 이 context가 쟤 건지 얘 건지 헷갈려서 미칠 뻔 했다.
코딩셰프님 유튭 강의 덕분에 핵심을 확실히 알 수 있어서 감사했다!
내 오후를 다 쓴 일기... 여기까지