-
Flutter_16. Flutter Internals, Way of Render with Tree, Key, Mutating Values> Frontend/Flutter 2023. 5. 16. 21:13
0) 학습 내용.
- Flutter Render 와 Tree
- Render 와 Tree를 통해 Flutter예제
- Key
- Mutating Values ( final, const, var)
1) Flutter Render 와 Tree
- Widget Tree: Widget 군집체)
1. Widget Tree는 APP UI를 구성하는 위젯들의 계층 구조 2. Widget Tree는 APP의 화면 구조를 정의하고, 각 위젯이 어떻게 배치되고 상호작용하는지 결정. 3. Widget Treesms 불변성을 가지며, 상태가 변경되면, 새로운 위젯 트리가 생성 - Element Tree: (Widget Tree 의 사용 설명서)
1. Element Tree는 Widget Tree 기반으로 실제로 화면에 렌더링되는 요소를 나타냄. 2. Element는 Widget Tree의 각 노드에 대응하며, 화면에 렌더링되기 위한 정보를 지님. 3. Element Tree는 Widget Tree와 달리, 가변성을 가지며, 상태 변경에 따라 업데이트 됨 - Render Tree: (Element Tree 진화 군집체: Element Tree transformed into corressponding Render Obj which has responsibility of handling layout, painting and more..)
1. Render Tree는 Element Tree의 렌더링 정보를 나타냄. Element Tree 비교를 통해 업데이트 진행 2. Flutter 엔진은 Render Tree를 사용해 실제 화면에 위젯을 그림. 3. Render Tree는 화면에 표시되는 위젯의 위치, 크기 등 정보를 가지고, Element Tree 변경 사항을 반영하여 업데이트함. - Render Tree 작동 원리
더보기- Widget Tree: The process starts with the Widget Tree, which is a hierarchical structure of widgets that define the UI components and their relationships. Each widget in the tree corresponds to a specific part of the UI, such as buttons, text, images, or containers. The Widget Tree is constructed based on the widget composition defined in your Flutter code.
- Element Tree: Once the Widget Tree is constructed, Flutter creates an Element Tree based on it. Elements are lightweight representations of widgets and serve as an intermediate layer between the widgets and the Render Tree. Elements are responsible for handling updates and maintaining the state of the widgets.
- Building the Render Tree: The Element Tree is then used to build the Render Tree. During this process, each element in the Element Tree is recursively transformed into a corresponding RenderObject, which represents a low-level rendering abstraction. RenderObjects are responsible for handling layout, painting, and hit testing.
- Render Object and RenderBox: RenderObject is the base class for all objects in the Render Tree, but the most common type of RenderObject used is RenderBox. RenderBox is a rectangular box that represents a visual element on the screen. It defines properties like position, size, and appearance for a specific widget.
- Layout and Constraints: After the Render Tree is built, Flutter performs a layout phase. During layout, each RenderBox receives constraints from its parent, which determine its available space. The RenderBox then computes its size based on these constraints and the intrinsic size of its child boxes. This process continues recursively until all RenderBoxes have computed their sizes.
- Painting: Once the layout is complete, the rendering engine triggers the painting phase. The Render Tree is traversed, and each RenderObject is responsible for painting itself on the screen. This involves filling shapes, rendering images, applying text styles, and handling animations. The painting process occurs from the top to the bottom of the Render Tree.
- Render Layer and Compositing: To optimize performance, Flutter uses a technique called compositing. The Render Tree is divided into multiple layers based on transparency and clipping. Each layer represents a group of RenderObjects that can be efficiently rendered together. The compositor then composites these layers, blending them to create the final image displayed on the screen.
- Rebuilding the Render Tree: When changes occur in the Widget Tree, such as user interactions or state updates, Flutter rebuilds the affected parts of the Widget Tree. It creates a new Element Tree and subsequently updates the corresponding parts of the Render Tree to reflect the changes. This process is optimized through a mechanism called Dirty Marking and Reconciliation, which efficiently identifies and updates the modified elements in the Render Tree.
In summary, the Render Tree in Flutter is the internal representation of the UI, built based on the Widget Tree and Element Tree. It performs layout, painting, and compositing to efficiently render the user interface on the screen. By utilizing this structured and optimized approach, Flutter delivers high-performance and visually appealing applications.
- Re-Rendering 과정:
1. widget's state 가 변화될 때, Widget Tree의 불변성을 유지하면서, 새로운 위젯트리 생성,
그리고 새로운 Widget트리는 Element Tree로, Render Tree로 변환2. Element Tree의 변경사항을 반영하기 위해, Dirty Marking(변경된 요소 표시)& Reconciliation(변경된 요소 실제 업데이트) 수행.
변경된 부분만 업데이트 되기 때문에 효율적인 UI 업데이트 가능3. Reconciliation(변경된 요소 실제 업데이트) 이후, 변경된 요소들을 기반으로 Render Tree가 판단 후, 업데이트 되며, Render Tree 업데이트는 다양한 방식으로 수행되며, 변경된 Widget 의 위치, 크기, 스타일등이 반영되며, Render Tree를 기반으로 실제 화면에 위젯이 그려짐. 2) Render 와 Tree를 통해 Flutter예제
- import 'package:flutter/material.dart'; class UIUpdatesDemo extends StatefulWidget { const UIUpdatesDemo({super.key}); StatefulElement createElement() { print('UIUpdatesDemo CREATEELEMENT called'); return super.createElement(); } State<UIUpdatesDemo> createState() { return _UIUpdatesDemo(); } } class _UIUpdatesDemo extends State<UIUpdatesDemo> { var _isUnderstood = false; Widget build(BuildContext context) { print('UIUpdatesDemo BUILD called'); return Padding( padding: const EdgeInsets.all(8.0), child: Center( child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.center, children: [ const Text( 'Every Flutter developer should have a basic understanding of Flutter\'s internals!', textAlign: TextAlign.center, style: TextStyle(fontWeight: FontWeight.bold), ), const SizedBox(height: 16), const Text( 'Do you understand how Flutter updates UIs?', textAlign: TextAlign.center, ), const SizedBox(height: 24), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ TextButton( onPressed: () { setState(() { _isUnderstood = false; }); }, child: const Text('No'), ), TextButton( onPressed: () { setState(() { _isUnderstood = true; }); }, child: const Text('Yes'), ), ], ), if (_isUnderstood) const Text('Awesome!'), ], ), ), ); } }
- Stateful widget 이기 때문에, 변화가 감지되면, 계속 Build 가 새롭게 발생된다. => Widget 트리 계속 생성 => 리소스 낭비 !
- 실제로 위의 파일을 실행해보면, 화면의 변화가 있든 없는, 새로운 Widget 이 계속 생성된다.
리소스 낭비 ! - 만약 변경되는 부분을 Component로 빼고, 전체 Widget Tree가 변화되는게 아니라, 변경되는 부분만 감지하면 좋치 않을까?
import 'package:flutter/material.dart'; import 'package:flutter07_todo/demo_buttons.dart'; class UIUpdatesDemo extends StatelessWidget { const UIUpdatesDemo({super.key}); @override Widget build(BuildContext context) { print('BUILD called'); return Padding( padding: const EdgeInsets.all(8.0), child: Center( child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.center, children: const [ Text( 'Every Flutter developer should have a basic understanding of Flutter\'s internals!', textAlign: TextAlign.center, style: TextStyle(fontWeight: FontWeight.bold), ), SizedBox(height: 16), Text( 'Do you understand how Flutter updates UIs?', textAlign: TextAlign.center, ), SizedBox(height: 24), DemoButtons() ], ), ), ); } }
- 코드를 DemoButtons()로 Component화 (DemoButtons()) 한 후, 변화가 없는 코드 앞에 const 삽입 (변화되지 않기 때문에, flutter 가 읽지 않음 )
import 'package:flutter/material.dart'; class DemoButtons extends StatefulWidget { const DemoButtons({Key? key}) : super(key: key); @override State<DemoButtons> createState() => _DemoButtonsState(); } class _DemoButtonsState extends State<DemoButtons> { var _isUnderstood = false; @override Widget build(BuildContext context) { return Column( mainAxisSize: MainAxisSize.min, children: [ Row( mainAxisAlignment: MainAxisAlignment.center, children: [ TextButton( onPressed: () { setState(() { _isUnderstood = false; }); }, child: const Text('No'), ), TextButton( onPressed: () { setState(() { _isUnderstood = true; }); }, child: const Text('Yes'), ), ], ), if (_isUnderstood) const Text('Awesome!'), ], ); } }
- 이렇게 바꾸면, 버튼을 눌러 화면이 바뀌어도, 다시 Widget 트리가 생성되지 않는다.
3) Key (v-for key 와 비슷)
- Key:
keys are objects used to uniquely identify and associate widgets or elements in the Widget Tree. They enable efficient reconciliation and updating of the UI during changes or updates. By using keys appropriately, you can optimize performance, preserve widget state, and ensure accurate rendering of the UI in Flutter applications.
// for (final todo in _orderedTodos) TodoItem(todo.text, todo.priority), for (final todo in _orderedTodos) CheckableTodoItem( key: ObjectKey(todo), // ValueKey() todo.text, todo.priority, ),
4) Mutating Values
// 1. final final numbers = [1,2,3]; numbers.add(4); // 0 numbers = [1]; // X // 2. Var var numbers = [1,2,3]; numbers.add(4); // 0 numbers = [1]; // 0 // 3. Const: const numbers = [1,2,3]; numbers.add(4); // X, cannot add to an unmodifiable list (runtime error) numbers = [1]; // X
- Const 의 경우, 값에 일체의 변화를 허용하지 않기 때문에, flutter에서 변하지 않는 widget 의 경우 const를 사용해서 메모리 리소스 낭비 줄임.
- 할당
재할당시, 위치 변함 변화할 때는, 위치 안변함 '> Frontend > Flutter' 카테고리의 다른 글
Flutter_18. App State_Provider(feat. Riverpod) (0) 2023.05.21 Flutter_17. GridView & Inkwell & FadeImage & Stack (0) 2023.05.17 Flutter_15. Building Responsive and adaptive User Interfaces (0) 2023.05.15 Flutter_14. Theme in Widget & Dark Mode & Named Constructor & For in (0) 2023.05.12 Flutter_14. Dropdown and Validation (0) 2023.05.09