Widget

flutter에서 거의 모든 것이 위젯이며, 위젯은 뷰를 묘사하는 다트 클래스이다. 위젯은 요소를 화면에 표시할 때 플러터가 사용하는 청사진이다. 위젯 클래스는 플러터가 이해할 수 있는 뷰 모델이다. 위젯에는 컨트롤러나 뷰가 따로 없다.

위젯은 특정 UI를 정의하는 클래스다!

리액트나 다른 프레인워크 컴포넌트와 달리 위젯은 앱 뷰의 모든 사항을 정의할 수 있다. 예를 들어, css에서 원하는 컴포넌트에 패딩을 추가할 수 있다면 플러터에서는 스타일을 다른 위젯으로 설정한다. 심지어 앱의 경로도 위젯이다. build라는 메서드로 다른 위젯을 반환하는 커스텀 위젯을 정의한다.

위젯트리와 형식, State 객체

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp()); //앱의 진입점
}

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

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

   //슈퍼클래스의 메소드 createState를 오버라이드한다.
  State<MyHomePage> createState() => _MyHomePageState();
  //StatefulWidget은 State객체를 반환하는 createState를 반드시 정의해야 한다.
}

class _MyHomePageState extends State<MyHomePage> {
  //상태 클래스는 flutter의 State를 상속받는다.
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

위는 새 프로젝트를 실행하면 기본으로 구현되어 있는 카운트 앱의 위젯트리구조 이다.

플러터 라이브러리의 대부분의 위젯들은 StatelessWidget이나, StatefulWidget의 형식을 갖는다.
플러터 UI를 개발한다는 것은 여러 위젯들을 조합해 위젯 트리 를 만들겠다는 의미이다.
트리의 각 노드는 위젯이고 이 노드가 모여 트리가 된다.
build 메서드에서 위젯을 추가할 때마다 트리에 새 노드를 추가하는 것이고 각 노드는 부모 자식관계로 연결된다.

개미정도로 작은 꿀팁

st...라고 입력하는 것 만으로 간단하게 StatelessWidget을 구현할 수 있다.

심지어 multiple line edit도 지원한다!🤪개꿀이다.(멍꿀멍꿀)

StatelessWidget

  • 위젯 생명주기 동안 내부 상태를 갖지 않는다.
  • 설정이나 자신이 표시하는 데이터를 신경쓰지 않는다.
  • 부모 위젯이 설정을 전달하거나 위젯 내부에서 설정 정보를 전달 할 수 있지만, 자신의 설정을 바꿀수 없다.
  • 상태를 바꿀 수 없는 위젯이다.
  • 이 위젯을 아무 데이터도 책임지지 않는다.

StatefulWidget

  • 내부 상태를 가지며 이를 관리한다.
  • 상태 객체를 갖는다.
  • StatefulWidget과 State는 같은 개체라고 생각해도 무방하다.
  • 플러터가 위젯을 다시 그리는 동안에도 상태를 유지할 수 있다.
class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

   //슈퍼클래스의 메소드 createState를 오버라이드한다.
  State<MyHomePage> createState() => _MyHomePageState();
  //StatefulWidget은 State객체를 반환하는 createState를 반드시 정의해야 한다.
}

class _MyHomePageState extends State<MyHomePage> {
  //상태 클래스는 flutter의 State를 상속받는다.

  
  Widget build(BuildContext context) {//StatefulWidget의 필수 메소드이다.
    return Container();
  }
}

BuildContext

ThemeData에서 테마를 바꾸거나 타이틀을 변경하면 모든 자식 위젯을 변경 및 갱신한다. How is that possible?

위젯의 모든 build 메서드는 위젯트리에서 위젯의 위치를 참조하는 BuildContext 하나를 인수로 받는다. build는 프레임워크가 호출하므로 BuildContext를 개발자가 관리할 필요는 없지만 자주 이를 사용하게 된다.

모든 위젯은 자신만의 BuildContext를 가지며 한 위젯이 다양한 테마를 반환하게 만들어 한 트리에 여러 테마를 적용할 수 있다. 카운터 앱의 테마나 다른 of 메서드는 트리에서 형식이 같은 가장 가까운 부모를 반환한다.

예를 들어 Scaffold > Center > Column > Text:
context.ancestorWidgetOfExactType(Scaffold) => Text 컨텍스트에서 트리 구조로 올라가서 첫 번째 Scaffold를 반환한다.

상위 BuildContext에서 하위 위젯(= 하위) 위젯 을 찾는 것도 가능 하지만 그렇게 하지 않는 것이 좋다.

BuildContext는 특정 위젯을 정확하게 어떻게 표현할지 결정한다.
예를 들어 플러터는 BuildContext를 통해 모달(modal)과 라우트(route)를 표시한다. 모달을 만드는 메서드에 BuildContext를 전달한다.

BuildContext는 빌드된 모든 위젯의 트리 구조 내 위젯 위치에 대한 참조일 뿐이다.

State와 BuildContext관계

stateful 위젯의 경우 state는 buildContext와 연결된다. 이 연결은 영구적이며, State개체는 BuildContext를 변경하지 않는다. 트리구조에서 이동할 수 있는 경우에도 State는 해당 BuildContext와 연결된 상태로 유지된다.

State객체가 BuildContext와 연관되어 있기 때문에 State객체가 다른 BuildContext를 통해 직접 엑세스할 수 없다는 것을 의미한다.

Scaffold.of 의 주의점

Scaffold의 정의와 Scaffold.of를 사용하는 곳이 동일한 build 메소드에 있는 경우는 잘 찾을 수가 없게 된다.

그 이유는 of 메소드가 build를 호출하는 widget으로부터 조상을 찾아가기 때문이다. 그래서 Scaffold는 자식이기 때문에 찾지 못하는 것이다. 따라서 이런 경우는 Builder를 사용해서 찾는 것이 일반적으로 사용된다.

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

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('test')),
      body:  Builder(
        builder: (BuildContext context) {
          return Center(
            child: ElevatedButton(
              child: const Text('snackbar'),
              onPressed: () {
                Scaffold.of(context).showSnackBar(const SnackBar(
                  content: Text('안녕!'),
                ));
              },
            ),
          );
        }
      )
    );
  } 
}

생명 주기

  • stateless와 stateful 차이

    출처 : https://www.youtube.com/watch?v=8nZ7kRPsHSA

  • state의 생명주기

    다이어그램 오른쪽의 흐름동안 state개체의 내부 상태를 확인할 수 있다. 또한 mounted되어 사용가능한 순간을 볼 수 있다.

initState

  • State초기 함수로 초기 셋팅을 위한 함수이다.
    예를 들어, 스트림을 구독하거나 사람이 볼 수 있는 형식으로 데이터를 변환하는 함수를 이곳에서 호출한다.
  • 이 메서드를 재정의하는 경우 일반적으로 super.initState()메서드를 호출해야 한다.
  • 이 단계에서는 컨텍스트를 사용할 수 있지만 프레임워크가 아직 State와 State를 완전히 연관시키지 않았기 때문에 컨텍스트를 아직 실제로 사용할 수 없다.
  • 생성시에 1번 호출되며 rebuild를 하여도 호출되지 않는다. (수명동안 더이상 호출되지 않음)
  • Async await가 불가능하며 BuildContext에 접근이 불가능하다.

didChangeDependencies

  • initState와 똑같이 생성시 초기 1번만 호출된다.
    차이점은 BuildContext에 접근이 가능하다.
  • 위젯이 InheritedWidget에 연결되어 있거나 일부 리스너(BuildContext기반)를 초기화해야 하는 경우 재정의된다.
  • 이 메서드를 재정의하는 경우 일반적으로 super.didChangeDependencied()를 호출해야 한다.

setState

  • rebuild를 요청하는 함수이다. state의 값을 변경 할 수 있다.

didUpdateWidget

  • 부모 Widget에서 rebuild를 하여 현재 Widget이 다시 그려질때
  • 과거의 Widget과 현재 Widget을 비교하는 함수이다. State값을 비교하여 rebuild를 막거나 실행 시킬 수 있다.

build

  • 모든 위젯은 다른 위젯을 반환하는 build메소드를 반드시 포함해야 한다.
  • State 객체가 변경될 때마다 (혹은 InheritedWidget이 등록된 위젯을 알려야 할 때마다)호출된다.
  • 강제로 다시 빌드하려면 setState를 호출하면 된다.

dispose

  • initState와 쌍으로 쓰이며 Controller, Stream 등 메모리 누수가 발생 할 수 있는 것들을 해제해주는 영역이다.
  • 화면이 종료될때 호출된다.
  • 이 메서드를 재정의하는 경우 일반적으로 super.dispose()를 호출한다.

참고

이 포스트를 작성하기 위해 참고한 곳

https://medium.com/flutter-community/widget-state-buildcontext-inheritedwidget-898d671b7956

0개의 댓글