본 문서는 Fluter Example의 내용을 원저작자의 동의하에 번역한것 입니다.
원 저작자 Eric Windmill에게 감사를 전합니다.
이해하는데 불필요한 문장은 과감하게 버렸습니다. 오 번역에 대해서 의견 주시면 적극 반영 하겠습니다.

BuildContext Class

모든 플러터 위젯은 BuildContext 인수(매개변수)가 포함된 @override build() 메소드를 가지고 있다.

1
2
3
4
5
class CartItemWidget extends StatelessWidget {

@override
Widget build(BuildContext context) {
// ...

why BuildContext

BuildContext를 간단히 설명하자면 다음과 같다:

  • 위젯tree에서 위젯의 위치.
  • 중첩되어 감싸진 위젯의 위젯. <div <div> .html>와 같은…
  • qt와 비슷한 부모 객체들(parent objects in qt and alike) (역자주: 여기서 말하는 qt가 뭔지 모르겠음.)
  • 플러터에선 최종 build.call() 까지 모든게 위젯임.
  • 마지막으로 위젯이 "stuff"를 리턴 할때 까지의 행(row) 차원(dimentions).

이해 해야할 중요한 개념은:

  1. 모든 위젯은 고유의 build() 메소드와 context를 가지고 있다.
  2. BuildContextbuild() 메소드에 의해 리턴된 위젯의 부모다.

즉, 위젯의 build() 메소드를 호출하는 위젯의BuildContextbuild()를 통해 리턴되는 위젯의 BuildContext는 동일하지 않다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class _MyHomePageState extends State<MyHomePage> {
_MyHomePageState() {
print(context.hashCode);
// prints 2011
}

@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: new Container(),
floatingActionButton:
new FloatingActionButton(onPressed: () => print(context.hashCode)),
// prints 63
);
}
}

(역자주 : 위 코드에서 두개의 print 메소드 실행시 전달된 BuildContext의 hashCode값이 다르다는것은 서로 다른 객체라는걸 알수 있다.)

그래서 뭘 얘기 하려는 거요?
이거 큰거 하나 잡았다!(큰거 하나 알게되었다는 뜻인듯…) ;-)

  • 잘못된 build()를 참조하기 쉽다.
  • 그리고 그것은 context. (역자주: 잘못된 build()의 리턴값인 BuildContext를 참조하여 문제를 일으 킬수 있다정도로 이해됨 )
  • 이것은 특히 of() 메소드를 사용할때 예기치 않은 상황이 발생 할 수 있다.

The ‘of()’ Method

플러터에선 모든게 위젯이므로,(역자주: 이제 집겨다!!) 어떤 경우 다른 위젯을 참조하기 위해 위젯 트리를 위/아래로 훑기도 한다. 이것은 일부 기능에선 필수이다.

특히 상속(inherited)된 위젯의 상태를 사용하려는 위젯은 상속한 위젯을 참조 할 수 있어야 한다. 이것은 일반적으로 of() 메소드의 형식으로 제공된다.

예 :

1
2
3
4
5
6
@override
Widget build(context) {
return new Text('Hello, World',
style: new TextStyle(color: Theme.of(context).primaryColor),
);
}

of() 메소드는 내부적으로 Theme 유형의 다음 상위 위젯에 대한 트리를 찾고 기본 색상 속성을 가져온다. 프레임워크는 이 build context와 관련한 트리를 알고 있으므로 올바른 Theme개체를 찾을 수 있다.

The Gotcha

플러터는 scaffold(비계)를 통해 우리에게 다음과 같은 문제를 해결 할 수 있는 좋은 방법을 제공한다.

snackbar와 같은 일부 위젯을 만들때, 가장 근접한 Scafold 컨텍스트를 얻어서 플러터가 snackbar를 그리는 방법을 알게 해야 한다. Scafold는 실제 우리가 snackbar를 디스플레이 할 수 있는 위젲이기 때문이다.

아래 코드는 작동하지 않는다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: new Container(),
/// 이 컨텍스트는 Scaffold가 없음
/// 왜냐면 빌드로 전달된 컨텍스트는 현재 트리의 상위 위젯이고
/// 상위 위젯에에는 Scaffold가 없기 때문임.
///
/// 이것은 에러를 던질것이다:
/// 'Scaffold.of() 호출시 전달된 context에는 Scaffold가 포함되어 있지 않기 때문에'
floatingActionButton: new FloatingActionButton(onPressed: () {
Scaffold.of(context).showSnackBar(
new SnackBar(
content: new Text('SnackBar'),
),
);
}));
}

Builder Methods

Builderclosure를 사용하여 하위 위젯을 작성하는 위젯이다. laymans(역자주: In laymans - 평신도 라는 단어인데 어떻게 해석할지 모르겠음)에서는 build 메소드로 리턴되는 자식들에게 직접 컨텍스트를 전달하는데 사용 할 수 있다.

위의 예제를 사용하면(역자주: 위에 오류난 코드를 사용):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: new Container(),
/// Builders를 사용하면 현재 build메소드에서 컨텍스트를 전달할 수 있다.
/// 이 build 메소드에서 리턴되는 하위 위젯에 직접 전달.
// 'builder' 속성은 모든 위젯에서 build메소드로 정확하게 처리 할 수 있는 callback를 허용.
floatingActionButton: new Builder(builder: (context) {
return new FloatingActionButton(onPressed: () {
Scaffold.of(context).showSnackBar(
new SnackBar(
backgroundColor: Colors.blue,
content: new Text('SnackBar'),
),
);
});
}),
);
}

tip: 여러분은 단순히 build메소드를 작게 만들고 더 상위 위젯에서 Scaffold를 리턴하여 이 문제를 해결 할 수도 있다. 의심 스러운 경우 더 작은 리턴 method를 고수하라!