본문 바로가기
App/Flutter

Flutter BuildContext에 대해 알아보기

by Day0404 2021. 7. 10.
728x90
반응형

출처 : https://flutter-ko.dev/

Flutter에서 BuildContext

Flutter로 프로젝트를 진행하거나 공부하다 보면 BuildContext라는 것이 궁금하게 될 겁니다.

저도 Flutter를 공부하면서 궁금해서 BuildContext에 대해 알아봤습니다.

 

"Scaffold.of() called with a context that does not contain a Scaffold"라는 생소한 에러 메시지를 받는 경우가 종종 있음

 

첫 번째 정의

Flutter 공식 홈페이지에서는

"A handle to the locatin of a widget in the widget tree."

widget tree에서 현재 widget의 위치를 알 수 있는 정보라고 합니다.

 

Flutter에서 모든 widget은 build method를 가지고 있습니다.

class MyPage extends StatelessWidget {
	@override
    Widget build(BuildContext context) {
    	return Scaffold(
        	// TODO
        )
    }
}

build method가 Scaffold Widget을 리턴할 때 위젯 트리상에서 어디에 위치하는가에 대한 정보를 가지고 있는 context라는 것을 넣어서 리턴해준다는 의미입니다.

 

두 번째 정의

"Each widget has its own BuildContext, which becomes the parent of the widget returned by the StatelessWidget. build or State.buil function"

"이 BuildContext는 stateless 위젯이나 state 빌드 메서드에 의해서 리턴된 위젯의 부모가 된다."

 

class MyPage extends StatelessWidget {
	@override
    Widget build(BuildContext context) {
    	return Scaffold(
        	// TODO
        )
    }
}

MyPage라는 커스텀 위젯도 자신만의 BuildContext를 가지고 있습니다.

build 메서드를 통해서 Scaffold 위젯이 리턴, 부모인 MyPage의 BuildContext를 그대로 물려받습니다.

 

위젯 트리상에서 어떤 필요에 의해 Scaffold 위젯의 위치가 필요하다는 가정하에 Scaffold 위젯의 Context를 참조하면 

"Scaffold.of() called with a context that does not contain a Scaffold" 에러가 발생하게 됩니다.

Scaffold 위젯이 존재하고 있으니 당연히 현재 Scaffold 위젯이 위젯 트리상에서 어디에 위치하고 있는지 알기 위해서 Scaffold 위젯이 가지고 있는 context를 참조하는 것이 상식적으로 마땅하지만 정작 Scaffold 위젯의 context는 위젯 트리 내에서 Scaffold 위젯이 어디에 위치하는지 정보를 전혀 가지고 있지 않기 때문입니다.

 

MyPage widget build method에 의해 리턴된 Scaffold Widget는 Build Context 타입의 context를 그대로 물려받게 된다고 했으니 Scaffold 위젯 밑에서 build 메소드로 무언가 위젯을 리턴하면 그 위젯은 부모인 Scaffold 위젯의 진짜 context를 가진 위젯이 됩니다.

 

위 내용을 더 자세히 알아보기 위해 .of() 메서드를 사용하여 알아보겠습니다.

 

Snack bar를 통한 Build Context

 

Scaffold.of(context) method

of() 에 대한 해석

"현재 주어진 context에서 위로 올라가면서 가장 가까운 Scaffold를 찾아서 반환하라"

Something.of(context)

위로 거슬러 올라가면서 가장 가까운 Something을 찾아 반환하라 라고 이해하면 된다.

Theme.of(context)

위로 거슬러 올라가면서 가장 가까운 Theme를 찾아 반환하라 라고 생각

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Appbar',
      theme: ThemeData(primarySwatch: Colors.red),
      home: MyPage(),
    );
  }
}

class MyPage extends StatelessWidget {
  const MyPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Snack Bar'),
        centerTitle: true,
      ),
      body: Center(
        child: FlatButton(
          child: Text(
            'Snack Bar Test',
            style: TextStyle(
              color: Colors.white
            ),
          ),
          color: Colors.red,
          onPressed: () {
          	Scaffold.of(context).showSnackBar(SnackBar(content: Text('Hello World'),));
          },
        ),
      ),
    );
  }
}

위 와 같이 코딩을 하고 실행을 해보면 아래와 같은 화면이 나옵니다.

해당 Snack Bar Test 버튼을 클릭해보면

 

위 와 같이 Console창에 에러 메시지를 확인할 수 있습니다.

Console창을 조금 더 내려보면 더 자세한 에러 메시지를 확인할 수 있습니다.

사용된 context는 MyPage위젯의 것임을 알 수 있습니다.

 

build 메서드를 통해 인자 값으로 불러오는 BuildContext는 리턴되는 Widget의 것이 아닌 이 함수를 불러오는 Widget의 

BuildContext입니다.

Scaffold.of(context)를 통해 Scaffold의 위치를 찾아가려고 했으나 of() 메서드가 사용한 BuildContext는 MyPage의 것이기 때문에 오류가 발생한 것입니다.

 

이제 이 오류를 해결하기 위해 Builder Widget을 사용해보겠습니다.

 

Builder Widget

지금까지 사용했던 context가 무엇이었든 간에 무시하고 새로운 context로 새로운 위젯을 만든다고 생각하면 됩니다.

 

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Appbar',
      theme: ThemeData(primarySwatch: Colors.red),
      home: MyPage(),
    );
  }
}

class MyPage extends StatelessWidget {
  const MyPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text('Snack Bar'),
          centerTitle: true,
        ),
        body: Builder(
          builder: (BuildContext ctx) {
            return Center(
              child: FlatButton(
                child: Text(
                  'Show me',
                  style: TextStyle(color: Colors.white),
                ),
                color: Colors.red,
                onPressed: () {
                  Scaffold.of(ctx).showSnackBar(SnackBar(
                    content: Text('Hello'),
                  ));
                },
              ),
            );
          },
        ));
  }
}

Builder Widget을 통해 위 코드를 작성하게 되면 아까와 같은 화면을 볼 수 있습니다.

이번엔 Snack Bar Test 버튼을 클릭하면 정상적으로 작동하는 것을 확인할 수 있습니다.

 

이렇게 하면 Builder Widget을 통해 더 이상 MyPage의 BuildContext를 찾아 위로 올라가면서 Scaffold를 찾는 게 아닌 Builder Widget 위에서 찾아 올라가니까 Scaffold 위젯을 찾을 수 있어서 정상적으로 스낵바가 생성됩니다.

 

 

반응형

댓글