Widget
- Widget 은 모두 ‘불변’ 의 법칙을 가지고 있다(immutable)
- 하지만 위젯의 값을 변경해야할 때가 있다. (색 변경 등)
- 변경이 필요하면 기존 위젯을 삭제하고, 새로운 위젯으로 대체한다.
- 단순 컨테이너 색깔을 파란색에서 빨간색으로 변경한다고해서 기존 위젯에 색깔만 변경하는게 아니라, 새로 생성하여 완전히 새로운 위젯으로 대체되는 것이다.
StatelessWidget 라이프 사이클 (Life Cycle)
- Constructor로 생성이 되고, 생성이 되자마자 build 함수가 실행된다.
- 마찬가지로 변경(색 변경 등)이 필요하면 완전히 새로운 위젯을 만들어버린다.
- 하나의 StatelessWidget 은 라이프 사이클동안 단 한번만 build 함수를 실행한다.
기본 StatefulWidget 라이프 사이클(Life Cycle)
- StatefulWidget 은 class 가 두개로 구성되어있다.
- StatefulWidget , State
- StatefulWidget 또한 위젯이기때문에 build() 는 한번만 실행되어야하므로, 법칙을 따를 필요가 없는 State 를 사용하여 stateful widget 을 구성한다.
[위젯이 생성되고 삭제될때의 생명주기]
- StatefulWidget 클래스에서 생성자 호출 (Constructor)
- createState() 함수 호출 :: State를 생성함
- initState() 호출 :: State 초기화 (init 은 생성될 때 한번만 호출됨)
- didChangeDependencies() 함수 호출
- 상태가 ‘dirty’ 상태가 됨 (무엇인가 변경되어 빌드되기 전 상태)
- ‘dirty’ 상태가 되면 build() 함수가 호출됨 :: 위젯을 화면에다 그려주는 등의 태스크를 진행
- ‘clean’ 상태로 변경 됨
- 위젯 삭제 시그널이 오면, deactivate() 호출 (많이 사용하지 않음)
- dispose () 호출
[파라미터가 바뀌었을때 생명 주기]
StatelessWidget 에서는 파라미터가 변경(컨테이너 색깔 변경 등)되면 기존 위젯을 삭제하고 새로 위젯을 생성했지만, statefulWidget 은 그러지 않는다. 빨간색에서 파란색으로 변경한다고 가정하자.
- 파란색 컨테이너 위젯 생성 (생성자)
- 기존 StatefulWidget(빨간 컨테이너) 이 가지고 있던 State를 찾아서 사용 (createState 호출하지 않음)
- clean 상태에서 didUpdateWidget() 호출 // 파라미터에 파란색
- dirty 상태로 변경됨 (무엇인가 변경되어 빌드되기 전 상태)
- 변경된 값들을 기반으로 build() 재실행
[setState를 실행했을때 생명주기]
가장 많이 사용되는 라이프 사이클임
- clean 상태에서 setState 실행()
- State 내부에서 setState() 를 실행하여 build를 진행함
- dirty 상태로 변경됨 (무엇인가 변경되어 빌드되기 전 상태)
- build() 실행됨
- 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, ); } }
[위젯 생성시]
- flutter: Widget Constructor 실행
- flutter: create State 실행
- flutter: initState 실행
- flutter: didChangeDependencies 실행
- flutter: build 실행!
[위젯 삭제시]
- flutter: deactivate 실행 // state 도 사라짐
- flutter: dispose 실행 // state 도 사라짐
[위젯 색상 변경시]
- flutter: Widget Constructor 실행
- flutter: didUpdateWidget 실행 // state는 사라지지 않으므로 다시 create state 하지 않음
- 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()만 호출됨.