mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
parent
e074af8099
commit
bb5a82a726
@ -31,7 +31,7 @@ void main() {
|
|||||||
|
|
||||||
for (int i = 0; i < _kNumberOfIterations || _kRunForever; ++i) {
|
for (int i = 0; i < _kNumberOfIterations || _kRunForever; ++i) {
|
||||||
renderView.configuration = (i % 2 == 0) ? big : small;
|
renderView.configuration = (i % 2 == 0) ? big : small;
|
||||||
RenderObject.flushLayout();
|
WidgetFlutterBinding.instance.pipelineOwner.flushLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
watch.stop();
|
watch.stop();
|
||||||
|
@ -20,8 +20,8 @@ The last phase of a frame is the Semantics phase. This only occurs if
|
|||||||
a semantics server has been installed, for example if the user is
|
a semantics server has been installed, for example if the user is
|
||||||
using an accessibility tool.
|
using an accessibility tool.
|
||||||
|
|
||||||
Each frame, the semantics phase starts with a call to the static
|
Each frame, the semantics phase starts with a call to the
|
||||||
`RenderObject.flushSemantics()` method from the `Renderer` binding's
|
`PipelineOwner.flushSemantics()` method from the `Renderer` binding's
|
||||||
`beginFrame()` method.
|
`beginFrame()` method.
|
||||||
|
|
||||||
Each node marked as needing semantics (which initially is just the
|
Each node marked as needing semantics (which initially is just the
|
||||||
|
@ -48,6 +48,10 @@ abstract class Renderer extends Object with Scheduler, Services
|
|||||||
handleMetricsChanged(); // configures renderView's metrics
|
handleMetricsChanged(); // configures renderView's metrics
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The render tree's owner, which maintains dirty state for layout,
|
||||||
|
/// composite, paint, and accessibility semantics
|
||||||
|
final PipelineOwner pipelineOwner = new PipelineOwner();
|
||||||
|
|
||||||
/// The render tree that's attached to the output surface.
|
/// The render tree that's attached to the output surface.
|
||||||
RenderView get renderView => _renderView;
|
RenderView get renderView => _renderView;
|
||||||
RenderView _renderView;
|
RenderView _renderView;
|
||||||
@ -58,7 +62,7 @@ abstract class Renderer extends Object with Scheduler, Services
|
|||||||
if (_renderView != null)
|
if (_renderView != null)
|
||||||
_renderView.detach();
|
_renderView.detach();
|
||||||
_renderView = value;
|
_renderView = value;
|
||||||
_renderView.attach();
|
_renderView.attach(pipelineOwner);
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleMetricsChanged() {
|
void handleMetricsChanged() {
|
||||||
@ -81,12 +85,12 @@ abstract class Renderer extends Object with Scheduler, Services
|
|||||||
/// Pump the rendering pipeline to generate a frame.
|
/// Pump the rendering pipeline to generate a frame.
|
||||||
void beginFrame() {
|
void beginFrame() {
|
||||||
assert(renderView != null);
|
assert(renderView != null);
|
||||||
RenderObject.flushLayout();
|
pipelineOwner.flushLayout();
|
||||||
RenderObject.flushCompositingBits();
|
pipelineOwner.flushCompositingBits();
|
||||||
RenderObject.flushPaint();
|
pipelineOwner.flushPaint();
|
||||||
renderView.compositeFrame(); // this sends the bits to the GPU
|
renderView.compositeFrame(); // this sends the bits to the GPU
|
||||||
if (SemanticsNode.hasListeners) {
|
if (SemanticsNode.hasListeners) {
|
||||||
RenderObject.flushSemantics();
|
pipelineOwner.flushSemantics();
|
||||||
SemanticsNode.sendSemanticsTree();
|
SemanticsNode.sendSemanticsTree();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -337,8 +337,8 @@ class RenderBlockViewport extends RenderBlockBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void attach() {
|
void attach(PipelineOwner owner) {
|
||||||
super.attach();
|
super.attach(owner);
|
||||||
_overlayPainter?.attach(this);
|
_overlayPainter?.attach(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -388,12 +388,7 @@ class BoxHitTestEntry extends HitTestEntry {
|
|||||||
/// Parent data used by [RenderBox] and its subclasses.
|
/// Parent data used by [RenderBox] and its subclasses.
|
||||||
class BoxParentData extends ParentData {
|
class BoxParentData extends ParentData {
|
||||||
/// The offset at which to paint the child in the parent's coordinate system
|
/// The offset at which to paint the child in the parent's coordinate system
|
||||||
Offset get offset => _offset;
|
Offset offset = Offset.zero;
|
||||||
Offset _offset = Offset.zero;
|
|
||||||
void set offset(Offset value) {
|
|
||||||
assert(RenderObject.debugDoingLayout);
|
|
||||||
_offset = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => 'offset=$offset';
|
String toString() => 'offset=$offset';
|
||||||
@ -556,9 +551,9 @@ abstract class RenderBox extends RenderObject {
|
|||||||
assert(!_debugDoingBaseline);
|
assert(!_debugDoingBaseline);
|
||||||
assert(() {
|
assert(() {
|
||||||
final RenderObject parent = this.parent;
|
final RenderObject parent = this.parent;
|
||||||
if (RenderObject.debugDoingLayout)
|
if (owner.debugDoingLayout)
|
||||||
return (RenderObject.debugActiveLayout == parent) && parent.debugDoingThisLayout;
|
return (RenderObject.debugActiveLayout == parent) && parent.debugDoingThisLayout;
|
||||||
if (RenderObject.debugDoingPaint)
|
if (owner.debugDoingPaint)
|
||||||
return ((RenderObject.debugActivePaint == parent) && parent.debugDoingThisPaint) ||
|
return ((RenderObject.debugActivePaint == parent) && parent.debugDoingThisPaint) ||
|
||||||
((RenderObject.debugActivePaint == this) && debugDoingThisPaint);
|
((RenderObject.debugActivePaint == this) && debugDoingThisPaint);
|
||||||
assert(parent == this.parent);
|
assert(parent == this.parent);
|
||||||
|
@ -171,8 +171,8 @@ class RenderChildView extends RenderBox {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void attach() {
|
void attach(PipelineOwner owner) {
|
||||||
super.attach();
|
super.attach(owner);
|
||||||
_child?._attach();
|
_child?._attach();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ class AbstractNode {
|
|||||||
|
|
||||||
/// Call only from overrides of [redepthChildren]
|
/// Call only from overrides of [redepthChildren]
|
||||||
void redepthChild(AbstractNode child) {
|
void redepthChild(AbstractNode child) {
|
||||||
assert(child._attached == _attached);
|
assert(child.owner == owner);
|
||||||
if (child._depth <= _depth) {
|
if (child._depth <= _depth) {
|
||||||
child._depth = _depth + 1;
|
child._depth = _depth + 1;
|
||||||
child.redepthChildren();
|
child.redepthChildren();
|
||||||
@ -62,16 +62,21 @@ class AbstractNode {
|
|||||||
/// redepthChild(child) for each child. Do not call directly.
|
/// redepthChild(child) for each child. Do not call directly.
|
||||||
void redepthChildren() { }
|
void redepthChildren() { }
|
||||||
|
|
||||||
bool _attached = false;
|
Object _owner;
|
||||||
/// Whether this node is in a tree whose root is attached to something.
|
/// The owner for this node (null if unattached).
|
||||||
bool get attached => _attached;
|
Object get owner => _owner;
|
||||||
|
|
||||||
/// Mark this node as attached.
|
/// Whether this node is in a tree whose root is attached to something.
|
||||||
|
bool get attached => _owner != null;
|
||||||
|
|
||||||
|
/// Mark this node as attached to the given owner.
|
||||||
///
|
///
|
||||||
/// Typically called only from the parent's attach(), and to mark the root of
|
/// Typically called only from the parent's attach(), and to mark the root of
|
||||||
/// a tree attached.
|
/// a tree attached.
|
||||||
void attach() {
|
void attach(Object owner) {
|
||||||
_attached = true;
|
assert(owner != null);
|
||||||
|
assert(_owner == null);
|
||||||
|
_owner = owner;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mark this node as detached.
|
/// Mark this node as detached.
|
||||||
@ -79,7 +84,8 @@ class AbstractNode {
|
|||||||
/// Typically called only from the parent's detach(), and to mark the root of
|
/// Typically called only from the parent's detach(), and to mark the root of
|
||||||
/// a tree detached.
|
/// a tree detached.
|
||||||
void detach() {
|
void detach() {
|
||||||
_attached = false;
|
assert(_owner != null);
|
||||||
|
_owner = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
AbstractNode _parent;
|
AbstractNode _parent;
|
||||||
@ -99,7 +105,7 @@ class AbstractNode {
|
|||||||
});
|
});
|
||||||
child._parent = this;
|
child._parent = this;
|
||||||
if (attached)
|
if (attached)
|
||||||
child.attach();
|
child.attach(_owner);
|
||||||
redepthChild(child);
|
redepthChild(child);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -559,7 +559,8 @@ class _RootSemanticsFragment extends _InterestingSemanticsFragment {
|
|||||||
assert(currentSemantics == null);
|
assert(currentSemantics == null);
|
||||||
assert(parentSemantics == null);
|
assert(parentSemantics == null);
|
||||||
owner._semantics ??= new SemanticsNode.root(
|
owner._semantics ??= new SemanticsNode.root(
|
||||||
handler: owner is SemanticActionHandler ? owner as dynamic : null
|
handler: owner is SemanticActionHandler ? owner as dynamic : null,
|
||||||
|
owner: owner.owner
|
||||||
);
|
);
|
||||||
SemanticsNode node = owner._semantics;
|
SemanticsNode node = owner._semantics;
|
||||||
assert(MatrixUtils.matrixEquals(node.transform, new Matrix4.identity()));
|
assert(MatrixUtils.matrixEquals(node.transform, new Matrix4.identity()));
|
||||||
@ -671,6 +672,105 @@ class _ForkingSemanticsFragment extends _SemanticsFragment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class PipelineOwner {
|
||||||
|
|
||||||
|
List<RenderObject> _nodesNeedingLayout = <RenderObject>[];
|
||||||
|
bool _debugDoingLayout = false;
|
||||||
|
bool get debugDoingLayout => _debugDoingLayout;
|
||||||
|
/// Update the layout information for all dirty render objects.
|
||||||
|
///
|
||||||
|
/// This function is one of the core stages of the rendering pipeline. Layout
|
||||||
|
/// information is cleaned prior to painting so that render objects will
|
||||||
|
/// appear on screen in their up-to-date locations.
|
||||||
|
///
|
||||||
|
/// See [FlutterBinding] for an example of how this function is used.
|
||||||
|
void flushLayout() {
|
||||||
|
Timeline.startSync('Layout');
|
||||||
|
_debugDoingLayout = true;
|
||||||
|
try {
|
||||||
|
// TODO(ianh): assert that we're not allowing previously dirty nodes to redirty themeselves
|
||||||
|
while (_nodesNeedingLayout.isNotEmpty) {
|
||||||
|
List<RenderObject> dirtyNodes = _nodesNeedingLayout;
|
||||||
|
_nodesNeedingLayout = <RenderObject>[];
|
||||||
|
for (RenderObject node in dirtyNodes..sort((RenderObject a, RenderObject b) => a.depth - b.depth)) {
|
||||||
|
if (node._needsLayout && node.owner == this)
|
||||||
|
node._layoutWithoutResize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
_debugDoingLayout = false;
|
||||||
|
Timeline.finishSync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<RenderObject> _nodesNeedingCompositingBitsUpdate = <RenderObject>[];
|
||||||
|
/// Updates the [needsCompositing] bits.
|
||||||
|
///
|
||||||
|
/// Called as part of the rendering pipeline after [flushLayout] and before
|
||||||
|
/// [flushPaint].
|
||||||
|
void flushCompositingBits() {
|
||||||
|
Timeline.startSync('Compositing Bits');
|
||||||
|
_nodesNeedingCompositingBitsUpdate.sort((RenderObject a, RenderObject b) => a.depth - b.depth);
|
||||||
|
for (RenderObject node in _nodesNeedingCompositingBitsUpdate) {
|
||||||
|
if (node.owner == this)
|
||||||
|
node._updateCompositingBits();
|
||||||
|
}
|
||||||
|
_nodesNeedingCompositingBitsUpdate.clear();
|
||||||
|
Timeline.finishSync();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<RenderObject> _nodesNeedingPaint = <RenderObject>[];
|
||||||
|
bool _debugDoingPaint = false;
|
||||||
|
bool get debugDoingPaint => _debugDoingPaint;
|
||||||
|
/// Update the display lists for all render objects.
|
||||||
|
///
|
||||||
|
/// This function is one of the core stages of the rendering pipeline.
|
||||||
|
/// Painting occurs after layout and before the scene is recomposited so that
|
||||||
|
/// scene is composited with up-to-date display lists for every render object.
|
||||||
|
///
|
||||||
|
/// See [FlutterBinding] for an example of how this function is used.
|
||||||
|
void flushPaint() {
|
||||||
|
Timeline.startSync('Paint');
|
||||||
|
_debugDoingPaint = true;
|
||||||
|
try {
|
||||||
|
List<RenderObject> dirtyNodes = _nodesNeedingPaint;
|
||||||
|
_nodesNeedingPaint = <RenderObject>[];
|
||||||
|
// Sort the dirty nodes in reverse order (deepest first).
|
||||||
|
for (RenderObject node in dirtyNodes..sort((RenderObject a, RenderObject b) => b.depth - a.depth)) {
|
||||||
|
assert(node._needsPaint);
|
||||||
|
if (node.owner == this)
|
||||||
|
PaintingContext.repaintCompositedChild(node);
|
||||||
|
};
|
||||||
|
assert(_nodesNeedingPaint.length == 0);
|
||||||
|
} finally {
|
||||||
|
_debugDoingPaint = false;
|
||||||
|
Timeline.finishSync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _semanticsEnabled = false;
|
||||||
|
bool _debugDoingSemantics = false;
|
||||||
|
List<RenderObject> _nodesNeedingSemantics = <RenderObject>[];
|
||||||
|
|
||||||
|
void flushSemantics() {
|
||||||
|
Timeline.startSync('Semantics');
|
||||||
|
assert(_semanticsEnabled);
|
||||||
|
assert(() { _debugDoingSemantics = true; return true; });
|
||||||
|
try {
|
||||||
|
_nodesNeedingSemantics.sort((RenderObject a, RenderObject b) => a.depth - b.depth);
|
||||||
|
for (RenderObject node in _nodesNeedingSemantics) {
|
||||||
|
if (node._needsSemanticsUpdate && node.owner == this)
|
||||||
|
node._updateSemantics();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
_nodesNeedingSemantics.clear();
|
||||||
|
assert(() { _debugDoingSemantics = false; return true; });
|
||||||
|
Timeline.finishSync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/// An object in the render tree.
|
/// An object in the render tree.
|
||||||
///
|
///
|
||||||
/// The [RenderObject] class hierarchy is the core of the rendering
|
/// The [RenderObject] class hierarchy is the core of the rendering
|
||||||
@ -811,8 +911,6 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool _debugDoingLayout = false;
|
|
||||||
static bool get debugDoingLayout => _debugDoingLayout;
|
|
||||||
bool _debugDoingThisResize = false;
|
bool _debugDoingThisResize = false;
|
||||||
bool get debugDoingThisResize => _debugDoingThisResize;
|
bool get debugDoingThisResize => _debugDoingThisResize;
|
||||||
bool _debugDoingThisLayout = false;
|
bool _debugDoingThisLayout = false;
|
||||||
@ -835,7 +933,9 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static List<RenderObject> _nodesNeedingLayout = <RenderObject>[];
|
@override
|
||||||
|
PipelineOwner get owner => super.owner;
|
||||||
|
|
||||||
bool _needsLayout = true;
|
bool _needsLayout = true;
|
||||||
/// Whether this render object's layout information is dirty.
|
/// Whether this render object's layout information is dirty.
|
||||||
bool get needsLayout => _needsLayout;
|
bool get needsLayout => _needsLayout;
|
||||||
@ -912,7 +1012,8 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget {
|
|||||||
debugPrintStack();
|
debugPrintStack();
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
_nodesNeedingLayout.add(this);
|
if (owner != null)
|
||||||
|
owner._nodesNeedingLayout.add(this);
|
||||||
Scheduler.instance.ensureVisualUpdate();
|
Scheduler.instance.ensureVisualUpdate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -936,41 +1037,16 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget {
|
|||||||
void scheduleInitialLayout() {
|
void scheduleInitialLayout() {
|
||||||
assert(attached);
|
assert(attached);
|
||||||
assert(parent is! RenderObject);
|
assert(parent is! RenderObject);
|
||||||
assert(!_debugDoingLayout);
|
assert(!owner._debugDoingLayout);
|
||||||
assert(_relayoutSubtreeRoot == null);
|
assert(_relayoutSubtreeRoot == null);
|
||||||
_relayoutSubtreeRoot = this;
|
_relayoutSubtreeRoot = this;
|
||||||
assert(() {
|
assert(() {
|
||||||
_debugCanParentUseSize = false;
|
_debugCanParentUseSize = false;
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
_nodesNeedingLayout.add(this);
|
owner._nodesNeedingLayout.add(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update the layout information for all dirty render objects.
|
|
||||||
///
|
|
||||||
/// This function is one of the core stages of the rendering pipeline. Layout
|
|
||||||
/// information is cleaned prior to painting so that render objects will
|
|
||||||
/// appear on screen in their up-to-date locations.
|
|
||||||
///
|
|
||||||
/// See [FlutterBinding] for an example of how this function is used.
|
|
||||||
static void flushLayout() {
|
|
||||||
Timeline.startSync('Layout');
|
|
||||||
_debugDoingLayout = true;
|
|
||||||
try {
|
|
||||||
// TODO(ianh): assert that we're not allowing previously dirty nodes to redirty themeselves
|
|
||||||
while (_nodesNeedingLayout.isNotEmpty) {
|
|
||||||
List<RenderObject> dirtyNodes = _nodesNeedingLayout;
|
|
||||||
_nodesNeedingLayout = <RenderObject>[];
|
|
||||||
for (RenderObject node in dirtyNodes..sort((RenderObject a, RenderObject b) => a.depth - b.depth)) {
|
|
||||||
if (node._needsLayout && node.attached)
|
|
||||||
node._layoutWithoutResize();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
_debugDoingLayout = false;
|
|
||||||
Timeline.finishSync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void _layoutWithoutResize() {
|
void _layoutWithoutResize() {
|
||||||
assert(_relayoutSubtreeRoot == this);
|
assert(_relayoutSubtreeRoot == this);
|
||||||
RenderObject debugPreviousActiveLayout;
|
RenderObject debugPreviousActiveLayout;
|
||||||
@ -1180,16 +1256,11 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget {
|
|||||||
|
|
||||||
// PAINTING
|
// PAINTING
|
||||||
|
|
||||||
static bool _debugDoingPaint = false;
|
|
||||||
static bool get debugDoingPaint => _debugDoingPaint;
|
|
||||||
bool _debugDoingThisPaint = false;
|
bool _debugDoingThisPaint = false;
|
||||||
bool get debugDoingThisPaint => _debugDoingThisPaint;
|
bool get debugDoingThisPaint => _debugDoingThisPaint;
|
||||||
static RenderObject _debugActivePaint;
|
static RenderObject _debugActivePaint;
|
||||||
static RenderObject get debugActivePaint => _debugActivePaint;
|
static RenderObject get debugActivePaint => _debugActivePaint;
|
||||||
|
|
||||||
static List<RenderObject> _nodesNeedingPaint = <RenderObject>[];
|
|
||||||
static List<RenderObject> _nodesNeedingCompositingBitsUpdate = <RenderObject>[];
|
|
||||||
|
|
||||||
/// Whether this render object repaints separately from its parent.
|
/// Whether this render object repaints separately from its parent.
|
||||||
///
|
///
|
||||||
/// Override this in subclasses to indicate that instances of your class ought
|
/// Override this in subclasses to indicate that instances of your class ought
|
||||||
@ -1260,22 +1331,8 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget {
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
// parent is fine (or there isn't one), but we are dirty
|
// parent is fine (or there isn't one), but we are dirty
|
||||||
_nodesNeedingCompositingBitsUpdate.add(this);
|
if (owner != null)
|
||||||
}
|
owner._nodesNeedingCompositingBitsUpdate.add(this);
|
||||||
|
|
||||||
/// Updates the [needsCompositing] bits.
|
|
||||||
///
|
|
||||||
/// Called as part of the rendering pipeline after [flushLayout] and before
|
|
||||||
/// [flushPaint].
|
|
||||||
static void flushCompositingBits() {
|
|
||||||
Timeline.startSync('Compositing Bits');
|
|
||||||
_nodesNeedingCompositingBitsUpdate.sort((RenderObject a, RenderObject b) => a.depth - b.depth);
|
|
||||||
for (RenderObject node in _nodesNeedingCompositingBitsUpdate) {
|
|
||||||
if (node.attached)
|
|
||||||
node._updateCompositingBits();
|
|
||||||
}
|
|
||||||
_nodesNeedingCompositingBitsUpdate.clear();
|
|
||||||
Timeline.finishSync();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _needsCompositing; // initialised in the constructor
|
bool _needsCompositing; // initialised in the constructor
|
||||||
@ -1319,7 +1376,7 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget {
|
|||||||
/// This mechanism batches the painting work so that multiple sequential
|
/// This mechanism batches the painting work so that multiple sequential
|
||||||
/// writes are coalesced, removing redundant computation.
|
/// writes are coalesced, removing redundant computation.
|
||||||
void markNeedsPaint() {
|
void markNeedsPaint() {
|
||||||
assert(!debugDoingPaint);
|
assert(owner == null || !owner.debugDoingPaint);
|
||||||
if (!attached)
|
if (!attached)
|
||||||
return; // Don't try painting things that aren't in the hierarchy
|
return; // Don't try painting things that aren't in the hierarchy
|
||||||
if (_needsPaint)
|
if (_needsPaint)
|
||||||
@ -1334,7 +1391,8 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget {
|
|||||||
// If we always have our own layer, then we can just repaint
|
// If we always have our own layer, then we can just repaint
|
||||||
// ourselves without involving any other nodes.
|
// ourselves without involving any other nodes.
|
||||||
assert(_layer != null);
|
assert(_layer != null);
|
||||||
_nodesNeedingPaint.add(this);
|
if (owner != null)
|
||||||
|
owner._nodesNeedingPaint.add(this);
|
||||||
Scheduler.instance.ensureVisualUpdate();
|
Scheduler.instance.ensureVisualUpdate();
|
||||||
} else if (parent is RenderObject) {
|
} else if (parent is RenderObject) {
|
||||||
// We don't have our own layer; one of our ancestors will take
|
// We don't have our own layer; one of our ancestors will take
|
||||||
@ -1353,32 +1411,6 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update the display lists for all render objects.
|
|
||||||
///
|
|
||||||
/// This function is one of the core stages of the rendering pipeline.
|
|
||||||
/// Painting occurs after layout and before the scene is recomposited so that
|
|
||||||
/// scene is composited with up-to-date display lists for every render object.
|
|
||||||
///
|
|
||||||
/// See [FlutterBinding] for an example of how this function is used.
|
|
||||||
static void flushPaint() {
|
|
||||||
Timeline.startSync('Paint');
|
|
||||||
_debugDoingPaint = true;
|
|
||||||
try {
|
|
||||||
List<RenderObject> dirtyNodes = _nodesNeedingPaint;
|
|
||||||
_nodesNeedingPaint = <RenderObject>[];
|
|
||||||
// Sort the dirty nodes in reverse order (deepest first).
|
|
||||||
for (RenderObject node in dirtyNodes..sort((RenderObject a, RenderObject b) => b.depth - a.depth)) {
|
|
||||||
assert(node._needsPaint);
|
|
||||||
if (node.attached)
|
|
||||||
PaintingContext.repaintCompositedChild(node);
|
|
||||||
};
|
|
||||||
assert(_nodesNeedingPaint.length == 0);
|
|
||||||
} finally {
|
|
||||||
_debugDoingPaint = false;
|
|
||||||
Timeline.finishSync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Bootstrap the rendering pipeline by scheduling the very first paint.
|
/// Bootstrap the rendering pipeline by scheduling the very first paint.
|
||||||
///
|
///
|
||||||
/// Requires that this render object is attached, is the root of the render
|
/// Requires that this render object is attached, is the root of the render
|
||||||
@ -1388,12 +1420,12 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget {
|
|||||||
void scheduleInitialPaint(ContainerLayer rootLayer) {
|
void scheduleInitialPaint(ContainerLayer rootLayer) {
|
||||||
assert(attached);
|
assert(attached);
|
||||||
assert(parent is! RenderObject);
|
assert(parent is! RenderObject);
|
||||||
assert(!_debugDoingPaint);
|
assert(!owner._debugDoingPaint);
|
||||||
assert(isRepaintBoundary);
|
assert(isRepaintBoundary);
|
||||||
assert(_layer == null);
|
assert(_layer == null);
|
||||||
_layer = rootLayer;
|
_layer = rootLayer;
|
||||||
assert(_needsPaint);
|
assert(_needsPaint);
|
||||||
_nodesNeedingPaint.add(this);
|
owner._nodesNeedingPaint.add(this);
|
||||||
}
|
}
|
||||||
void _paintWithContext(PaintingContext context, Offset offset) {
|
void _paintWithContext(PaintingContext context, Offset offset) {
|
||||||
assert(!_debugDoingThisPaint);
|
assert(!_debugDoingThisPaint);
|
||||||
@ -1477,10 +1509,6 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget {
|
|||||||
|
|
||||||
// SEMANTICS
|
// SEMANTICS
|
||||||
|
|
||||||
static bool _semanticsEnabled = false;
|
|
||||||
static bool _debugDoingSemantics = false;
|
|
||||||
static List<RenderObject> _nodesNeedingSemantics = <RenderObject>[];
|
|
||||||
|
|
||||||
/// Bootstrap the semantics reporting mechanism by marking this node
|
/// Bootstrap the semantics reporting mechanism by marking this node
|
||||||
/// as needing a semantics update.
|
/// as needing a semantics update.
|
||||||
///
|
///
|
||||||
@ -1491,32 +1519,15 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget {
|
|||||||
void scheduleInitialSemantics() {
|
void scheduleInitialSemantics() {
|
||||||
assert(attached);
|
assert(attached);
|
||||||
assert(parent is! RenderObject);
|
assert(parent is! RenderObject);
|
||||||
assert(!_debugDoingSemantics);
|
assert(!owner._debugDoingSemantics);
|
||||||
assert(_semantics == null);
|
assert(_semantics == null);
|
||||||
assert(_needsSemanticsUpdate);
|
assert(_needsSemanticsUpdate);
|
||||||
assert(_semanticsEnabled == false);
|
assert(owner._semanticsEnabled == false);
|
||||||
_semanticsEnabled = true;
|
owner._semanticsEnabled = true;
|
||||||
_nodesNeedingSemantics.add(this);
|
owner._nodesNeedingSemantics.add(this);
|
||||||
Scheduler.instance.ensureVisualUpdate();
|
Scheduler.instance.ensureVisualUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void flushSemantics() {
|
|
||||||
Timeline.startSync('Semantics');
|
|
||||||
assert(_semanticsEnabled);
|
|
||||||
assert(() { _debugDoingSemantics = true; return true; });
|
|
||||||
try {
|
|
||||||
_nodesNeedingSemantics.sort((RenderObject a, RenderObject b) => a.depth - b.depth);
|
|
||||||
for (RenderObject node in _nodesNeedingSemantics) {
|
|
||||||
if (node._needsSemanticsUpdate)
|
|
||||||
node._updateSemantics();
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
_nodesNeedingSemantics.clear();
|
|
||||||
assert(() { _debugDoingSemantics = false; return true; });
|
|
||||||
Timeline.finishSync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Whether this RenderObject introduces a new box for accessibility purposes.
|
/// Whether this RenderObject introduces a new box for accessibility purposes.
|
||||||
bool get hasSemantics => false;
|
bool get hasSemantics => false;
|
||||||
|
|
||||||
@ -1561,8 +1572,8 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget {
|
|||||||
/// 'noGeometry: true' when the geometry did change, the semantic
|
/// 'noGeometry: true' when the geometry did change, the semantic
|
||||||
/// tree will be out of date.
|
/// tree will be out of date.
|
||||||
void markNeedsSemanticsUpdate({ bool onlyChanges: false, bool noGeometry: false }) {
|
void markNeedsSemanticsUpdate({ bool onlyChanges: false, bool noGeometry: false }) {
|
||||||
assert(!_debugDoingSemantics);
|
assert(!attached || !owner._debugDoingSemantics);
|
||||||
if (!_semanticsEnabled || !attached || (_needsSemanticsUpdate && onlyChanges && (_needsSemanticsGeometryUpdate || noGeometry)))
|
if (!attached || !owner._semanticsEnabled || (_needsSemanticsUpdate && onlyChanges && (_needsSemanticsGeometryUpdate || noGeometry)))
|
||||||
return;
|
return;
|
||||||
if (!noGeometry && (_semantics == null || (_semantics.hasChildren && _semantics.wasAffectedByClip))) {
|
if (!noGeometry && (_semantics == null || (_semantics.hasChildren && _semantics.wasAffectedByClip))) {
|
||||||
// Since the geometry might have changed, we need to make sure to reapply any clips.
|
// Since the geometry might have changed, we need to make sure to reapply any clips.
|
||||||
@ -1582,7 +1593,7 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget {
|
|||||||
}
|
}
|
||||||
if (!node._needsSemanticsUpdate) {
|
if (!node._needsSemanticsUpdate) {
|
||||||
node._needsSemanticsUpdate = true;
|
node._needsSemanticsUpdate = true;
|
||||||
_nodesNeedingSemantics.add(node);
|
owner._nodesNeedingSemantics.add(node);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// The shape of the semantics tree around us may have changed.
|
// The shape of the semantics tree around us may have changed.
|
||||||
@ -1601,7 +1612,7 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget {
|
|||||||
node._semantics?.reset();
|
node._semantics?.reset();
|
||||||
if (!node._needsSemanticsUpdate) {
|
if (!node._needsSemanticsUpdate) {
|
||||||
node._needsSemanticsUpdate = true;
|
node._needsSemanticsUpdate = true;
|
||||||
_nodesNeedingSemantics.add(node);
|
owner._nodesNeedingSemantics.add(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1814,10 +1825,10 @@ abstract class RenderObjectWithChildMixin<ChildType extends RenderObject> implem
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void attach() {
|
void attach(PipelineOwner owner) {
|
||||||
super.attach();
|
super.attach(owner);
|
||||||
if (_child != null)
|
if (_child != null)
|
||||||
_child.attach();
|
_child.attach(owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -2034,11 +2045,11 @@ abstract class ContainerRenderObjectMixin<ChildType extends RenderObject, Parent
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void attach() {
|
void attach(PipelineOwner owner) {
|
||||||
super.attach();
|
super.attach(owner);
|
||||||
ChildType child = _firstChild;
|
ChildType child = _firstChild;
|
||||||
while (child != null) {
|
while (child != null) {
|
||||||
child.attach();
|
child.attach(owner);
|
||||||
final ParentDataType childParentData = child.parentData;
|
final ParentDataType childParentData = child.parentData;
|
||||||
child = childParentData.nextSibling;
|
child = childParentData.nextSibling;
|
||||||
}
|
}
|
||||||
|
@ -1035,8 +1035,8 @@ class RenderDecoratedBox extends RenderProxyBox {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void attach() {
|
void attach(PipelineOwner owner) {
|
||||||
super.attach();
|
super.attach(owner);
|
||||||
_addListenerIfNeeded();
|
_addListenerIfNeeded();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,10 +58,11 @@ class SemanticsNode extends AbstractNode {
|
|||||||
_actionHandler = handler;
|
_actionHandler = handler;
|
||||||
|
|
||||||
SemanticsNode.root({
|
SemanticsNode.root({
|
||||||
SemanticActionHandler handler
|
SemanticActionHandler handler,
|
||||||
|
Object owner
|
||||||
}) : _id = 0,
|
}) : _id = 0,
|
||||||
_actionHandler = handler {
|
_actionHandler = handler {
|
||||||
attach();
|
attach(owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int _lastIdentifier = 0;
|
static int _lastIdentifier = 0;
|
||||||
@ -265,8 +266,8 @@ class SemanticsNode extends AbstractNode {
|
|||||||
static Set<SemanticsNode> _detachedNodes = new Set<SemanticsNode>();
|
static Set<SemanticsNode> _detachedNodes = new Set<SemanticsNode>();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void attach() {
|
void attach(Object owner) {
|
||||||
super.attach();
|
super.attach(owner);
|
||||||
assert(!_nodes.containsKey(_id));
|
assert(!_nodes.containsKey(_id));
|
||||||
_nodes[_id] = this;
|
_nodes[_id] = this;
|
||||||
_detachedNodes.remove(this);
|
_detachedNodes.remove(this);
|
||||||
@ -274,7 +275,7 @@ class SemanticsNode extends AbstractNode {
|
|||||||
_inheritedMergeAllDescendantsIntoThisNode = parent._shouldMergeAllDescendantsIntoThisNode;
|
_inheritedMergeAllDescendantsIntoThisNode = parent._shouldMergeAllDescendantsIntoThisNode;
|
||||||
if (_children != null) {
|
if (_children != null) {
|
||||||
for (SemanticsNode child in _children)
|
for (SemanticsNode child in _children)
|
||||||
child.attach();
|
child.attach(owner);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -404,7 +405,7 @@ class SemanticsNode extends AbstractNode {
|
|||||||
child._inheritedMergeAllDescendantsIntoThisNode = false; // this can add the node to the dirty list
|
child._inheritedMergeAllDescendantsIntoThisNode = false; // this can add the node to the dirty list
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert(_dirtyNodes[index] == node); // make sure nothing went in front of us in the list
|
assert(_dirtyNodes[index] == node); // make sure nothing went in front of us in the list
|
||||||
}
|
}
|
||||||
_dirtyNodes.sort((SemanticsNode a, SemanticsNode b) => a.depth - b.depth);
|
_dirtyNodes.sort((SemanticsNode a, SemanticsNode b) => a.depth - b.depth);
|
||||||
|
@ -33,7 +33,7 @@ class ViewConfiguration {
|
|||||||
/// The root of the render tree.
|
/// The root of the render tree.
|
||||||
///
|
///
|
||||||
/// The view represents the total output surface of the render tree and handles
|
/// The view represents the total output surface of the render tree and handles
|
||||||
/// bootstraping the rendering pipeline. The view has a unique child
|
/// bootstrapping the rendering pipeline. The view has a unique child
|
||||||
/// [RenderBox], which is required to fill the entire output surface.
|
/// [RenderBox], which is required to fill the entire output surface.
|
||||||
class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox> {
|
class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox> {
|
||||||
RenderView({
|
RenderView({
|
||||||
|
@ -168,8 +168,8 @@ class RenderViewportBase extends RenderBox implements HasMainAxis {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void attach() {
|
void attach(PipelineOwner owner) {
|
||||||
super.attach();
|
super.attach(owner);
|
||||||
_overlayPainter?.attach(this);
|
_overlayPainter?.attach(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -335,7 +335,11 @@ class RawGestureDetectorState extends State<RawGestureDetector> {
|
|||||||
/// the gesture detector should be enabled.
|
/// the gesture detector should be enabled.
|
||||||
void replaceGestureRecognizers(Map<Type, GestureRecognizerFactory> gestures) {
|
void replaceGestureRecognizers(Map<Type, GestureRecognizerFactory> gestures) {
|
||||||
assert(() {
|
assert(() {
|
||||||
if (!RenderObject.debugDoingLayout) {
|
// TODO kgiesing This assert will trigger if the owner of the current
|
||||||
|
// tree is different from the owner assigned to the renderer instance.
|
||||||
|
// Once elements have a notion of owners this assertion can be written
|
||||||
|
// more clearly.
|
||||||
|
if (!Renderer.instance.pipelineOwner.debugDoingLayout) {
|
||||||
throw new FlutterError(
|
throw new FlutterError(
|
||||||
'Unexpected call to replaceGestureRecognizers() method of RawGestureDetectorState.\n'
|
'Unexpected call to replaceGestureRecognizers() method of RawGestureDetectorState.\n'
|
||||||
'The replaceGestureRecognizers() method can only be called during the layout phase. '
|
'The replaceGestureRecognizers() method can only be called during the layout phase. '
|
||||||
|
84
packages/flutter/test/rendering/independent_layout_test.dart
Normal file
84
packages/flutter/test/rendering/independent_layout_test.dart
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
// Copyright 2016 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/rendering.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
import 'rendering_tester.dart';
|
||||||
|
|
||||||
|
class TestLayout {
|
||||||
|
TestLayout() {
|
||||||
|
// viewport incoming constraints are tight 800x600
|
||||||
|
// viewport is vertical by default
|
||||||
|
root = new RenderViewport(
|
||||||
|
child: new RenderCustomPaint(
|
||||||
|
painter: new TestCallbackPainter(
|
||||||
|
onPaint: () { painted = true; }
|
||||||
|
),
|
||||||
|
child: child = new RenderConstrainedBox(
|
||||||
|
additionalConstraints: new BoxConstraints.tightFor(height: 10.0, width: 10.0)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
RenderBox root;
|
||||||
|
RenderBox child;
|
||||||
|
bool painted = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
test('onscreen layout does not affect offscreen', () {
|
||||||
|
TestLayout onscreen = new TestLayout();
|
||||||
|
TestLayout offscreen = new TestLayout();
|
||||||
|
expect(onscreen.child.hasSize, isFalse);
|
||||||
|
expect(onscreen.painted, isFalse);
|
||||||
|
expect(offscreen.child.hasSize, isFalse);
|
||||||
|
expect(offscreen.painted, isFalse);
|
||||||
|
// Attach the offscreen to a custom render view and owner
|
||||||
|
RenderView renderView = new TestRenderView();
|
||||||
|
PipelineOwner pipelineOwner = new PipelineOwner();
|
||||||
|
renderView.attach(pipelineOwner);
|
||||||
|
renderView.child = offscreen.root;
|
||||||
|
renderView.scheduleInitialFrame();
|
||||||
|
// Lay out the onscreen in the default binding
|
||||||
|
layout(onscreen.root, phase: EnginePhase.layout);
|
||||||
|
expect(onscreen.child.hasSize, isTrue);
|
||||||
|
expect(onscreen.painted, isFalse);
|
||||||
|
expect(onscreen.child.size, equals(const Size(800.0, 10.0)));
|
||||||
|
// Make sure the offscreen didn't get laid out
|
||||||
|
expect(offscreen.child.hasSize, isFalse);
|
||||||
|
expect(offscreen.painted, isFalse);
|
||||||
|
// Now lay out the offscreen
|
||||||
|
pipelineOwner.flushLayout();
|
||||||
|
expect(offscreen.child.hasSize, isTrue);
|
||||||
|
expect(offscreen.painted, isFalse);
|
||||||
|
});
|
||||||
|
test('offscreen layout does not affect onscreen', () {
|
||||||
|
TestLayout onscreen = new TestLayout();
|
||||||
|
TestLayout offscreen = new TestLayout();
|
||||||
|
expect(onscreen.child.hasSize, isFalse);
|
||||||
|
expect(onscreen.painted, isFalse);
|
||||||
|
expect(offscreen.child.hasSize, isFalse);
|
||||||
|
expect(offscreen.painted, isFalse);
|
||||||
|
// Attach the offscreen to a custom render view and owner
|
||||||
|
RenderView renderView = new TestRenderView();
|
||||||
|
PipelineOwner pipelineOwner = new PipelineOwner();
|
||||||
|
renderView.attach(pipelineOwner);
|
||||||
|
renderView.child = offscreen.root;
|
||||||
|
renderView.scheduleInitialFrame();
|
||||||
|
// Lay out the offscreen
|
||||||
|
pipelineOwner.flushLayout();
|
||||||
|
expect(offscreen.child.hasSize, isTrue);
|
||||||
|
expect(offscreen.painted, isFalse);
|
||||||
|
// Make sure the onscreen didn't get laid out
|
||||||
|
expect(onscreen.child.hasSize, isFalse);
|
||||||
|
expect(onscreen.painted, isFalse);
|
||||||
|
// Now lay out the onscreen in the default binding
|
||||||
|
layout(onscreen.root, phase: EnginePhase.layout);
|
||||||
|
expect(onscreen.child.hasSize, isTrue);
|
||||||
|
expect(onscreen.painted, isFalse);
|
||||||
|
expect(onscreen.child.size, equals(const Size(800.0, 10.0)));
|
||||||
|
});
|
||||||
|
}
|
@ -41,16 +41,16 @@ class TestRenderingFlutterBinding extends BindingBase with Scheduler, Services,
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void beginFrame() {
|
void beginFrame() {
|
||||||
RenderObject.flushLayout();
|
pipelineOwner.flushLayout();
|
||||||
if (phase == EnginePhase.layout)
|
if (phase == EnginePhase.layout)
|
||||||
return;
|
return;
|
||||||
RenderObject.flushCompositingBits();
|
pipelineOwner.flushCompositingBits();
|
||||||
if (phase == EnginePhase.compositingBits)
|
if (phase == EnginePhase.compositingBits)
|
||||||
return;
|
return;
|
||||||
RenderObject.flushPaint();
|
pipelineOwner.flushPaint();
|
||||||
if (phase == EnginePhase.paint)
|
if (phase == EnginePhase.paint)
|
||||||
return;
|
return;
|
||||||
renderer.renderView.compositeFrame();
|
renderView.compositeFrame();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,8 +58,8 @@ class SpriteBox extends RenderBox {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void attach() {
|
void attach(PipelineOwner owner) {
|
||||||
super.attach();
|
super.attach(owner);
|
||||||
_scheduleTick();
|
_scheduleTick();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,20 +46,20 @@ class _SteppedWidgetFlutterBinding extends WidgetFlutterBinding {
|
|||||||
// Cloned from Renderer.beginFrame() but with early-exit semantics.
|
// Cloned from Renderer.beginFrame() but with early-exit semantics.
|
||||||
void _beginFrame() {
|
void _beginFrame() {
|
||||||
assert(renderView != null);
|
assert(renderView != null);
|
||||||
RenderObject.flushLayout();
|
pipelineOwner.flushLayout();
|
||||||
if (phase == EnginePhase.layout)
|
if (phase == EnginePhase.layout)
|
||||||
return;
|
return;
|
||||||
RenderObject.flushCompositingBits();
|
pipelineOwner.flushCompositingBits();
|
||||||
if (phase == EnginePhase.compositingBits)
|
if (phase == EnginePhase.compositingBits)
|
||||||
return;
|
return;
|
||||||
RenderObject.flushPaint();
|
pipelineOwner.flushPaint();
|
||||||
if (phase == EnginePhase.paint)
|
if (phase == EnginePhase.paint)
|
||||||
return;
|
return;
|
||||||
renderView.compositeFrame(); // this sends the bits to the GPU
|
renderView.compositeFrame(); // this sends the bits to the GPU
|
||||||
if (phase == EnginePhase.composite)
|
if (phase == EnginePhase.composite)
|
||||||
return;
|
return;
|
||||||
if (SemanticsNode.hasListeners) {
|
if (SemanticsNode.hasListeners) {
|
||||||
RenderObject.flushSemantics();
|
pipelineOwner.flushSemantics();
|
||||||
if (phase == EnginePhase.flushSemantics)
|
if (phase == EnginePhase.flushSemantics)
|
||||||
return;
|
return;
|
||||||
SemanticsNode.sendSemanticsTree();
|
SemanticsNode.sendSemanticsTree();
|
||||||
|
@ -347,6 +347,7 @@ class AnalyzeCommand extends FlutterCommand {
|
|||||||
new RegExp('^\\[(hint|error)\\] Unused import \\(${mainFile.path},'),
|
new RegExp('^\\[(hint|error)\\] Unused import \\(${mainFile.path},'),
|
||||||
new RegExp(r'^\[.+\] .+ \(.+/\.pub-cache/.+'),
|
new RegExp(r'^\[.+\] .+ \(.+/\.pub-cache/.+'),
|
||||||
new RegExp('\\[warning\\] Missing concrete implementation of \'RenderObject\\.applyPaintTransform\''), // https://github.com/dart-lang/sdk/issues/25232
|
new RegExp('\\[warning\\] Missing concrete implementation of \'RenderObject\\.applyPaintTransform\''), // https://github.com/dart-lang/sdk/issues/25232
|
||||||
|
new RegExp('\\[warning\\] Missing concrete implementation of \'AbstractNode\\.attach\''), // https://github.com/dart-lang/sdk/issues/25232
|
||||||
new RegExp(r'[0-9]+ (error|warning|hint|lint).+found\.'),
|
new RegExp(r'[0-9]+ (error|warning|hint|lint).+found\.'),
|
||||||
new RegExp(r'^$'),
|
new RegExp(r'^$'),
|
||||||
];
|
];
|
||||||
|
Loading…
Reference in New Issue
Block a user