본문으로 바로가기

BuildContext는 위젯 트리에서 현재 위젯의 위치를 알 수 있는 정보이다.

 

다음은 플러터 프로젝트를 실행하면 자동으로 작성되어 있는 코드인데, BuildContext가 있음을 확인할 수 있다.

Dart 문법에 의하면, build 함수는 Widget을 리턴하며 context라는 변수는 BuildContext 타입임을 알 수 있다.

 

int plus (int a int b) {
  return a+b
}
class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(),
    );
  }
}

 

BuildContext는 stateless 위젯이나 state 빌드 메서드에 의해서 리턴 된 위젯의 부모가 된다. 아래 코드의 경우 리턴한 Scaffold가 context를 물려 받게 되는 것이다.

 

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("text"),
        centerTitle: true,
        elevation: 0,
        actions: <Widget>[
          IconButton(
              icon: Icon(Icons.shopping_cart),
              onPressed: () {
                print("shopping!");
              }),

 

 

이 context를 어떻게 활용하는지를 잘 보여줄 수 있는 snackbar를 구현함으로써 context를 좀 더 잘 이해해보자.

 

 

Display a snackbar

How to implement a snackbar to display messages.

flutter.dev

 

클릭하면 SnackBar를 호출하도록 플랫 버튼 하나를 만들었다. 

Scaffold.of(context).showSnackBar(snackBar);

Something.of(context) => 현재 주어진 context를 타고 올라가면서 주어진 Something을 반환하라.

Scaffold.of(context) => 현재 주어진 context를 타고 올라가면서 주어진 Scaffold를 반환하라.

 

여기서는 SnackBarTest부터 시작해서 위로 타고 올라가며 Scaffold를 찾을 것이다. 그러나 SnackBarTest 상위 위젯에는 Scaffold가 없고, SnackBarTest 아래에 리턴값으로 Scaffold가 있을 뿐이다. 

(쉽게 말하면, Scaffold 내부에 body에 showSnackBar를 주었기 때문에, 상위로 인식되지 않는 것입니다)

 

Scaffold를 찾을 수 없으므로 결국 Scaffold.of() called with a context that does not contain a Scaffold. 오류를 받게 됩니다.

 

무슨 컨텍스트를 사용했는지는 디버그 콘솔에 뜹니다. 여기서는 The context used was: SnackBarTest라고 뜨네요.

... 중략

class SnackBarTest extends StatelessWidget {
  final snackBar = SnackBar(content: Text('Yay! A SnackBar!'));

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("SnackBar"),
          centerTitle: true,
        ),
        body: Center(
          child: FlatButton(
            onPressed: () {
              Scaffold.of(context).showSnackBar(snackBar);
            },
            child: Text("show me"),
            color: Colors.red,
          ),
        ));
  }
}

 

 

이를 해결하는 방법은 Builder를 새로 끼워넣어 위젯 트리 상 Scaffold를 상위에 존재하는 것으로 만들어야 합니다. 현재 body의 값으로 준 Center()를 Builder()로 감싼 후 기존의 context와 구별하기 위해 다른 이름을 줍니다. 여기서는 ctx라고 주었습니다. 

 

그 다음 본래 SnackBar가 참조하면 context를 ctx로 바꿔주어 Builder를 거쳐가도록 하면, Scaffold를 찾게 됩니다.

 

이제 Scaffold 상에서 스낵바가 뜨는 것을 볼 수 있습니다.

 

import 'package:flutter/material.dart';
import 'MyHomePage.dart';

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(
          primarySwatch: Colors.blue,
          visualDensity: VisualDensity.adaptivePlatformDensity,
        ),
        home: SnackBarTest()
        // home: MyHomePage(),
        );
  }
}

class SnackBarTest extends StatelessWidget {
  final snackBar = SnackBar(content: Text('Yay! A SnackBar!'));

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("SnackBar"),
          centerTitle: true,
        ),
        body: Builder(
          builder: (BuildContext ctx) {
            return Center(
              child: FlatButton(
                onPressed: () {
                  Scaffold.of(ctx).showSnackBar(snackBar);
                },
                child: Text("show me"),
                color: Colors.red,
              ),
            );
          },
        ));
  }
}

 

 


 

그런데, 애초에 구성을 잘해서, Scaffold를 상위 위젯으로 만들어 Builder를 사용하지 않고도 스낵바를 구현하는 것이 더 쉬운 방법인 것 같습니다.

 

위와 달리 애초에 Scaffold를 return한 곳에 바로 showSnackBar를 하는 것이 아니라 클래스를 분리하여 한 레이어를 거쳐가도록 코딩했습니다. 따라서 아래 코드는 Builder 없이도 SnackBar가 정상적으로 작동합니다.

 

import 'package:flutter/material.dart';
import 'MyHomePage.dart';

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(
          primarySwatch: Colors.blue,
          visualDensity: VisualDensity.adaptivePlatformDensity,
        ),
        home: Home()
        // home: MyHomePage(),
        );
  }
}

class Home extends StatelessWidget {
  final snackBar = SnackBar(content: Text('Yay! A SnackBar!'));

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("SnackBar"),
          centerTitle: true,
        ),
        body: MySnackBar());
  }
}

class MySnackBar extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: RaisedButton(
          child: Text("show me!"),
          onPressed: () {
            Scaffold.of(context).showSnackBar(SnackBar(
              content: Text("what"),
            ));
          }),
    );
  }
}

darren, dev blog
블로그 이미지 DarrenKwonDev 님의 블로그
VISITOR 오늘 / 전체