본문 바로가기

개발(합니다)/Flutter&android&ios

[flutter-07] buildContext와 SnackBar

반응형

buildContext란

공식 문서에는 2가지로 설명하고 있습니다.

  1. flutter 공식 문서에서는 widget tree에서 현재 widget의 위치를 알 수 있는 정보라고 설명합니다.

flutter는 함수를 항상 가지고 이를 가지고 widget이라는 계층 구조를 만들어갑니다.

// 함수의 기본 형태
int addNumber(int a, int b) {
    return a + b;
}

class MyClass extends StatelessWidget {


class MyCard extends StatelessWidget {
  @override
  Widget build(BuildContext context) { // 빌드 메서드에 들어오는 BuildContext 라는 인자 값 대입한 Scaffold라는 위젯을 리턴한다.
    return Scaffold(
      appBar: AppBar(
        title: Text("BBANTO"),
        centerTitle: true,
        backgroundColor: Colors.redAccent,
        elevation: 0.0,
      ),
      body:
      // Padding(
      //     padding: EdgeInsets.fromLTRB(
      //       // insets : 무언가를 삽입하다, 끼워넣다.
      //       // LTRB : left, top, right, bottom
      //         30.0, 40.0, 0.0, 0.0
      //     ),
      //   child:
        Center( // 가로축 정렬
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center , // 세로축 정렬
            children: [
              Text("Test"),
              Text("Test"),
              Text("Test"),
              Text("Test"),
            ],
          // ),
      ),
        ),
    );
  }
}

아래 그림처럼 build 함수는 Scaffold 함수를 리턴할 때 Widget Tree상에서 어디에 위치하는지에 대한 정보를 context라는 변수를 넣어서 리턴해준다는 의미를 가집니다.

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

위 소스에서 Scaffold는 MyCard의 build(BuildContext context) 를 그대로 물려 받게 됩니다.

Scaffold 위젯의 context를 참조하게 되면 "Scaffold.of() aclled with a context that does not contain a Scaffold" 에러를 볼 수 있습니다.
Scaffold 위젯이 존재하지만 context를 참조 할 수 없는 이유는 context는 실제로 Scaffold가 어디에 위치해 있는지 알 수 없어서 발생합니다.

해결 방법

부모의 Context를 그대로 물러 받게 되므로 Scaffold 여기 build를 통해 리턴하게 되는 Widget은 Scaffold의 Context를 물러 받게 되므로 Scaffold의 위치 정보를 알 수 있게 됩니다.

SnackBar Class 공식 문서에서 SnackBar 사용법

  • Scallfold.of(context).showSnackBar
  • 현재 주어진 context에서 위로 올라가면서 가장 가까운 Scaffold를 찾아서 반환하라. 라는 내용으로 return 한 Scaffold가 있을 때까지 부모를 찾으라는 내용입니다.
  • Somthing.of(context)는 위로 거슬러 올라가면서 Somthing을 찾으라는 내용입니다.
  • ex) Theme.of(context) : 가장 가까운 Theme을 찾아서 반환하라는 내용입니다.
  • BuildContext와 context의 차이는 BuildContext는 클래스이고 context는 인스턴스입니다.
class Person {
    String name;
    int age;
}

void main() {
    Person p = new Person(); // BuildContext는 Person과 같은 클래스이고 context는 p 와 같은 인스턴스입니다.
} 

import 'package:flutter/material.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',
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Snap Bar"),
        centerTitle: true,
      ),
      body: Center(
        child: FlatButton(
          child: Text(
            "show me",
            style: TextStyle(color: Colors.white),
          ),
          color: Colors.red,
          onPressed: () => {
            Scaffold.of(context).showSnackBar(SnackBar(
              content: Text("Hellow"),
            ))
          },
        ), // Raised button, Floating action button 디자인과 모양이 다르고 기능은 동일함
      ),
    );
  }
}

위 소스를 실행하면 아무런 반응이 없고 디버그창을 보면 오류를 볼 수 있습니다.

위에서 설명했던 Scffold를 찾으려고 했으나 찾을 수 없어서 생긴 오류입니다.

왜냐하면 context는 리턴되는 위젯이 아니라 함수를 불러오는 클래스를 가리킵니다.
MyHomePage부터 위로 찾게되면서 Scaffold가 없다고 하는 에러를 표출합니다.

해결 방법

새로운 context를 생성하여 새로운 위치에서부터 위로 찾는 기능으로 Builder 위젯을 사용합니다.

Builder 위젯의 역할은 기존 context는 무시하고 새로운 context를 생성하는 위젯으로 builder부터 위로 찾습니다.

import 'package:flutter/material.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',
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("Snap Bar"),
          centerTitle: true,
        ),
        body: Builder(
          builder: (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("Hellow"),
                  ))
                },
              ), // Raised button, Floating action button 디자인과 모양이 다르고 기능은 동일함
            );
          },
        ));
  }
}
반응형