서론
플러터 코드를 보면 늘 BuildContext를 사용하는데 그냥 있거니 하고 지나치다가 좀 제대로 정리하려고 한다.
BuildContext란?
BuildContext 정의
1) A handle to the location of a widget in the widget tree
widget tree에서 현재 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는 stateless위젯이나 state 빌드 메서드에 의해서 리턴 된 위젯의 부모가 된다.
1) widget tree에서 현재 widget의 위치를 알 수 있는 정보
- 모든 위젯은 build라는 함수를 가지고 있음 또한 이를 통해 계층 구조를 만들 수가 있다
- build함수의 리턴 타입은 Widget이고 매개변수 타입은 BuildContext이다
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
decoration: const BoxDecoration(
// context라는 BuildContext 인자값을 가진 Scaffold 위젯을 리턴한다.
- 해석 : build함수는 Container라는 위젯을 리턴하는데 이 때 해당 위젯이 위젯 트리에서 어디에 위치하는지에 대한 정보 BuildContext context 를 넣어서 리턴해준다.
- 각 위젯은 자기 자신만의 BuildContext를 가지고 있다
2) BuildContext는 stateless위젯이나 state 빌드 메서드에 의해서 리턴 된 위젯의 부모가 된다.
class SplashScreen extends StatelessWidget {
const SplashScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
- 커스텀 위젯(SplashScreen)도 자기 자신만의 BuildContext를 가지고 있다.
- build메서드를 통해서 Scaffold위젯이 리턴이 되고 Scaffoldd위젯은 부모의 SplashScreen의 context를 그대로 상속받음
Scaffold 위젯의 context를 참조할 때
위젯트리에서 Scaffold 위젯의 위치가 필요하다고 지금 Scaffold위젯의 context를 참조하면 에러가 뜸
- Scaffold.of() called with a context that does not contain a Scaffold
사실상 Scaffold가 존재하지만 해당 위치 정보 context는 부모의 SplashScreen의 BuildContext정보를 그대로 상속받기 때문에 Scaffold의 위치 정보가 나오지 않아서 에러가 나는 것
무슨 되돌이표 같긴한데 지금 Scaffold는 부모의 것을 물려받고 있으니까 Scaffold 밑에 위젯하나를 두면 이 위젯의 context는 Scaffold의 context를 물려받을 테니 이걸로 알 수 있지 않을까?
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
//of메서드를
print(Scaffold.of(context).hasAppBar);
},
style: ElevatedButton.styleFrom(
foregroundColor: Colors.white,
backgroundColor: Colors.red,
),
child: const Text('hello'),
)
],
),
],
),
),
);
- 버튼을 누르면 콜백함수가 호출되고 Scaffold의 위젯이 어디에 있는지 파악하기 위해서 context를 참조하고 해당 context를 사용하여 로직을 실행
- 에러 내용을 보면 해당 context는 Scaffold위젯의 정보를 포함하지 못했다.
- 지금 사용하는 context는 SplashScreen만의 정보를 담고 있고 Scaffold위젯의 정보를 담고 있지 않다.
- 위에서 설명한 거 같이 context는 부모의 context를 그대로 물려받아서 아직 scaffold의 위치 정보가 담겨 있지 않은 것이다.
Scaffold.of(context) method
-현재 주어진 context에서 위로 올라가면서 가장 가까운 Scaffold를 찾아서 반환해라
ex) MediaQuery.of(context).size 이것도 그냥 보면 해당 위치에서 가장 가까운 MediaQuery를 찾아서 반환 때린 다음에 해당 사이즈를 찾는 느낌인듯
Builder위젯을 사용하자
Builder위젯은 지금까지 사용한 context를 다 무시하고 새로운 context로 새로운 위젯을 만들자!
즉 Builder 밑에 있는 Scaffold.of(context)는 위로 올라가면서 Builder위젯에 새롭게 만들어진 context를 참조하여 Scaffold의 위치정보를 참조하게 된다.
class SplashScreen extends StatelessWidget {
const SplashScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Builder(builder: (BuildContext ctx) {
return Container(
decoration: const BoxDecoration(
//color: Colors.orange,
color: Color(0xFFF99231),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
// Builder위젯의 context를 참조해서 Scaffold 위젯 정보를 참조한다
print(Scaffold.of(ctx).hasAppBar);
},
style: ElevatedButton.styleFrom(
foregroundColor: Colors.white,
backgroundColor: Colors.red,
),
child: const Text('hello'),
)
],
),
],
),
);
}),
);
}
}
위의 소스에서 보다시피 Scaffold.of(ctx)는 저기 SplashScreen에 있는 context를 참조하는 것이 아니라 Builder가 새로 만든 context를 참조하여 로직을 실행하게 되었다.
위젯 트리 내에서 새로운 context를 참조할 경우
widget을 class로 정의할때는 build가 필수적으로 들어가서 context가 설정되지만
class로 정의하지않고 어느 위젯의 child에서 사용할경우는 따로 context설정을 위해서 builder를 사용한다.
참고
https://api.flutter.dev/flutter/widgets/BuildContext-class.html
https://api.flutter.dev/flutter/widgets/BuildContext-class.html
'프로그래밍 > 플러터' 카테고리의 다른 글
PageView와 Timer (1) | 2024.02.24 |
---|---|
WebView & WebViewController (0) | 2024.02.24 |
스플래시 스크린 앱 작성 (0) | 2024.02.21 |
플러터의 기본 위젯들 (0) | 2024.02.20 |
플러터 입문하기 (0) | 2024.02.19 |
하고 싶은 걸 하고 되고 싶은 사람이 되자!
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!