You are on page 1of 35

08

사용자 인터페이스 아키텍처


08-1 화면을 구성하는 위젯
08-2 위젯 트리 알아보기
08-3 정적인 화면 만들기
08-4 동적인 화면 만들기
08-5 상태의 생명주기
08-6 BuildContext 객체와 위젯 키
08-1 화면을 구성하는 위젯
 위젯 widget 은 화면에 보일 뷰 view 를 설명하는 객체
 화면과 관련된 모든 것이 위젯
 runApp() 함수는 전달받은 위젯을 위젯 트리 widget tree* 의 루트로 만듭니다 .

HTML 은 모든 것이 Element 인 것 처럼
Flutter 에서는 모든 것이 Widget 이다 .
08-1 화면을 구성하는 위젯
vsCode 로 입력 실습

몇 개의 위젯이 있는가 ?
08-1 화면을 구성하는 위젯
 MaterialApp: 머티리얼 디자인 적용
 Scaffold: 화면 구조 설계
 AppBar: 화면 위쪽 앱바 구성
 Text: 앱바의 제목
 Center: 가운데 정렬
 GestureDetector: 사용자 이벤트 처리
 Text: 본문에 문자열 출력

 플러터의 위젯은 모두 Widget 의 자식 클래스


 Object → DiagnosticableTree → Widget
 Object → DiagnosticableTree → Widget → StatelessWidget → Text
 Object → DiagnosticableTree → Widget → RenderObjectWidget → SingleChildRenderObjectWidget → Align →
Center
08-1 화면을 구성하는 위젯
 선언형 프로그래밍으로 화면을 구성한다
 선언형은 화면 구성 정보만 작성
 프레임워크가 알아서 API 를 이용해 화면을 출력

 위젯은 불변이다
 객체를 생성한 후 상태를 바꿀 수 없습니다 .
 화면을 새 데이터로 갱신하려면 새로운 위젯 객체를 만들어야 합니다 .
08-2 위젯 트리 알아보기
위젯의 트리 구조
 화면은 위젯을 여러 개 조합해서 구성
08-2 위젯 트리 알아보기
화면을 구성하는 3 개의 트리 구조
 위젯 트리 , 엘리먼트 트리 element tree 와 렌더 트리 render tree
 엘리먼트 트리는 ComponentElement 와 RenderObjectElement 객체로 구성 Widget Tree 는 HTML
 Component Element 객체는 트리 구조에서 다른 객체를 포함하는 역할 의 무엇과 비슷한가 ?
 실제 화면에 출력할 정보는 RenderObjectElement
08-2 위젯 트리 알아보기
화면을 구성하는 3 개의 트리 구조

 렌더 트리는 실제 화면에 출력할 정보를 가지는 RenderObjectElement 에 해당하는 객체로만 구성


정리
 Dirty?
 Flutter 은 초당 60~120 fps
 컴퓨터에서 1/60 초는 아주 긴 시간
 1/60 초 동안 여러 개의 화면 변경이 발생되면 ?
 버퍼와 유사하게 모아 놓고 있다가 한번 씩 갱신
 핵심은 element tree
 widget tree node  element tree node  rendering tree node
 Stateful widget 에 변경이 발생되는 시나리오
1. element tree 는 새로운 state 를 생성하고 widget tree elt 를 변경
 관련되어 변경되어야 하면 이것도 처리
2. 그것에 맞추어 rendering tree 도 조정
 Re-rendering 해야 할 widget 도 설정
 최적화 알고리즘을 돌리고 Make dirty
3. 1/60 초가 지나면 dirty 인 경우 rendering tree 만을 참고하여 re-rendering !
08-3 정적인 화면 만들기
 위젯은 다음 3 가지 클래스 가운데 하나를 상속받아 작성
4. Provider
 StatelessWidget: 상태를 관리하지 않는 정적인 위젯
구글에서 이런 Inherited
 StatefulWidget: 상태를 관리하는 동적인 위젯
위젯을 만들긴 했지만 사실 맘에
 InheritedWidget: 여러 위젯에서 공통으로 이용할 상태 관리 위젯 안들었는지 Provider 라는
패키지를 쓰라고 권장 합니다 .
 정적인 화면을 만들 때는 StatelessWidget 을 상속받는 클래스를 선언
고로 여러분이 이 글에서
얻어가실거는 Inherited Wid-
get 이 왜 탄생했냐 정도만
아시면 됩니다 .

허무하죠 ?
정적인 화면 만들기

샘플 코드 실행하는 방법

flutter create app1


find source code
Copy to lib/main.dart
08-4 동적인 화면 만들기
Vscode 에서 code snippet 사용방법
 StatefulWidget 은 상태를 유지하는 위젯
 상태란 화면에서 갱신해야 하는 데이터를 의미
 StatefulWidget 을 상속받은 클래스와 State 를 상속받은 클래스가 필요
 StatefulWidget: 위젯 클래스
 State: StatefulWidget 의 상탯값을 유지하고 화면을 구성하는 클래스
08-4 동적인 화면 만들기
상탯값 변경하기
 State 에 선언한 변숫값을 단순히 변경하는 것만으로 화면을 다시 빌드하지는 않습니다 .

State 클래스에 선언된 속성은


state 의 역할을 수행
08-4 동적인 화면 만들기
상탯값 변경하기
 setState() 함수는 State 클래스에서 사용할 수 있으며
화면을 다시 빌드
 setState() 함수를 호출하면 화면을 구성하는 build()
함수가 다시 호출되고 그 결과로 반환된 위젯으로 화면을
갱신
08-4 동적인 화면 만들기
상태 클래스의 역할 알아보기

MyParentStatefulWidget 는 인터페이스 역할을 하고


 객체 생성시 인자를 받는 역할
_MyParentStatefulWidgetState 는 Rendering 역할을 수행
 widget.arg1 형태로 MyParentStatefulWidget 의 속성을 접근
08-4 동적인 화면 만들기
상태 클래스의 역할 알아보기

 플러터에서 위젯은 불변이므로 StatelessWidget 이든 StatefulWidget 이든 화면을 다시 빌드하면 이전 객체를 다시


이용하는 것이 아니라 새로운 객체가 생성
 StatefulWidget 은
 데이터를 유지하면서 다양한 업무를 처리하고 그 결과로 화면을 갱신
 위젯 트리 구조에 포함해 매번 생성되게 만들고 , 실제 데이터와 업무 로직은 State 객체를 따로 두어 화면이
다시 빌드될 때마다 매번 생성되지 않게 합니다 .
08-4 동적인 화면 만들기 x
상태 클래스의 역할 알아보기
동적인 화면 만들기

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {


const MyApp({super.key});

@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Stateless Test'),
),
body: const MyStatefulWidget()));
}
}

class MyStatefulWidget extends StatefulWidget {


const MyStatefulWidget({super.key});

@override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}

class _MyStatefulWidgetState extends State<MyStatefulWidget> {

8.3 으로 8.4 를 만드는 방법


bool enabled = false;
String stateText = "disable";

void changeCheck() {
setState(() {
if (enabled) {
stateText = "disable";
enabled = false;
} else {
stateText = "enable";
enabled = true;
}
});
}

@override
Widget build(BuildContext context) {
return Center(

1. 8.3 을 복사해서 실행
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
IconButton(
icon: (enabled
? const Icon(
Icons.check_box,
size: 20,
)
: const Icon(
Icons.check_box_outline_blank,
size: 20,
)),
color: Colors.red,
onPressed: changeCheck,
),
Container(
padding: const EdgeInsets.only(left: 16),
child: Text(

2. 수정
stateText,
style: const TextStyle(fontSize: 30, fontWeight: FontWeight.bold),
),
),
],
),
);
}
}

- Snippet 으로 stateful 을 생성
- 변경되는 속성 및 메소드를 이동
- 메소드를 setState 로 감싼다 .
08-5 상태의 생명주기 매우 중요 !!

 Clean 은 State 에 의해 화면이 출력되고 있는


정상 상태이며 , Dirty 는 State 화면을 다시 빌드해야
하는 상태

부모에
의해
다시
그려질때
08-5 상태의 생명주기
 initState() 함수 호출 시점
 initState() 함수는 State 객체가 생성되자마자 가장 먼저 최초에 한 번 호출
 When the Stateful widget is created, this function is called only once and used to initialize the state.
 didChangeDependencies() 함수 호출 시점
 didChangeDependencies() 함수는 initState() 함수가 호출된 후에 이어서 호출
 상위 위젯의 상태 데이터가 변경될 때 하위 위젯의 didChangeDependencies() 가 자동으로 호출되어
이 함수에서 상위 위젯의 변경된 상태 데이터를 이용 x
 if you use InheritedWidget or Provider, when the InheritedWidget or Provider value is changed,
 this function is called again.
 Dependency 라는 것은 Provider 에 대한 의존을 의미
 didUpdateWidget() 함수 호출 시점
 State 에서는 자신과 연결된 StatefulWidget 이 다시 생성되는 순간을 감지 x
 부모 위젯이 변경되어 다시 그려져야 하는 경우 0
08-5 상태의 생명주기 deactivate()
• The deactivate function is called when the
build() 함수 호출 시점 state object is removed on the tree.
• Sometimes, Flutter deletes the state ob-
ject and add it again.
 최초 호출
• For example, when you move the widget
 setState() 함수에 의해 호출 to the some other place of the widget tree
 didUpdateWidget() 함수에 의해 호출 by GlobalKey, so the deactivate function is
executed when the state object is re-
dispose() 함수 호출 시점 moved.

 dispose() 함수는 상태 객체를 소멸할 때 자동으로 호출


08-6 BuildContext 객체
위젯 정보를 나타내는 BuildContext 객체
 BuildContext 객체는 StatelessWidget 의 build() 함수 매개변수나 State 의 build() 함수 매개변수
 BuildContext 에는 위젯을 이용할 때 필요한 다양한 정보

"A handle to the location of a widget in the widget tree."


08-6 위젯 키 ( 요약 정리 )
위젯을 식별하는 키

 모든 위젯은 키 값을 가질 수 있습니다 .

 StatelessWidget 일 때 식별하기 — 키 미사용


 StatelessWidget 은 상태를 표현할 수 없으며
화면에 표시할 데이터를 위젯이 직접 가지고 있으므로 키로 식별하지 않아도
 객체를 이용하는 데 문제가 없습니다 .
 같은 타입의 StatefulWidget 객체를 여러 개 이용한다면
 위젯의 구조가 변경될 때 바라지 않는 상황이 발생할 수도 있습니다 .

• 추천 방법 :
그냥 신경쓰지 말고 코딩할 것
Quick fix 에서 제안하는 대로 key 를 추가 할 것
VS Code 에서는 Stateless Widget 도 무조건 key 를 설정하는 것을 추천
다음 코드를 분석하고 이번 장을 종료
 이후 설명은 key 을 제거하면 동작이 제대로 되지 않음을 보여준다 .

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {


@override
Widget build(BuildContext context) {
return MaterialApp(home: MyListWidget());
}
}

class MyListWidget extends StatefulWidget {

class _MyListWidgetState extends State<MyListWidget> {


@override
State<StatefulWidget> createState() {
return _MyListWidgetState();
}
}

class _MyListWidgetState extends State<MyListWidget> {

List<Widget> widgetList = [
List<Widget> widgetList = [
MyColorItemWidget(
Colors.red,
key: UniqueKey(),
),
MyColorItemWidget(Colors.blue, key: UniqueKey()),
];

MyColorItemWidget(
onChange() {
print(widgetList.elementAt(0).key);
setState(() {
widgetList.insert(1, widgetList.removeAt(0));
});
}

Colors.red,
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Key Test'),
),
body: Column(children: [

key: UniqueKey(),
Row(
children: widgetList,
),
ElevatedButton(onPressed: onChange, child: Text("toggle"))
]));
}
}

),
class MyColorItemWidget extends StatefulWidget {
Color color;
MyColorItemWidget(this.color, {Key? key}) : super(key: key);
@override
State<StatefulWidget> createState() {
return _MyColorItemWidgetState(color);

MyColorItemWidget(Colors.blue, key: UniqueKey()),


}
}

class _MyColorItemWidgetState extends State<MyColorItemWidget> {


Color color;
_MyColorItemWidgetState(this.color);
@override
Widget build(BuildContext context) {
return Expanded(
child: Container(
color: color,
width: 150,
height: 150,
));
}
}
08-6 BuildContext 객체와 위젯 키
08-6 BuildContext 객체와 위젯 키
08-6 BuildContext 객체와 위젯 키
 다른 타입의 StatefulWidget 식별하기 — 키 미사용
08-6 BuildContext 객체와 위젯 키
 다른 타입의 StatefulWidget 식별하기 — 키 미사용
08-6 BuildContext 객체와 위젯 키
 같은 타입의 StatefulWidget 식별하기 — 키 사용
 같은 타입의 StatefulWidget 객체를 여러 개 이용한다면
 위젯의 구조가 변경될 때 바라지 않는 상황이 발생할 수도 있습니다 .
08-6 BuildContext 객체와 위젯 키
08-6 BuildContext 객체와 위젯 키
 같은 타입의 StatefulWidget 식별하기 — 키 사용
08-6 BuildContext 객체와 위젯 키
키 클래스
 GlobalKey 는 앱 전체에서 유일한 값
 GlobalKey 는 currentState, currentWidget 속성을 제공
 LocalKey 는 이 키 값이 지정된 위젯의 부모부터 자식 위젯에서 유일한 값
 LocalKey 하위 클래스로 ValueKey, UniqueKey, ObjectKey 가 있고 주로 이 클래스를 이용해 위젯의 키 값을 지정

 ValueKey: 문자열 , 숫자 키값
 ObjectKey: 객체 키값
 UniqueKey: 유일한 난수 키값
08-6 BuildContext 객체와 위젯 키
키 클래스
위젯 키 활용하기
감사합니다
단단히 마음먹고 떠난 사람은
산꼭대기에 도착할 수 있다 .
산은 올라가는 사람에게만 정복된다 .

윌리엄 셰익스피어
William Shakespeare

You might also like