Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
243960d7
Commit
243960d7
authored
Mar 31, 2016
by
krisgiesing
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #3010 from krisgiesing/offscreen_layout
Part 2 of independent layout pipelines
parents
4ff2a338
9dfd5d40
Changes
8
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
351 additions
and
145 deletions
+351
-145
spinning_mixed.dart
examples/layers/widgets/spinning_mixed.dart
+2
-1
build_bench.dart
packages/flutter/benchmark/stocks/build_bench.dart
+1
-1
binding.dart
packages/flutter/lib/src/widgets/binding.dart
+16
-59
framework.dart
packages/flutter/lib/src/widgets/framework.dart
+164
-79
mixed_viewport.dart
packages/flutter/lib/src/widgets/mixed_viewport.dart
+1
-1
virtual_viewport.dart
packages/flutter/lib/src/widgets/virtual_viewport.dart
+1
-1
independent_widget_layout_test.dart
...s/flutter/test/widget/independent_widget_layout_test.dart
+163
-0
widget_tester.dart
packages/flutter_test/lib/src/widget_tester.dart
+3
-3
No files found.
examples/layers/widgets/spinning_mixed.dart
View file @
243960d7
...
...
@@ -33,6 +33,7 @@ class Rectangle extends StatelessWidget {
double
value
;
RenderObjectToWidgetElement
<
RenderBox
>
element
;
BuildOwner
owner
;
void
attachWidgetTreeToRenderTree
(
RenderProxyBox
container
)
{
element
=
new
RenderObjectToWidgetAdapter
<
RenderBox
>(
container:
container
,
...
...
@@ -70,7 +71,7 @@ void attachWidgetTreeToRenderTree(RenderProxyBox container) {
mainAxisAlignment:
MainAxisAlignment
.
spaceBetween
)
)
).
attachToRenderTree
(
element
);
).
attachToRenderTree
(
owner
,
element
);
}
Duration
timeBase
;
...
...
packages/flutter/benchmark/stocks/build_bench.dart
View file @
243960d7
...
...
@@ -34,7 +34,7 @@ void main() {
for
(
int
i
=
0
;
i
<
_kNumberOfIterations
||
_kRunForever
;
++
i
)
{
appState
.
setState
(
_doNothing
);
binding
.
buildDirtyElements
();
binding
.
build
Owner
.
build
DirtyElements
();
}
watch
.
stop
();
...
...
packages/flutter/lib/src/widgets/binding.dart
View file @
243960d7
...
...
@@ -2,7 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'dart:developer'
;
import
'dart:ui'
as
ui
show
window
;
import
'dart:ui'
show
AppLifecycleState
,
Locale
;
...
...
@@ -26,6 +25,10 @@ class BindingObserver {
/// This is the glue that binds the framework to the Flutter engine.
class
WidgetFlutterBinding
extends
BindingBase
with
Scheduler
,
Gesturer
,
Services
,
Renderer
{
WidgetFlutterBinding
()
{
buildOwner
.
onBuildScheduled
=
ensureVisualUpdate
;
}
/// Creates and initializes the WidgetFlutterBinding. This constructor is
/// idempotent; calling it a second time will just return the
/// previously-created instance.
...
...
@@ -35,11 +38,15 @@ class WidgetFlutterBinding extends BindingBase with Scheduler, Gesturer, Service
return
_instance
;
}
final
BuildOwner
_buildOwner
=
new
BuildOwner
();
/// The [BuildOwner] in charge of executing the build pipeline for the
/// widget tree rooted at this binding.
BuildOwner
get
buildOwner
=>
_buildOwner
;
@override
void
initInstances
()
{
super
.
initInstances
();
_instance
=
this
;
BuildableElement
.
scheduleBuildFor
=
scheduleBuildFor
;
ui
.
window
.
onLocaleChanged
=
handleLocaleChanged
;
ui
.
window
.
onPopRoute
=
handlePopRoute
;
ui
.
window
.
onAppLifecycleStateChanged
=
handleAppLifecycleStateChanged
;
...
...
@@ -92,60 +99,9 @@ class WidgetFlutterBinding extends BindingBase with Scheduler, Gesturer, Service
@override
void
beginFrame
()
{
buildDirtyElements
();
build
Owner
.
build
DirtyElements
();
super
.
beginFrame
();
Element
.
finalizeTree
();
}
List
<
BuildableElement
>
_dirtyElements
=
<
BuildableElement
>[];
/// Adds an element to the dirty elements list so that it will be rebuilt
/// when buildDirtyElements is called.
void
scheduleBuildFor
(
BuildableElement
element
)
{
assert
(!
_dirtyElements
.
contains
(
element
));
assert
(
element
.
dirty
);
if
(
_dirtyElements
.
isEmpty
)
ensureVisualUpdate
();
_dirtyElements
.
add
(
element
);
}
static
int
_elementSort
(
BuildableElement
a
,
BuildableElement
b
)
{
if
(
a
.
depth
<
b
.
depth
)
return
-
1
;
if
(
b
.
depth
<
a
.
depth
)
return
1
;
if
(
b
.
dirty
&&
!
a
.
dirty
)
return
-
1
;
if
(
a
.
dirty
&&
!
b
.
dirty
)
return
1
;
return
0
;
}
/// Builds all the elements that were marked as dirty using schedule(), in depth order.
/// If elements are marked as dirty while this runs, they must be deeper than the algorithm
/// has yet reached.
/// This is called by beginFrame().
void
buildDirtyElements
()
{
if
(
_dirtyElements
.
isEmpty
)
return
;
Timeline
.
startSync
(
'Build'
);
BuildableElement
.
lockState
(()
{
_dirtyElements
.
sort
(
_elementSort
);
int
dirtyCount
=
_dirtyElements
.
length
;
int
index
=
0
;
while
(
index
<
dirtyCount
)
{
_dirtyElements
[
index
].
rebuild
();
index
+=
1
;
if
(
dirtyCount
<
_dirtyElements
.
length
)
{
_dirtyElements
.
sort
(
_elementSort
);
dirtyCount
=
_dirtyElements
.
length
;
}
}
assert
(!
_dirtyElements
.
any
((
BuildableElement
element
)
=>
element
.
dirty
));
_dirtyElements
.
clear
();
},
building:
true
);
assert
(
_dirtyElements
.
isEmpty
);
Timeline
.
finishSync
();
buildOwner
.
finalizeTree
();
}
/// The [Element] that is at the root of the hierarchy (and which wraps the
...
...
@@ -157,7 +113,7 @@ class WidgetFlutterBinding extends BindingBase with Scheduler, Gesturer, Service
container:
renderView
,
debugShortDescription:
'[root]'
,
child:
app
).
attachToRenderTree
(
_renderViewElement
);
).
attachToRenderTree
(
buildOwner
,
_renderViewElement
);
beginFrame
();
}
}
...
...
@@ -205,10 +161,11 @@ class RenderObjectToWidgetAdapter<T extends RenderObject> extends RenderObjectWi
@override
void
updateRenderObject
(
BuildContext
context
,
RenderObject
renderObject
)
{
}
RenderObjectToWidgetElement
<
T
>
attachToRenderTree
([
RenderObjectToWidgetElement
<
T
>
element
])
{
BuildableElement
.
lockState
(()
{
RenderObjectToWidgetElement
<
T
>
attachToRenderTree
(
BuildOwner
owner
,
[
RenderObjectToWidgetElement
<
T
>
element
])
{
owner
.
lockState
(()
{
if
(
element
==
null
)
{
element
=
createElement
();
element
.
assignOwner
(
owner
);
element
.
mount
(
null
,
null
);
}
else
{
element
.
update
(
this
);
...
...
@@ -229,7 +186,7 @@ class RenderObjectToWidgetAdapter<T extends RenderObject> extends RenderObjectWi
/// whose container is the RenderView that connects to the Flutter engine. In
/// this usage, it is normally instantiated by the bootstrapping logic in the
/// WidgetFlutterBinding singleton created by runApp().
class
RenderObjectToWidgetElement
<
T
extends
RenderObject
>
extends
RenderObjectElement
{
class
RenderObjectToWidgetElement
<
T
extends
RenderObject
>
extends
R
ootR
enderObjectElement
{
RenderObjectToWidgetElement
(
RenderObjectToWidgetAdapter
<
T
>
widget
)
:
super
(
widget
);
@override
...
...
packages/flutter/lib/src/widgets/framework.dart
View file @
243960d7
This diff is collapsed.
Click to expand it.
packages/flutter/lib/src/widgets/mixed_viewport.dart
View file @
243960d7
...
...
@@ -252,7 +252,7 @@ class _MixedViewportElement extends RenderObjectElement {
_resetCache
();
_lastLayoutConstraints
=
constraints
;
}
BuildableElement
.
lockState
(()
{
owner
.
lockState
(()
{
_doLayout
(
constraints
);
},
building:
true
);
}
...
...
packages/flutter/lib/src/widgets/virtual_viewport.dart
View file @
243960d7
...
...
@@ -157,7 +157,7 @@ abstract class VirtualViewportElement extends RenderObjectElement {
assert
(
startOffsetBase
!=
null
);
assert
(
startOffsetLimit
!=
null
);
_updatePaintOffset
();
BuildableElement
.
lockState
(
_materializeChildren
,
building:
true
);
owner
.
lockState
(
_materializeChildren
,
building:
true
);
}
void
_materializeChildren
()
{
...
...
packages/flutter/test/widget/independent_widget_layout_test.dart
0 → 100644
View file @
243960d7
// 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_test/flutter_test.dart'
;
import
'package:flutter/material.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:test/test.dart'
;
const
Size
_kTestViewSize
=
const
Size
(
800.0
,
600.0
);
class
OffscreenRenderView
extends
RenderView
{
OffscreenRenderView
()
{
configuration
=
new
ViewConfiguration
(
size:
_kTestViewSize
);
}
@override
void
scheduleInitialFrame
()
{
scheduleInitialLayout
();
scheduleInitialPaint
(
new
TransformLayer
(
transform:
new
Matrix4
.
identity
()));
// Don't call Scheduler.instance.ensureVisualUpdate()
}
@override
void
compositeFrame
()
{
// Don't draw to ui.window
}
}
class
OffscreenWidgetTree
{
OffscreenWidgetTree
()
{
renderView
.
attach
(
pipelineOwner
);
renderView
.
scheduleInitialFrame
();
}
final
RenderView
renderView
=
new
OffscreenRenderView
();
final
BuildOwner
buildOwner
=
new
BuildOwner
();
final
PipelineOwner
pipelineOwner
=
new
PipelineOwner
();
RenderObjectToWidgetElement
<
RenderBox
>
root
;
void
pumpWidget
(
Widget
app
)
{
root
=
new
RenderObjectToWidgetAdapter
<
RenderBox
>(
container:
renderView
,
debugShortDescription:
'[root]'
,
child:
app
).
attachToRenderTree
(
buildOwner
,
root
);
pumpFrame
();
}
void
pumpFrame
()
{
buildOwner
.
buildDirtyElements
();
pipelineOwner
.
flushLayout
();
pipelineOwner
.
flushCompositingBits
();
pipelineOwner
.
flushPaint
();
renderView
.
compositeFrame
();
if
(
SemanticsNode
.
hasListeners
)
{
pipelineOwner
.
flushSemantics
();
SemanticsNode
.
sendSemanticsTree
();
}
buildOwner
.
finalizeTree
();
}
}
class
Counter
{
int
count
=
0
;
}
class
Trigger
{
VoidCallback
callback
;
void
fire
()
{
if
(
callback
!=
null
)
callback
();
}
}
class
TriggerableWidget
extends
StatefulWidget
{
TriggerableWidget
({
this
.
trigger
,
this
.
counter
});
final
Trigger
trigger
;
final
Counter
counter
;
@override
TriggerableState
createState
()
=>
new
TriggerableState
();
}
class
TriggerableState
extends
State
<
TriggerableWidget
>
{
@override
void
initState
()
{
super
.
initState
();
config
.
trigger
.
callback
=
this
.
fire
;
}
@override
void
didUpdateConfig
(
TriggerableWidget
oldConfig
)
{
config
.
trigger
.
callback
=
this
.
fire
;
}
int
_count
=
0
;
void
fire
()
{
setState
(()
{
_count
++;
});
}
@override
Widget
build
(
BuildContext
context
)
{
config
.
counter
.
count
++;
return
new
Text
(
"Bang
$_count
!"
);
}
}
void
main
(
)
{
test
(
'no crosstalk between widget build owners'
,
()
{
testWidgets
((
WidgetTester
tester
)
{
Trigger
trigger1
=
new
Trigger
();
Counter
counter1
=
new
Counter
();
Trigger
trigger2
=
new
Trigger
();
Counter
counter2
=
new
Counter
();
OffscreenWidgetTree
tree
=
new
OffscreenWidgetTree
();
// Both counts should start at zero
expect
(
counter1
.
count
,
equals
(
0
));
expect
(
counter2
.
count
,
equals
(
0
));
// Lay out the "onscreen" in the default test binding
tester
.
pumpWidget
(
new
TriggerableWidget
(
trigger:
trigger1
,
counter:
counter1
));
// Only the "onscreen" widget should have built
expect
(
counter1
.
count
,
equals
(
1
));
expect
(
counter2
.
count
,
equals
(
0
));
// Lay out the "offscreen" in a separate tree
tree
.
pumpWidget
(
new
TriggerableWidget
(
trigger:
trigger2
,
counter:
counter2
));
// Now both widgets should have built
expect
(
counter1
.
count
,
equals
(
1
));
expect
(
counter2
.
count
,
equals
(
1
));
// Mark both as needing layout
trigger1
.
fire
();
trigger2
.
fire
();
// Marking as needing layout shouldn't immediately build anything
expect
(
counter1
.
count
,
equals
(
1
));
expect
(
counter2
.
count
,
equals
(
1
));
// Pump the "onscreen" layout
tester
.
pump
();
// Only the "onscreen" widget should have rebuilt
expect
(
counter1
.
count
,
equals
(
2
));
expect
(
counter2
.
count
,
equals
(
1
));
// Pump the "offscreen" layout
tree
.
pumpFrame
();
// Now both widgets should have rebuilt
expect
(
counter1
.
count
,
equals
(
2
));
expect
(
counter2
.
count
,
equals
(
2
));
// Mark both as needing layout, again
trigger1
.
fire
();
trigger2
.
fire
();
// Now pump the "offscreen" layout first
tree
.
pumpFrame
();
// Only the "offscreen" widget should have rebuilt
expect
(
counter1
.
count
,
equals
(
2
));
expect
(
counter2
.
count
,
equals
(
3
));
// Pump the "onscreen" layout
tester
.
pump
();
// Now both widgets should have rebuilt
expect
(
counter1
.
count
,
equals
(
3
));
expect
(
counter2
.
count
,
equals
(
3
));
});
});
}
packages/flutter_test/lib/src/widget_tester.dart
View file @
243960d7
...
...
@@ -38,9 +38,9 @@ class _SteppedWidgetFlutterBinding extends WidgetFlutterBinding {
// Pump the rendering pipeline up to the given phase.
@override
void
beginFrame
()
{
buildDirtyElements
();
build
Owner
.
build
DirtyElements
();
_beginFrame
();
Element
.
finalizeTree
();
buildOwner
.
finalizeTree
();
}
// Cloned from Renderer.beginFrame() but with early-exit semantics.
...
...
@@ -84,7 +84,7 @@ class WidgetTester extends Instrumentation {
final
FakeAsync
async
;
final
Clock
clock
;
/// Calls [runApp()] with the given widget, then triggers a frame sequen
t
and
/// Calls [runApp()] with the given widget, then triggers a frame sequen
ce
and
/// flushes microtasks, by calling [pump()] with the same duration (if any).
/// The supplied EnginePhase is the final phase reached during the pump pass;
/// if not supplied, the whole pass is executed.
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment