Flutter-basic

11538 단어 flutterflutter

Widget

모든 것은 Widget이다. Widget은 UI를 구성하는 가장 기본적인 단위이다. Flutter는 Widget으로 시작하여, Widget으로 끝난다고 해도 과언이 아니다.

Rendering

Flutter는 Widget 구성으로 생성된 Layer tree를, Skia라는 그래픽 라이브러리를 통해 랜더링한다.

Android, IOS, Chrome, Firefox 등 다양한 환경에서 공통 API를 통해 랜더링 할 수 있는 2D 오픈 소스 라이브러리이며,

Stateless / Stateful Widget

Flutter의 Widget은, Stateless / Stateful Widget을 상속받아 만들어지며, 내부 Build 메서드를 통하여 Layer Tree를 만든다.

Stateful : Widget의 상태를 저장하여 지속적으로 추적 / 보존, State의 변화에 따라 상호작용하는 동적인 Widget
Stateless : 어떠한 상호작용도 없이 정적인 Widget

Stateless Widget은 단 한번의 Build 과정으로 화면이 계속 유지되며, Stateful Widget은 state의 변화(setState)가 발생할 때 마다 Build 과정이 일어나, 동적 화면을 구현할 수 있게 한다.

BuildContext

모든 flutter app은, main() 함수 내부에서 runApp() 메소드를 통해 Root Widget을 만드는 것으로 시작된다.

void main() {
	runApp(MyApp( // Root Widget, Origin of Widget
    	app Router: //...
        ));
}

BuildContext 사이의 관계

모든 Widget은 build 메소드를 통해 형성되며, 메소드 내부에서 BuildContext 클래스를 인가받는다.

  • build() 메소드 내에서 기본적으로 생성되는 context는, 모두 같은 BuildContext인 것인가?
    Widget 내부에서 build() 메소드를 작성한다고 생각해 보자. 이 메소드에서 작성된 Buildcontext는, 단순히 함수 정의에서 받는 context 인자를 만들어 둔 것이지, 모두 같은 context가 아니다.
    즉, 호출될 때 어떤 context를 넘겨주는가에 따라 달라지는 것이다.
void func1(int value){}
void func2(int value){}
//value at func1 != value at func2
  • BuildContext instance를 넘겨주지 않고 생성되는 Widget은, context를 사용하지 않고 build되는 것인가?
    모든 Widget은, build() 메소드를 사용하여 만들어 진다. 하지만 Text(),MaterialApp(),MaterialButton()... 와 같은 Widget은, 별도로 build() 메소드를 작성하지 않고 만들 수 있는데, 이 또한 내부적으로 build() 메소드를 호출함으로서 Widget이 생성된다.

하지만, 위와 같은 Widget은 내부적으로 익명의 context를 갖기 때문에, 해당 widget에서 갖는 Child widget의 context 정보는 자신의 widget context가 아닌, 자기 자신의 부모의 context 정보를 넘겨주게 되는 것이다.

이 때, 만약 Child widget이 자기 자신의 context 정보를 갖게 하기 위해선, Builder widget을 별도로 사용하여, 자신과 자기 자식의 widget 사이에 별도의 widget을 생성하게 하여, 그 context 정보를 넘겨줘야 한다. (아래 Widget tree 예시에서 참고)

  • build()되는 Child Widget에서의 BuildContext는 부모의 것과 관련이 있는 것인가?
    특정 Widget에서 사용되는 BuildContext는, 직접적으로 연관된 부모만을 참조한다(Bottom-up relationship). 이 사항이 중요한데, 한 Widget은, 부모만을 알고 있기 때문에, Widget의 탐색은, 항상 부모 방향의 단방향으로만 진행된다.

Widget tree

Widget은 tree 계층 구조로 이루어져 있으며 Widget은 부모 / 자식을 가질 수 있다. (Parent widget = Widget container)

각 Widget은 어떻게 자기가 어느 위치에 있는지 알 수 있을까?

Widget은 내부 Build 메서드를 가지고 있으며, 이 때 BuildContext를 인자로 넘겨받는다. 이 BuildContext는 상호 Widget간의 위치정보를 담고 있으며, Navigator ,MediaQuery, ListView 등 다양한 builder등에서 사용된다.

완전하게 자세하게는 코드분석을 할 수 없지만, 공식 Flutter 유투브에서 설명하는 바는 아래와 같다.

Widget은, Widget 클래스를 상속받거나, implement 받는데, Widget 클래스는 내부에 createElement() 함수를 실행한다. 이 때 각 상호 Widget간의 관계정보가 정의된 Element를 반환하게 되는것이다. Element는 BuildContext 타입을 상속받는 클래스로, 즉 BuildContext가 생성된다고 할 수 있다.

더 알아봐야 하겠지만, 특정 Widget에서 받는 BuildContext 인자는 자기 자신의 context 정보가 아닌, 부모 Widget의 Context를 받는 것 같다. 따라서 각 Widget의 context는, 수직적 tree 구조의 위치(부모 / 자식) 구조는 알 수 있으나, 수평적 구조(형제)의 위치는 알 수 없다.

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

  
  Widget build(BuildContext MyPagecontext) {
    return Scaffold( //Anonymous context
      appBar: AppBar(
        title: Text('Snack Bar'),
        centerTitle: true,
      ),
      body: Builder(builder: (BuildContext Buildercontext) {
        return Center(
          child: TextButton(
            child: Text(
              'Show me',
              style: TextStyle(color: Colors.white),
            ),
            style: ButtonStyle(
              backgroundColor: MaterialStateProperty.all(Colors.red),
            ),
            onPressed: () {
              Scaffold.of(Buildercontext).showSnackBar(
              SnackBar(content: Text('Hello')));
            },
          ),
        );
      }),
    );
  }
}

위 예시는, Mypage 자식으로 Scaffold widget이 있는 간단한 예시이다. Scaffold 내부에선, Snackbar 기능을 활용하기 위해 flutter 2.0 이전 버젼에서 지원하는(deprecated now) Scaffold.of().showSnackBar() 메소드를 사용한다.Something.of(context)는, 인가된 context 부모를 따라 Something 객체를 탐색한다.

Mypage build 메소드 인자인 contextMypagecontext이기 때문에 자식 widget인 Scaffold의 정보를 알지 못하며, Scaffold.of().showSnackBar()에 사용되는 contextScaffold 자식 widget의 context여야만 한다.

이를 위해, Scaffold body에 Builder widget을 활용하여, 새로운 Buildercontext인 Buildercontext를 정의, 이는 Scaffold 하위 Builder의 context 정보를 갖게 만들기 때문에 이를 활용하여, Scaffold widget을 탐색할 수 있다.

Widget의 생명주기

1. Constructor

Widget의 인스턴스를 생성할 때 인가받을 변수등을 정의할 수 있는 생성자.

2. InitState()

Widget이 생성될 때, 상태의 초기화를 위해 한번만 호출되는 메소드.

3. didChangeDependencies

InitState와 동일하게 최초 생성될 때 한번 호출 됨. InitState와는 다르게, Context에 접근할 수 있다.(InitState 단계에서는 아직 context가 생성되지 않음.) 따라서 대부분 Provider, MediaQuery등을 사용할 때 호출함.

4. build()

화면에 표시 될 위젯등을 반환하며, 상태가 변화할 때 마다(setState()) 다시 호출되게 된다.
따라서 build 내부에 쓸모 없는 연산 로직이 들어가계 될 경우, 앱의 부하가 상당히 증가할 수 있음을 알아야 함.

5. didUpdateWidget

부모 위젯에 의해 rebuild 되어야 할 필요가 있을 때, build() 메소드 직전에 호출된다. 보통 부모 위젯의 변경으로 인해 애니메이션을 다시 실행할 필요가 있을 때 자주 사용되며, 이전 상태를 가지고 있는 oldWidget을 사용할 수 있다.

6. deactivate, dispose

상태가 제거 되었을 때 deactivate가 호출되며, 위젯이 widget tree에서 제거되었을 경우 dispose가 호출된다.

출처

  1. Flutter 간단 정리하기

좋은 웹페이지 즐겨찾기