플러터의 BuildContext

윤뿔소·2023년 6월 1일
0

Dart / Flutter

목록 보기
16/18

그동안 플러터로 개발해오면서 BuildContext를 애써 무시하며 Stateless, State 등을 렌더링 해왔다.

class App extends StatefulWidget {
  const App({super.key});

  
  State<App> createState() => _AppState();
}

class _AppState extends State<App> {
  
  // 여기!
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: Scaffold(
      ...

위 부분 중 BuildContext가 무엇인지 알아볼 것이다.

Theme

그동안 우리가 각 제목과 본문 등의 Text에 직접 스타일을 넣어서 꾸며줬다. 사실 그러한 하드코딩은 플러터를 제대로 이용하지 않는 식이었다. MaterialApp 위젯의 theme 이용해서 색과 글씨 크기 등을 재사용 가능하도록 편하게 설정 할 수 있다.

class _AppState extends State<App> {
  
  Widget build(BuildContext context) {
    return MaterialApp(
      // 여기 theme!
      theme: ThemeData(
        textTheme: const TextTheme(
          titleLarge: TextStyle(
            color: Colors.red,
          ),
        ),
      ),
      home: const Scaffold(
        backgroundColor: Color(0xFFF4EDDB),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              MyLargeTitle(),
            ],
          ),
        ),
      ),
    );
  }
}

class MyLargeTitle extends StatelessWidget {
  const MyLargeTitle({
    super.key,
  });

  
  Widget build(BuildContext context) {
    return const Text(
      'My Large Title',
      style: TextStyle(fontSize: 30),
    );
  }
}

위 코드는 My Large Title Text를 꾸며주기 위해 하드코딩했지만

    ...
    return MaterialApp(
      // 여기 theme!
      theme: ThemeData(
        textTheme: const TextTheme(
          titleLarge: TextStyle(
            color: Colors.red,
          ),
        ),
      ),
    ...

위 부분을 추가해서 우리 TitletitleLarge를 적용토록 할 것이다.

BuildContext

여기서 BuildContext가 나온다. 위 코드를 보면은 MyLargeTitle라고, 커스텀 위젯을 만들어서 분리가 돼 _AppState의 자식으로 속해져버렸다.

근데 _AppState에서 theme이 설정되지 않았는가? 이 theme은 자식에게서 바로 쓸 수가 없다. 왜냐면 분리된 상태니까!

리액트 유저라면 분리된 컴포넌트에서 그에 관한 로직을 짜지 않고서는 서로의 선언된 변수를 사용할 수 없다고 이해하면 된다.

그걸 가능케 하는 것이 바로 BuildContext다.

위젯 트리

구동 원리를 보기 전 위젯 트리를 이해하고 넘어가는 것이 중요하다.

참고 : 위젯트리는 어떻게 그려질까?

위 사진을 보면 Container가 루트 위젯이자 제일 부모 위젯이고, 그 자식 Row, Row의 자식이자 형제인 Image, Text 등으로 트리가 이루어 진다.

내가 썼던 코드에도 이런 식의 트리가 형성돼 있다.


위젯 트리의 이해와 Theme을 선언한 게 무슨 이유?

바로! 부모인 App 위젯에서 Theme을 선언했지만 자식 위젯이자 커스텀 위젯인 MyLargeTitle 위젯이 그 Theme을 접근하기 위해서는 BuildContext의 설정이 필요한 것이다!!

왜냐하면 위젯들이 분리돼있기에 선언된 인스턴스나 변수 등을 사용하지 못하기에 그 자식들이 부모의 설정, 테마 등을 접근하고 확인하기 위해 쓰는 것이다!

마치 리액트의 진화된 Props와 전역 상태의 조합 같달까..

요약

  • BuildContext는 플러터 프레임워크에서 사용되는 개념으로, 위젯 트리에서 현재 위젯의 위치와 상태에 대한 정보를 제공.
  • BuildContext는 현재 위젯의 렌더링 및 업데이트와 관련된 작업을 수행하기 위해 사용.
  • BuildContext는 위젯 트리의 각 노드에 대해 생성되고, 해당 노드의 위젯에 대한 정보를 담고 있음.
  • 위젯은 부모 위젯으로부터 BuildContext를 전달받고, 필요한 경우 하위 위젯에게도 BuildContext를 전달하여 정보를 공유.
  • BuildContext는 위젯 트리의 계층 구조를 따라 단방향인 상위에서 하위로 전달, 따라서 정확한 BuildContext를 사용하는 것이 중요.
    이를 통해 찾고픈 위젯을 찾고, 필요한 상태 및 동작에 접근 가능.

예시

아까 썼던 코드를 이어 써보자.

... // App

class MyLargeTitle extends StatelessWidget {
  const MyLargeTitle({
    super.key,
  });

  
  Widget build(BuildContext context) {
    return const Text(
      'My Large Title',
      style: TextStyle(fontSize: 30, color: Theme.of(context)),
    );
  }
}

Theme.of로 선택하고 탐색할 수 있다. 자동완성을 따라가보면 color: Theme.of(context)),이 됐고, 그 사이 context가 중요한 포인트다.

우리 '테마'(theme) '의'(of) 어떠한 것을 '위젯 트리'(context)로부터 원하는 것을 가져와야한다.

그래서 context뒤에 우리가 원하는 설정을 가져와보자.

class MyLargeTitle extends StatelessWidget {
  const MyLargeTitle({
    super.key,
  });

  
  Widget build(BuildContext context) {
    return Text(
      'My Large Title',
      style: TextStyle(
        fontSize: 30,
        // 여기 !
        color: Theme.of(context).textTheme.titleLarge!.color,
      ),
    );
  }
}

짜잔 됐다! 여기서 !를 붙인 이유는 다트는 null safe 언어기 때문에 선언이 됐는지 안됐는지 확인이 되지 읺아서 오류가 뜬다. !를 붙여서 값이 존재한다고 확실히 얘기해주자.

결과

아주 잘된 모습이다.

정리하자면 Props이자 전역 변수같은 BuildContext가 항상 선언이 돼서 부모의 설정, 테마 등의 context를 자식이 사용할 수 있다! 라는 말이다.

이만큼 BuildContext를 잘 이해하고 활용한다면 아주 강력한 기능이 될 것이다.

참고: BuildContext를 사용한 또다른 예시 코드

  1. Theme
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Theme Example',
      theme: ThemeData(
        primaryColor: Colors.blue, // 기본 테마의 주요 색상 설정
        accentColor: Colors.red, // 기본 테마의 강조 색상 설정
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Theme Example'),
      ),
      body: Center(
        child: Text(
          'Hello, World!',
          style: Theme.of(context).textTheme.headline4,
        ),
      ),
    );
  }
}
  1. _showAlertDialog를 인자로 context에 전달
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Context Example',
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  void _showAlertDialog(BuildContext context) {
    showDialog(
      context: context,
      builder: (BuildContext context) {
        return AlertDialog(
          title: Text('AlertDialog'),
          content: Text('This is an example AlertDialog.'),
          actions: [
            TextButton(
              onPressed: () {
                Navigator.of(context).pop();
              },
              child: Text('OK'),
            ),
          ],
        );
      },
    );
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Context Example'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            _showAlertDialog(context); // BuildContext를 인자로 전달
          },
          child: Text('Show AlertDialog'),
        ),
      ),
    );
  }
}
profile
코뿔소처럼 저돌적으로

5개의 댓글

comment-user-thumbnail
2023년 6월 3일

context가 들어가면 비슷한 개념들이 많네요 ㅋㅋ 고생하셨습니다

답글 달기
comment-user-thumbnail
2023년 6월 4일

오호 ㅎㅎ 뭔가 플러터를 모르지만 이렇게 보는게 재밌네욤 저도 얼른 ... !

답글 달기
comment-user-thumbnail
2023년 6월 4일

플러터에도 context 개념이 있군욤 잘 읽었습니다!ㅎㅎㅎ

답글 달기
comment-user-thumbnail
2023년 6월 4일

BuildContext 참 중요한 개념인 것 같아요!! 정리 잘 해주셔서 한번더 복습하고 갑니당

답글 달기
comment-user-thumbnail
2023년 6월 4일

context가 있군용 잘 봤습니다

답글 달기