Как двадцать первая статья в серии, эта статья рассматривает общие принципы рендера Flutter Framework с другого ракурса, углубленно анализируя процесс отрисовки после создания слоев (Layers) в Flutter, чтобы разработчики лучше понимали принципы рендера и логику реализации.
Напомним, что в Flutter компоненты проходят через изменения от Widget
до Element
, затем до RenderObject
и наконец до Layer
. Определение того, какие объекты составят слой (Layer
), зависит от значения свойства isRepaintBoundary
в каждом RenderObject
.
При вызове метода
setState
,RenderObject
начинает поиск вверх по родительской цепочке, где значениеisRepaintBoundary
равноtrue
. Это определяет область, которая будет перерисована. Другими словами, это способ определить, какие области будут обновлены.
К примеру, при переходе между маршрутами с помощью Navigator
, каждый маршрут имеет внутри себя компонент RepaintBoundary
, который имеет значение isRepaintBoundary
равное true
. Таким образом, маршруты становятся независимыми слоями.
Поэтому связанные RenderObject
вместе образуют слой (Layer
), а дерево слоев (Layer Tree
) в конечном итоге передается в движок Flutter для отображения.
Тогда как работает слой? Какова его сущность? Какие шаги предпринимает Flutter Framework для отправки слоев в движок?## 2. Отрисовка в Flutter Framework
Ответив на вопросы выше относительно слоев, давайте сделаем предположение: если мы игнорируем готовые компоненты Flutter Framework, как нам создать картинку? Или говоря другими словами, как создать слой?
Допустим, следующий код показывает, как можно отобразить центрально расположенный синий квадрат размером 100x100 пикселей, используя такие менее знакомые объекты, как PictureRecorder
, Canvas
и SceneBuilder
, вместо использования Widgets
, RenderObjects
или Layers
.
import 'dart:ui' as ui;
void main() {
ui.window.onBeginFrame = beginFrame;
ui.window.scheduleFrame();
}
void beginFrame(Duration frameTime) {
final double devicePixelRatio = ui.window.devicePixelRatio;
// Создаем холст для рисования
final ui.PictureRecorder recorder = ui.PictureRecorder();
// На основе холста создаем объект Canvas
final ui.Canvas canvas = ui.Canvas(recorder);
canvas.scale(devicePixelRatio, devicePixelRatio);
var centerX = ui.window.physicalSize.width / 2.0;
var centerY = ui.window.physicalSize.height / 2.0;
// Рисуем синий прямоугольник размером 100x100 пикселей
canvas.drawRect(
Rect.fromCenter(
center: Offset.zero,
width: 100,
height: 100),
new Paint()..color = Colors.blue);
// Закончим рисование
final ui.Picture picture = recorder.endRecording();
final ui.SceneBuilder sceneBuilder = ui.SceneBuilder()
..pushOffset(centerX, centerY)
..addImage(ui.Offset.zero, picture)
..pop();
}
``` ui.окно.рендер(sceneBuilder.построить());
}
Поскольку в Flutter создание объекта Canvas
обязательно требует PictureRecorder
, а PictureRecorder
как следует из названия, используется для создания изображения для записи рисунка, то в приведённом выше коде:
PictureRecorder
;PictureRecorder
создаётся Canvas
;Canvas
рисуется синий квадрат;PictureRecorder.endRecording()
получается Picture
;SceneBuilder.pushOffset
и SceneBuilder.addPicture
добавляется отрисованное содержимое;ui.window.render
происходит отрисовка.Внимание: метод
render
ограничен вызовом только внутриonBeginFrame
илиonDrawFrame
. Поэтому в коде есть строкаwindow.onBeginFrame = beginFrame;
.
Flutter Framework в своей нижней части использует window.render
для окончательной отрисовки. Как показывает следующий код, метод render
принимает параметром объект Scene
, и сам метод является native
, что указывает на то, что Flutter Framework передаёт Engine объект Scene
.
void render(Scene сцена) native 'Window_render';
А что такое Scene
? Где же Layer
? И какие между ними отношения?
---В Flutter объект Scene
фактически является нативным объектом, который соответствует структуре scene.cc
в модуле Engine
. Внутри scene.cc
содержится поле layer_tree_
, которое используется для отрисовки, поэтому можно сделать вывод, что объект Scene
связан с layer_tree_
внутри Engine
.Далее следует отметить, что в фреймворке Flutter объект Scene
может быть создан только через SceneBuilder
, а в SceneBuilder
присутствуют различные методы, такие как pushOffset
, pushClipRect
, pushOpacity
и так далее. Выполнение этих методов позволяет создать соответствующий объект EngineLayer
.
OffsetEngineLayer pushOffset(double dx, double dy, { OffsetEngineLayer oldLayer }) {
assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushOffset'));
final OffsetEngineLayer layer = OffsetEngineLayer._(_pushOffset(dx, dy));
assert(_debugPushLayer(layer));
return layer;
}
EngineLayer _pushOffset(double dx, double dy) native 'SceneBuilder_pushOffset';
Итак, до создания объекта Scene
с помощью SceneBuilder
, можно использовать методы типа push
для создания объектов EngineLayer
, например, в примере с синим квадратом, SceneBuilder
использует метод pushOffset
для создания соответствующего слоя смещения.
Затем рассмотрим объект Layer
в фреймворке Flutter, где видно, что по умолчанию в объекте Layer
уже есть поле EngineLayer
, что указывает на связь между Layer
и SceneBuilder
.
@protected
ui.EngineLayer get engineLayer => _engineLayer;
@protected
set engineLayer(ui.EngineLayer value) {
_engineLayer = value;
if (!alwaysNeedsAddToScene) {
if (parent != null && !parent.alwaysNeedsAddToScene) {
parent.markNeedsAddToScene();
}
}
}
ui.EngineLayer _engineLayer;
/// Переопределите этот метод для загрузки этого слоя в движок.
///
/// Верните слой движка для сохраненного рендера. Когда нет соответствующего слоя движка, вернётся null.
```Кроме того, в объекте `Layer` имеется ключевой метод `addToScene`, который реализуется в производных классах и после выполнения которого получается объект `EngineLayer`. Этот метод требует передачи объекта `SceneBuilder`, и его реализация включает в себя классы `OffsetLayer` и `PictureLayer` и другие. Изображение:
Поэтому, как показано ниже в коде, можно заметить реализацию методов `addToScene` для классов `OffsetLayer` и `PictureLayer`:
- `PictureLayer` вызывает метод `addPicture` класса `SceneBuilder`;
- `OffsetLayer` вызывает метод `pushOffset` класса `SceneBuilder`;
```dart
class PictureLayer extends Layer {
...
@override
void addToScene(ui.SceneBuilder builder, [Offset layerOffset = Offset.zero]) {
builder.addPicture(layerOffset, picture, isComplexHint: isComplexHint, willChangeHint: willChangeHint);
}
...
}
class OffsetLayer extends ContainerLayer {
...
OffsetLayer({Offset offset = Offset.zero}) : _offset = offset;
@override
void addToScene(ui.SceneBuilder builder, [Offset layerOffset = Offset.zero]) {
engineLayer = builder.pushOffset(
layerOffset.dx + offset.dx,
layerOffset.dy + offset.dy,
oldLayer: _engineLayer as ui.OffsetEngineLayer,
);
addChildrenToScene(builder);
builder.pop();
}
...
}
На этом этапе SceneBuilder
и Layer
успешно связаны через методы EngineLayer
и addToScene
, а также Scene
, который передается методом window.render
, был создан с помощью SceneBuilder
. Поэтому, как показано на следующем изображении, Layer
и Scene
таким образом "соединен" вместе.
Изображение:Перед этим синим прямоугольником кода, как показано ниже, здесь используется подход с использованием Layer
, что ближе к реализации Flutter Framework: через rootLayer
создаются последовательно Layer
деревья с помощью метода append
, а затем rootLayer
вызывает метод addToScene
, который выполняет метод addChildrenToScene
, тем самым запуская выполнение метода addToScene
для всех потомков Layer
.```dart
import 'dart:ui' as ui;
void main() { ui.window.onBeginFrame = beginFrame;
ui.window.scheduleFrame(); }
void beginFrame(Duration timeStamp) { final double devicePixelRatio = ui.window.devicePixelRatio;
// Создание холста final ui.PictureRecorder recorder = ui.PictureRecorder();
// Создание Canvas на основе холста final ui.Canvas canvas = ui.Canvas(recorder); canvas.scale(devicePixelRatio, devicePixelRatio);
var centerX = ui.window.physicalSize.width / 2.0; var centerY = ui.window.physicalSize.height / 2.0; }```### 4. Виды слоев
Здесь мы рассмотрим наиболее распространенные виды слоев в Flutter. Как показано на следующей схеме, слои обычно можно разделить на ContainerLayer
и непосредственно на слои.
ContainerLayer
могут иметь дочерние узлы и имеют метод append
. Они могут быть разделены на несколько категорий:
Почему эти слои являются ContainerLayer
? Это потому что они представляют собой операции по составлению пикселей и сами по себе не способны "рисовать" компоненты. Например, как в случае с маленьким синим квадратом, если требуется отобразить изображение, это обычно требует сочетания с PictureLayer.
Например, при создании ClipRRect
, его внутренний RenderClipRRect
может создать ClipRRectLayer
при вызове pushClipRRect
, а новый ClipRRectLayer
будет добавлен в качестве потомка родительского слоя через метод append
.
Некоторые слои не являются ContainerLayer
и не имеют дочерних узлов, такие как:
```- PictureLayer
используется для рисования изображений; большинство компонентов Flutter рисуются именно здесь;
TextureLayer
используется для внешних текстур, таких как воспроизведение видео или данные камеры;PlatformViewLayer
используется для встраивания текстур PlatformView на iOS;Пример того, как компоненты рисуются на Canvas, который берется из PaintingContext. Как показано ниже, после выполнения рисования через _repaintCompositedChild
, полученный Picture
передается в PictureLayer.picture
.
void stopRecordingIfNeeded() {
if (!_isRecording) return;
_currentLayer.picture = _recorder.endRecording();
_currentLayer = null;
_recorder = null;
_canvas = null;
}
После того как мы узнали, как Layer
отправляет данные для отрисовки, теперь рассмотрим процесс обновления и повторного использования слоев.
Как известно, когда свойство isRepaintBoundary
объекта RenderObject
равно true
, фреймворк Flutter автоматически создает слой OffsetLayer
, который "обволакивает" эту область. Обновление содержимого внутри слоя обычно не влияет на другие слои.
Как происходит обновление слоев? Это связано с методами markNeedsAddToScene
и updateSubtreeNeedsAddToScene
в составе слоя.
Вот пример кода:
@protected
@visibleForTesting
void markNeedsAddToScene() {
// Уже помечен. Кратчайший путь.
if (_needsAddToScene) {
return;
}
``` _needsAddToScene = true;
}
@override
void updateSubtreeNeedsAddToScene() {
super.updateSubtreeNeedsAddToScene();
Layer child = firstChild;
while (child != null) {
child.updateSubtreeNeedsAddToScene();
_needsAddToScene = _needsAddToScene || child._needsAddToScene;
child = child.nextSibling;
}
}
```Метод `markNeedsAddToScene` просто устанавливает значение `_needsAddToScene` равным `true`. Метод `updateSubtreeNeedsAddToScene` проходит по всем потомкам (`children`) и использует рекурсивный вызов `updateSubtreeNeedsAddToScene()` для проверки необходимости обновления каждого потомка. Если хотя бы один потомок требует обновления, то текущий слой также будет помечен как требующий обновления.
Аналогично тому, как `setState` помечает компонент как `_dirty`, **когда `_needsAddToScene` равно `true`, метод `addToScene` данного слоя будет вызван; если же `_needsAddToScene` равно `false` и `_engineLayer` не является пустым значением, это указывает на возможность повторного использования слоя**.
```dart
void _addToSceneWithRetainedRendering(ui.SceneBuilder builder) {
if (!_needsAddToScene && _engineLayer != null) {
builder.addRetained(_engineLayer);
return;
}
addToScene(builder);
_needsAddToScene = false;
}
Да, когда значение _needsAddToScene
равно false
, это говорит о том, что данный слой не требует обновления. Если при этом существует _engineLayer
, этот слой может быть повторно использован для отрисовки. Например, при открытии нового экрана нижний экран остается без изменений и используется для составления общего изображения, поэтому его слои могут быть повторно использованы для отрисовки.
Когда вызывается markNeedsAddToScene
?Как показано на следующей схеме, когда параметры потомков Layer
, такие как picture
в PictureLayer
или offset
в OffsetLayer
, изменяются, Layer
активно вызывает метод markNeedsAddToScene
, чтобы пометить себя как "грязный" слой. Кроме того, если свойство engineLayer
в Layer
меняется, это пытается спровоцировать родительский Layer
вызвать markNeedsAddToScene
, что приведёт к изменениям в родительском слое.```dart
@protected
set engineLayer(ui.EngineLayer value) {
_engineLayer = value;
if (!alwaysNeedsAddToScene) {
if (parent != null && !parent.alwaysNeedsAddToScene) {
parent.markNeedsAddToScene();
}
}
}
Метод `updateSubtreeNeedsAddToScene` вызывается во время выполнения `buildScene`. Перед вызовом `addToScene` выполняется ещё один вызов `updateSubtreeNeedsAddToScene`, который проверяет все дочерние узлы и определяет, требуется ли их изменение.
```dart
ui.Scene buildScene(ui.SceneBuilder builder) {
List<PictureLayer> temporaryLayers;
assert(() {
if (debugCheckElevationsEnabled) {
temporaryLayers = _debugCheckElevations();
}
return true;
}());
updateSubtreeNeedsAddToScene();
addToScene(builder);
_needsAddToScene = false;
final ui.Scene scene = builder.build();
return scene;
}
Наконец, вернёмся к Flutter Framework. В Flutter Framework метод _window.render
вызывается внутри метода compositeFrame
класса RenderView
; сам же RenderView
инициализируется в методе initRenderView
класса RendererBinding
; а этот метод вызывается при выполнении initInstances
, то есть при запуске runApp
.Проще говоря, можно сказать, что при запуске runApp
создаётся RenderView
, а его внутренний метод compositeFrame
использует _window.render
для отправки команд на отрисовку слоёв.
void составнойРамки() {
Timeline.startSync('Составление', arguments: timelineWhitelistArguments);
try {
final ui.SceneBuilder builder = ui.SceneBuilder();
final ui.Scene сцена = слой.построитьСцену(билдер);
if (автоматическаяНастройкаSystemUi)
_обновитьSystemChrome();
_окно.рендер(сцена);
сцена.disposing();
assert(() {
if (debugRepaintRainbowEnabled || debugRepaintTextRainbowEnabled)
debugCurrentRepaintColor = debugCurrentRepaintColor.cWithHue((debugCurrentRepaintColor.hue + 2.0) % 360.0);
return true;
}());
} finally {
Timeline.finishSync();
}
}
Поэтому когда вызывается runApp
, Flutter создаёт RenderView
, а в методе drawFrame
окна вызывается renderView.compositeFrame()
для подтверждения отрисовки. RenderView
как корневой узел имеет rootLayer
, который является подклассом OffsetLayer
— TransformLayer
. Это корневой узел слоев в Flutter.
Для примера рассмотрим следующий простой некорректный код, который после запуска выводит черный пустой экран. В этом случае мы можем использовать метод debugDumpLayerTree
для печати структуры слоев.
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// Этот виджет является корнем вашего приложения.
@override
Widget build(BuildContext context) {
new Future.delayed(Duration(seconds: 1), () {
debugDumpLayerTree();
});
return MaterialApp(
title: 'GSY Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Container(),
// routes: routers,
);
}
}
Результат печати представлен ниже:
По умолчанию из-за механизма создания слоев (
isRepaintBoundary
равенtrue
) автоматически создается одинOffsetLayer
, а также требуется хотя бы одинOffsetLayer
иPictureLayer
для работы сCanvas
.Layer tree dump: OffsetLayer@900.0% size | PictureLayer@900.0% size
Этот вывод показывает структуру слоев, которая была создана автоматически.Также стоит отметить, что TransformLayer
как rootLayer
имеет владельцем RenderView
, и у него есть два дочерних узла: child1
— OffsetLayer
и child2
— PictureLayer
.
I/flutter (32494): TransformLayer#f8fa5
I/flutter (32494): │ владелец: RenderView#2d51e
I/flutter (32494): │ создатель: [root]
I/flutter (32494): │ смещение: Offset(0.0, 0.0)
I/flutter (32494): │ преобразование:
I/flutter (32494): │ [0] 2.8,0.0,0.0,0.0
I/flutter (32494): │ [1] 0.0,2.8,0.0,0.0
I/flutter (32494): │ [2] 0.0,0.0,1.0,0.0
I/flutter (32494): │ [3] 0.0,0.0,0.0,1.0
I/flutter (32494): │
I/flutter (32494): ├─потомок 1: OffsetLayer#4503b
I/flutter (32494): │ │ создатель: RepaintBoundary ← _FocusMarker ← Semantics ← FocusScope
I/flutter (32494): │ │ ← PageStorage ← Offstage ← _ModalScopeStatus ←
I/flutter (32494): │ │ _ModalScope<dynamic>-[LabeledGlobalKey<_ModalScopeState<dynamic>>#e1be1]
I/flutter (32494): │ │ ← _OverlayEntry-[LabeledGlobalKey<_OverlayEntryState>#95107] ←
I/flutter (32494): │ │ Stack ← _Theatre ←
I/flutter (32494): │ │ Overlay-[LabeledGlobalKey<OverlayState>#ceb36] ← ⋯
I/flutter (32494): │ │ смещение: Offset(0.0, 0.0)
I/flutter (32494): │ │
I/flutter (32494): │ └─потомок 1: OffsetLayer#e8309
I/flutter (32494): │ создатель: RepaintBoundary-[GlobalKey#bbad8] ← IgnorePointer ←
I/flutter (32494): │ FadeTransition ← FractionalTranslation ← SlideTransition ←
I/flutter (32494): │ _FadeUpwardsPageTransition ← AnimatedBuilder ← RepaintBoundary
I/flutter (32494): │ ← _FocusMarker ← Semantics ← FocusScope ← PageStorage ← ⋯
I/flutter (32494): │ смещение: Offset(0.0, 0.0)
I/flutter (32494): │
I/flutter (32494): └─потомок 2: PictureLayer#be4f1
I/flutter (32494): границы отрисовки: Rect.fromLTRB(0.0, 0.0, 1080.0, 2030.0)
Согласно вышеуказанному логу можно сделать вывод:- OffsetLayer
создается с помощью RepaintBoundary
, а его источник — это Overlay
. В Flutter Overlay
используется для создания глобальных плавающих компонентов, и он создаётся в Navigator
приложения MaterialApp
как независимый слой;
OffsetLayer
является PageStorage
, который создаётся путём маршрутизации (Route
) и представляет собой первый по умолчанию маршрут.Таким образом, теперь понятно, почему Overlay
может быть глобально плавающим во всех маршрутах приложения MaterialApp
.
Вот пример кода, где к исходному коду добавлен Scaffold
, после чего выполнена функция debugDumpLayerTree
.
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
new Future.delayed(Duration(seconds: 1), () {
debugDumpLayerTree();
});
return MaterialApp(
title: 'GSY Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
body: Container(),
),
// routes: routers,
);
}
}
```Здесь можно заметить появление двух новых слоев: `PhysicalModelLayer` и `PictureLayer`. `PhysicalModelLayer` используется для установки эффектов тени и других свойств, таких как отключение тени у `AppBar` при выключении `debugDisablePhysicalShapeLayers`. А `PictureLayer` служит для рисования.
```markdown
I/flutter (32494): TransformLayer#ac14b
I/flutter (32494): │ владелец: RenderView#f5ecc
I/flutter (32494): │ создатель: [root]
I/flutter (32494): │ смещение: Offset(0.0, 0.0)
I/flutter (32494): │ преобразование:
I/flutter (32494): │ [0] 2.8, 0.0, 0.0, 0.0
I/flutter (32494): │ [1] 0.0, 2.8, 0.0, 0.0
I/flutter (32494): │ [2] 0.0, 0.0, 1.0, 0.0
I/flutter (32494): │ [3] 0.0, 0.0, 0.0, 1.0
I/flutter (32494): │
I/flutter (32494): ├─потомок 1: OffsetLayer#c0128
I/flutter (32494): │ │ создатель: RepaintBoundary ← _FocusMarker ← Semantics ← FocusScope
I/flutter (32494): │ │ ← PageStorage ← Offstage ← _ModalScopeStatus ←
I/flutter (32494): │ │ _ModalScope<dynamic>-[LabeledGlobalKey<_ModalScopeState<dynamic>>#fe143]
I/flutter (32494): │ │ ← _OverlayEntry-[LabeledGlobalKey<_OverlayEntryState>#9cb60] ←
I/flutter (32494): │ │ Stack ← _Theatre ←
I/flutter (32494): │ │ Overlay-[LabeledGlobalKey<OverlayState>#ee455] ← ⋯
I/flutter (32494): │ │ смещение: Offset(0.0, 0.0)
I/flutter (32494): │ │
I/flutter (32494): │ └─потомок 1: OffsetLayer#fb2a6
I/flutter (32494): │ │ создатель: RepaintBoundary-[GlobalKey#fd46b] ← IgnorePointer ←
I/flutter (32494): │ │ FadeTransition ← FractionalTranslation ← SlideTransition ←
I/flutter (32494): │ │ _FadeUpwardsPageTransition ← AnimatedBuilder ← RepaintBoundary
I/flutter (32494): │ │ ← _FocusMarker ← Semantics ← FocusScope ← PageStorage ← ⋯
I/flutter (32494): │ │ смещение: Offset(0.0, 0.0)
I/flutter (32494): │ │
I/flutter (32494): │ └─потомок 1: PhysicalModelLayer#f1460
I/flutter (32494): │ │ создатель: PhysicalModel ← AnimatedPhysicalModel ← Material ←
```I/flutter (32494): │ │ PrimaryScrollController ← _ScaffoldScope ← Scaffold ← Semantics
I/flutter (32494): │ │ ← Builder ← RepaintBoundary-[GlobalKey#fd46b] ← IgnorePointer ←
I/flutter (32494): │ │ FadeTransition ← FractionalTranslation ← ⋯
I/flutter (32494): │ │ выпуклость: 0.0
I/flutter (32494): │ │ цвет: Color(0xfffafafa)
I/flutter (32494): │ │
I/flutter (32494): │ └─потомок 1: PictureLayer#f800f
I/flutter (32494): │ границы рисунка: Rect.fromLTRB(0.0, 0.0, 392.7, 738.2)
I/flutter (32494): │
I/flutter (32494): └─потомок 2: PictureLayer#af14d
```I/flutter (32494): paint bounds: Rect.fromLTRB(0.0, 0.0, 1080.0, 2030.0)
I/flutter (32494):```markdown
Итак, после того как вы снова используете `Navigator`, чтобы перейти на другой экран, и печатаете дерево слоёв (`Layer` tree) на новой странице, можно заметить появление новых слоёв — `PictureLayer`, `AnnotatedRegionLayer` и `TransformLayer`. В частности, новый слой `AnnotatedRegionLayer` используется для управления отображением области состояния в верхней части нового экрана.
``````markdown
I/flutter (32494): TransformLayer#12e21
I/flutter (32494): │ владелец: RenderView#aa5c7
I/flutter (32494): │ создатель: [root]
I/flutter (32494): │ смещение: Offset(0.0, 0.0)
I/flutter (32494): │ преобразование:
I/flutter (32494): │ [0] 2.8,0.0,0.0,0.0
I/flutter (32494): │ [1] 0.0,2.8,0.0,0.0
I/flutter (32494): │ [2] 0.0,0.0,1.0,0.0
I/flutter (32494): │ [3] 0.0,0.0,0.0,1.0
I/flutter (32494): │
I/flutter (32494): ├─потомок 1: OffsetLayer#fc176
I/flutter (32494): │ │ создатель: RepaintBoundary ← _FocusMarker ← Semantics ← FocusScope
I/flutter (32494): │ │ ← PageStorage ← Offstage ← _ModalScopeStatus ←
I/flutter (32494): │ │ _ModalScope<dynamic>-[LabeledGlobalKey<_ModalScopeState<dynamic>>#43140]
I/flutter (32494): │ │ ← _OverlayEntry-[LabeledGlobalKey<_OverlayEntryState>#46f19] ←I/flutter (32494): │ │ Stack ← _Theatre ←
I/flutter (32494): │ │ Overlay-[LabeledGlobalKey<OverlayState>#af6f4] ← ⋯
I/flutter (32494): │ │ смещение: Offset(0.0, 0.0)
I/flutter (32494): │ │
I/flutter (32494): │ └─потомок 1: OffsetLayer#b6e14
I/flutter (32494): │ │ создатель: RepaintBoundary-[GlobalKey#0ce90] ← IgnorePointer ←
I/flutter (32494): │ │ FadeTransition ← FractionalTranslation ← SlideTransition ←
I/flutter (32494): │ │ _FadeUpwardsPageTransition ← AnimatedBuilder ← RepaintBoundary
I/flutter (32494): │ │ ← _FocusMarker ← Semantics ← FocusScope ← PageStorage ← ⋯
I/flutter (32494): │ │ смещение: Offset(0.0, 0.0)
I/flutter (32494): │ │
I/flutter (32494): │ └─потомок 1: PhysicalModelLayer#4fdc6
I/flutter (32494): │ │ создатель: PhysicalModel ← AnimatedPhysicalModel ← Material ←
I/flutter (32494): │ │ PrimaryScrollController ← _ScaffoldScope ← Scaffold ←
I/flutter (32494): │ │ ClipDemoPage ← Semantics ← Builder ←
I/flutter (32494): │ │ RepaintBoundary-[GlobalKey#0ce90] ← IgnorePointer ←
I/flutter (32494): │ │ FadeTransition ← ⋯
I/flutter (32494): │ │ выступание: 0.0
I/flutter (32494): │ │ цвет: Color(0xfffafafa)
I/flutter (32494): │ │
I/flutter (32494): │ ├─потомок 1: PictureLayer#6ee26
I/flutter (32494): │ │ границы рисунка: Rect.fromLTWH(0.0, 0.0, 392.7, 738.2)
I/flutter (32494): │ │
```I/flutter (32494): │ ├─потомок 2: AnnotatedRegionLayer<SystemUiOverlayStyle>#cbeaf
I/flutter (32494): │ │ │ значение: {systemNavigationBarColor: 4278190080,
I/flutter (32494): │ │ │ systemNavigationBarDividerColor: null, statusBarColor: null,
I/flutter (32494): │ │ │ statusBarBrightness: Brightness.dark, statusBarIconBrightness:
I/flutter (32494): │ │ │ Brightness.light, systemNavigationBarIconBrightness:
I/flutter (32494): │ │ │ Brightness.light}
I/flutter (32494): │ │ │ размер: Size(392.7, 83.6)
I/flutter (32494): │ │ │ смещение: Offset(0.0, 0.0)
I/flutter (32494): │ │ │ I/flutter (32494): │ │ └─потомок 1: PhysicalModelLayer#edb15
I/flutter (32494): │ │ │ создатель: PhysicalModel ← AnimatedPhysicalModel ← Material ←
I/flutter (32494): │ │ │ AnnotatedRegion<SystemUiOverlayStyle> ← Semantics ← AppBar ←
I/flutter (32494): │ │ │ FlexibleSpaceBarSettings ← ConstrainedBox ← MediaQuery ←
I/flutter (32494): │ │ │ LayoutId-[<_ScaffoldSlot.appBar>] ← CustomMultiChildLayout ←
I/flutter (32494): │ │ │ AnimatedBuilder ← ⋯
I/flutter (32494): │ │ │ высота: 4.0
I/flutter (32494): │ │ │ цвет: MaterialColor(основное значение: Color(0xff2196f3))
I/flutter (32494): │ │ │
I/flutter (32494): │ │ └─потомок 1: PictureLayer#418ce
I/flutter (32494): │ │ область рисования: Rect.fromLTBR(0.0, 0.0, 392.7, 83.6)
I/flutter (32494): │ │
I/flutter (32494): │ └─потомок 3: TransformLayer#7f867
I/flutter (32494): │ │ смещение: Offset(0.0, 0.0)
I/flutter (32494): │ │ преобразование:
I/flutter (32494): │ │ [0] 1.0, 0.0, 0.0, -0.0
I/flutter (32494): │ │ [1] -0.0, 1.0, 0.0, 0.0
I/flutter (32494): │ │ [2] 0.0, 0.0, 1.0, 0.0
I/flutter (32494): │ │ [3] 0.0, 0.0, 0.0, 1.0
I/flutter (32494): │ │
I/flutter (32494): │ └─потомок 1: PhysicalModelLayer#9f36b
I/flutter (32494): │ │ создатель: PhysicalShape ← _MaterialInterior ← Material ←
I/flutter (32494): │ │ ConstrainedBox ← _FocusMarker ← Focus ← _InputPadding ←
I/flutter (32494): │ │ Semantics ← RawMaterialButton ← KeyedSubtree-[GlobalKey#9ead9]
I/flutter (32494): │ │ ← TickerMode ← Offstage ← ⋯
I/flutter (32494): │ │ выступание: 6.0
I/flutter (32494): │ │ цвет: Color(0xff2196f3)
I/flutter (32494): │ │
I/flutter (32494): │ └─потомок 1: PictureLayer#2a074
I/flutter (32494): │ границы рисования: Rect.fromLTRB(320.7, 666.2, 376.7, 722.2)
I/flutter (32494): │
I/flutter (32494): └─потомок 2: PictureLayer#3d42dI/flutter (32494): границы рисования: Rect. fromLTRB(0.0, 0.0, 1080.0, 2030.0)
I/flutter (32494): ```
Как можно заметить, в Flutter `Widget` в конечном итоге формирует различные `Layer`, каждый из которых имеет свой отдельный регион и функцию, например, `AnnotatedRegionLayer` управляет изменениями цвета состояния панели навигации на новых страницах, а эти `Layer` в конечном итоге преобразуются в `EngineLayer` с помощью `SceneBuilder`, который затем передается как `Scene` для отрисовки через движок.
Краткий вывод: **`Layer` в Flutter Framework до того, как они будут отрисованы, проходят через процесс обработки с использованием `SceneBuilder`, чтобы получить `EngineLayer`. Можно сказать, что `Layer` в Flutter Framework представляют собой объекты, упакованные внутри `SceneBuilder`, тогда как `EngineLayer` представляет собой реальные слои движка, которые затем становятся частью `Scene` для отрисовки через движок.**
> Таким образом, двадцать первая статья наконец завершена! (///▽///)
## Рекомендации по материалам
* Github: https://github.com/CarGuo
* **Открытый проект Flutter с полной реализацией: https://github.com/CarGuo/GSYGithubAppFlutter**
* **Открытый проект Flutter с множеством примеров: https://github.com/CarGuo/GSYFlutterDemo**
* **Открытая электронная книга по Flutter: https://github.com/CarGuo/GSYFlutterBook**
* Открытый проект React Native: https://github.com/CarGuo/GSYGithubApp

Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )