Flutter – StatefulWidget

Widget

  • Widget 은 모두 ‘불변’ 의 법칙을 가지고 있다(immutable)
  • 하지만 위젯의 값을 변경해야할 때가 있다. (색 변경 등)
  • 변경이 필요하면 기존 위젯을 삭제하고, 새로운 위젯으로 대체한다.
    • 단순 컨테이너 색깔을 파란색에서 빨간색으로 변경한다고해서 기존 위젯에 색깔만 변경하는게 아니라, 새로 생성하여 완전히 새로운 위젯으로 대체되는 것이다.

StatelessWidget 라이프 사이클 (Life Cycle)

  • Constructor로 생성이 되고, 생성이 되자마자 build 함수가 실행된다.
  • 마찬가지로 변경(색 변경 등)이 필요하면 완전히 새로운 위젯을 만들어버린다.
  • 하나의 StatelessWidget 은 라이프 사이클동안 단 한번만 build 함수를 실행한다.

기본 StatefulWidget 라이프 사이클(Life Cycle)

  • StatefulWidget 은 class 가 두개로 구성되어있다.
    • StatefulWidget , State
    • StatefulWidget 또한 위젯이기때문에 build() 는 한번만 실행되어야하므로, 법칙을 따를 필요가 없는 State 를 사용하여 stateful widget 을 구성한다.

[위젯이 생성되고 삭제될때의 생명주기]

  1. StatefulWidget 클래스에서 생성자 호출 (Constructor)
  2. createState() 함수 호출 :: State를 생성함
  3. initState() 호출 :: State 초기화 (init 은 생성될 때 한번만 호출됨)
  4. didChangeDependencies() 함수 호출
  5. 상태가 ‘dirty’ 상태가 됨 (무엇인가 변경되어 빌드되기 전 상태)
  6. ‘dirty’ 상태가 되면 build() 함수가 호출됨 :: 위젯을 화면에다 그려주는 등의 태스크를 진행
  7. ‘clean’ 상태로 변경 됨
  8. 위젯 삭제 시그널이 오면, deactivate() 호출 (많이 사용하지 않음)
  9. dispose () 호출

[파라미터가 바뀌었을때 생명 주기]

StatelessWidget 에서는 파라미터가 변경(컨테이너 색깔 변경 등)되면 기존 위젯을 삭제하고 새로 위젯을 생성했지만, statefulWidget 은 그러지 않는다. 빨간색에서 파란색으로 변경한다고 가정하자.

  1. 파란색 컨테이너 위젯 생성 (생성자)
  2. 기존 StatefulWidget(빨간 컨테이너) 이 가지고 있던 State를 찾아서 사용 (createState 호출하지 않음)
  3. clean 상태에서 didUpdateWidget() 호출 // 파라미터에 파란색
  4. dirty 상태로 변경됨 (무엇인가 변경되어 빌드되기 전 상태)
  5. 변경된 값들을 기반으로 build() 재실행

[setState를 실행했을때 생명주기]

가장 많이 사용되는 라이프 사이클임

  1. clean 상태에서 setState 실행()
    • State 내부에서 setState() 를 실행하여 build를 진행함
  2. dirty 상태로 변경됨 (무엇인가 변경되어 빌드되기 전 상태)
  3. build() 실행됨
  4. clean 상태로 변경됨

import 'package:flutter/material.dart';

class HomeScreen extends StatefulWidget { // stateful widget의 widget
  final Color color;

  const HomeScreen({
    required this.color,
    Key? key,
  }) : super(key: key);

  @override
  State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> { // stateful widget의 state
  @override
  Widget build(BuildContext context) {
    return Container(
      width: 50.0,
      height: 50.0,
      color: widget.color,
    );
  }
}

class _HomeScreen extends StatelessWidget { // stateless widget
  final Color color;

  const _HomeScreen({
    required this.color,
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      width: 50.0,
      height: 50.0,
      color: color,
    );
  }
}

코드에서 보이다시피 State를 상속하는 _HomeScreenState 내에 build() 가 있는 것을 볼 수 있다. 이때 color 가 변경될때마다 build() 를 실행하여 clean 상태로 만든다. State에서 변경된 color를 가져오는건 State를 상속했다면 widget 을 통해 쉽게 가져올 수 있다. (widget.color)

stateless widget 인 _HomeScreen 도 동일한 동작을 하지만, 위젯이 삭제되었다 추가 되는 것이다.

그럼 실제로 Stateful 위젯에서 어떤 순서로 호출하는지 직접 메서드를 오버라이딩해서 살펴보자

import 'package:flutter/material.dart';

class HomeScreen extends StatefulWidget {
  final Color color;

  HomeScreen({
    required this.color,
    Key? key,
  }) : super(key: key) {
    print('Widget Constructor 실행');
  }

  @override
  State<HomeScreen> createState() {
    print('create State 실행');
    return _HomeScreenState();
  }
}

class _HomeScreenState extends State<HomeScreen> {
  @override
  void initState() {
    super.initState();

    print('initState 실행');
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();

    print('didChangeDependencies 실행');
  }

  @override
  void deactivate() {
    print('deactivate 실행');
    super.deactivate();
  }

  @override
  void dispose() {
    print('dispose 실행');
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    print('build 실행!');
    return Container(
      width: 50.0,
      height: 50.0,
      color: widget.color,
    );
  }
}

class _HomeScreen extends StatelessWidget {
  final Color color;

  const _HomeScreen({
    required this.color,
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      width: 50.0,
      height: 50.0,
      color: color,
    );
  }
}

[위젯 생성시]

  1. flutter: Widget Constructor 실행
  2. flutter: create State 실행
  3. flutter: initState 실행
  4. flutter: didChangeDependencies 실행
  5. flutter: build 실행!

[위젯 삭제시]

  1. flutter: deactivate 실행 // state 도 사라짐
  2. flutter: dispose 실행 // state 도 사라짐

[위젯 색상 변경시]

  1. flutter: Widget Constructor 실행
  2. flutter: didUpdateWidget 실행 // state는 사라지지 않으므로 다시 create state 하지 않음
  3. flutter: build 실행!

hot reload의 원리도 다시 build()를 실행해주는 것과 같다


GestureDetector

GestureDetector를 사용하면 여러 제스쳐(행동)을 감지하여 원하는 콜백함수를 실행시킬 수 있다. 위젯이 deactivate 되지않는한 State는 유지되므로 색상을 변경하여도 number 값은 유지되는 것을 알 수 있다.

@override
  Widget build(BuildContext context) {
    print('build 실행!');
    return GestureDetector(
      onTap: (){ // tap 했을때 콜백함수 실행
          setState(() { // State 변경
            number ++;
          });
      },
      child: Container(
        width: 50.0,
        height: 50.0,
        color: widget.color,
        child: Center(
          child: Text(
            number.toString(),
          ),
        ),
      ),
    );
  }

tap 시, build()만 호출됨.