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
1dd24b8a
Unverified
Commit
1dd24b8a
authored
Mar 01, 2022
by
Jonah Williams
Committed by
GitHub
Mar 01, 2022
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[framework] improve Notification API performance by skipping full Element tree traversal (#98451)
parent
38dbbb17
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
124 additions
and
54 deletions
+124
-54
framework.dart
packages/flutter/lib/src/widgets/framework.dart
+63
-0
notification_listener.dart
packages/flutter/lib/src/widgets/notification_listener.dart
+26
-43
scroll_notification.dart
packages/flutter/lib/src/widgets/scroll_notification.dart
+16
-7
single_child_scroll_view.dart
...ges/flutter/lib/src/widgets/single_child_scroll_view.dart
+9
-0
size_changed_layout_notifier.dart
...flutter/lib/src/widgets/size_changed_layout_notifier.dart
+5
-2
viewport.dart
packages/flutter/lib/src/widgets/viewport.dart
+4
-1
material_test.dart
packages/flutter/test/material/material_test.dart
+1
-1
No files found.
packages/flutter/lib/src/widgets/framework.dart
View file @
1dd24b8a
...
...
@@ -13,6 +13,7 @@ import 'binding.dart';
import
'debug.dart'
;
import
'focus_manager.dart'
;
import
'inherited_model.dart'
;
import
'notification_listener.dart'
;
export
'package:flutter/foundation.dart'
show
factory
,
...
...
@@ -2390,6 +2391,13 @@ abstract class BuildContext {
/// data down to them.
void
visitChildElements
(
ElementVisitor
visitor
);
/// Start bubbling this notification at the given build context.
///
/// The notification will be delivered to any [NotificationListener] widgets
/// with the appropriate type parameters that are ancestors of the given
/// [BuildContext].
void
dispatchNotification
(
Notification
notification
);
/// Returns a description of the [Element] associated with the current build context.
///
/// The `name` is typically something like "The element being rebuilt was".
...
...
@@ -3101,6 +3109,39 @@ class BuildOwner {
}
}
/// Mixin this class to allow receiving [Notification] objects dispatched by
/// child elements.
///
/// See also:
/// * [NotificationListener], for a widget that allows consuming notifications.
mixin
NotifiableElementMixin
on
Element
{
/// Called when a notification of the appropriate type arrives at this
/// location in the tree.
///
/// Return true to cancel the notification bubbling. Return false to
/// allow the notification to continue to be dispatched to further ancestors.
bool
onNotification
(
Notification
notification
);
@override
void
attachNotificationTree
()
{
_notificationTree
=
_NotificationNode
(
_parent
?.
_notificationTree
,
this
);
}
}
class
_NotificationNode
{
_NotificationNode
(
this
.
parent
,
this
.
current
);
NotifiableElementMixin
?
current
;
_NotificationNode
?
parent
;
void
dispatchNotification
(
Notification
notification
)
{
if
(
current
?.
onNotification
(
notification
)
??
true
)
{
return
;
}
parent
?.
dispatchNotification
(
notification
);
}
}
/// An instantiation of a [Widget] at a particular location in the tree.
///
/// Widgets describe how to configure a subtree but the same widget can be used
...
...
@@ -3161,6 +3202,7 @@ abstract class Element extends DiagnosticableTree implements BuildContext {
Element
?
_parent
;
DebugReassembleConfig
?
_debugReassembleConfig
;
_NotificationNode
?
_notificationTree
;
/// Compare two widgets for equality.
///
...
...
@@ -3614,6 +3656,7 @@ abstract class Element extends DiagnosticableTree implements BuildContext {
owner
!.
_registerGlobalKey
(
key
,
this
);
}
_updateInheritance
();
attachNotificationTree
();
}
void
_debugRemoveGlobalKeyReservation
(
Element
child
)
{
...
...
@@ -3950,6 +3993,7 @@ abstract class Element extends DiagnosticableTree implements BuildContext {
_dependencies
?.
clear
();
_hadUnsatisfiedDependencies
=
false
;
_updateInheritance
();
attachNotificationTree
();
if
(
_dirty
)
owner
!.
scheduleBuildFor
(
this
);
if
(
hadDependencies
)
...
...
@@ -4231,6 +4275,20 @@ abstract class Element extends DiagnosticableTree implements BuildContext {
return
ancestor
;
}
/// Called in [Element.mount] and [Element.activate] to register this element in
/// the notification tree.
///
/// This method is only exposed so that [NotifiableElementMixin] can be implemented.
/// Subclasses of [Element] that wish to respond to notifications should mix that
/// in instead.
///
/// See also:
/// * [NotificationListener], a widget that allows listening to notifications.
@protected
void
attachNotificationTree
()
{
_notificationTree
=
_parent
?.
_notificationTree
;
}
void
_updateInheritance
()
{
assert
(
_lifecycleState
==
_ElementLifecycle
.
active
);
_inheritedLookup
=
_parent
?.
_inheritedLookup
;
...
...
@@ -4359,6 +4417,11 @@ abstract class Element extends DiagnosticableTree implements BuildContext {
return
chain
;
}
@override
void
dispatchNotification
(
Notification
notification
)
{
_notificationTree
?.
dispatchNotification
(
notification
);
}
/// A short, textual description of this element.
@override
String
toStringShort
()
=>
_widget
?.
toStringShort
()
??
'
${describeIdentity(this)}
(DEFUNCT)'
;
...
...
packages/flutter/lib/src/widgets/notification_listener.dart
View file @
1dd24b8a
...
...
@@ -50,27 +50,6 @@ abstract class Notification {
/// const constructors so that they can be used in const expressions.
const
Notification
();
/// Applied to each ancestor of the [dispatch] target.
///
/// The [Notification] class implementation of this method dispatches the
/// given [Notification] to each ancestor [NotificationListener] widget.
///
/// Subclasses can override this to apply additional filtering or to update
/// the notification as it is bubbled (for example, increasing a `depth` field
/// for each ancestor of a particular type).
@protected
@mustCallSuper
bool
visitAncestor
(
Element
element
)
{
if
(
element
is
StatelessElement
)
{
final
Widget
widget
=
element
.
widget
;
if
(
widget
is
NotificationListener
<
Notification
>)
{
if
(
widget
.
_dispatch
(
this
,
element
))
// that function checks the type dynamically
return
false
;
}
}
return
true
;
}
/// Start bubbling this notification at the given build context.
///
/// The notification will be delivered to any [NotificationListener] widgets
...
...
@@ -78,9 +57,7 @@ abstract class Notification {
/// [BuildContext]. If the [BuildContext] is null, the notification is not
/// dispatched.
void
dispatch
(
BuildContext
?
target
)
{
// The `target` may be null if the subtree the notification is supposed to be
// dispatched in is in the process of being disposed.
target
?.
visitAncestorElements
(
visitAncestor
);
target
?.
dispatchNotification
(
this
);
}
@override
...
...
@@ -112,20 +89,13 @@ abstract class Notification {
/// [runtimeType] is a subtype of `T`.
///
/// To dispatch notifications, use the [Notification.dispatch] method.
class
NotificationListener
<
T
extends
Notification
>
extends
Stateless
Widget
{
class
NotificationListener
<
T
extends
Notification
>
extends
Proxy
Widget
{
/// Creates a widget that listens for notifications.
const
NotificationListener
({
Key
?
key
,
required
this
.
child
,
required
Widget
child
,
this
.
onNotification
,
})
:
super
(
key:
key
);
/// The widget directly below this widget in the tree.
///
/// This is not necessarily the widget that dispatched the notification.
///
/// {@macro flutter.widgets.ProxyWidget.child}
final
Widget
child
;
})
:
super
(
key:
key
,
child:
child
);
/// Called when a notification of the appropriate type arrives at this
/// location in the tree.
...
...
@@ -133,9 +103,6 @@ class NotificationListener<T extends Notification> extends StatelessWidget {
/// Return true to cancel the notification bubbling. Return false to
/// allow the notification to continue to be dispatched to further ancestors.
///
/// The notification's [Notification.visitAncestor] method is called for each
/// ancestor, and invokes this callback as appropriate.
///
/// Notifications vary in terms of when they are dispatched. There are two
/// main possibilities: dispatch between frames, and dispatch during layout.
///
...
...
@@ -146,16 +113,29 @@ class NotificationListener<T extends Notification> extends StatelessWidget {
/// widgets that depend on layout, consider a [LayoutBuilder] instead.
final
NotificationListenerCallback
<
T
>?
onNotification
;
bool
_dispatch
(
Notification
notification
,
Element
element
)
{
if
(
onNotification
!=
null
&&
notification
is
T
)
{
final
bool
result
=
onNotification
!(
notification
);
return
result
==
true
;
// so that null and false have the same effect
@override
Element
createElement
()
{
return
_NotificationElement
<
T
>(
this
);
}
}
/// An element used to host [NotificationListener] elements.
class
_NotificationElement
<
T
extends
Notification
>
extends
ProxyElement
with
NotifiableElementMixin
{
_NotificationElement
(
NotificationListener
<
T
>
widget
)
:
super
(
widget
);
@override
bool
onNotification
(
Notification
notification
)
{
final
NotificationListener
<
T
>
listener
=
widget
as
NotificationListener
<
T
>;
if
(
listener
.
onNotification
!=
null
&&
notification
is
T
)
{
return
listener
.
onNotification
!(
notification
);
}
return
false
;
}
@override
Widget
build
(
BuildContext
context
)
=>
child
;
void
notifyClients
(
covariant
ProxyWidget
oldWidget
)
{
// Notification tree does not need to notify clients.
}
}
/// Indicates that the layout of one of the descendants of the object receiving
...
...
@@ -186,4 +166,7 @@ class NotificationListener<T extends Notification> extends StatelessWidget {
/// useful for paint effects that depend on the layout. If you were to use this
/// notification to change the build, for instance, you would always be one
/// frame behind, which would look really ugly and laggy.
class
LayoutChangedNotification
extends
Notification
{
}
class
LayoutChangedNotification
extends
Notification
{
/// Create a new [LayoutChangedNotification].
const
LayoutChangedNotification
();
}
packages/flutter/lib/src/widgets/scroll_notification.dart
View file @
1dd24b8a
...
...
@@ -24,13 +24,6 @@ mixin ViewportNotificationMixin on Notification {
int
get
depth
=>
_depth
;
int
_depth
=
0
;
@override
bool
visitAncestor
(
Element
element
)
{
if
(
element
is
RenderObjectElement
&&
element
.
renderObject
is
RenderAbstractViewport
)
_depth
+=
1
;
return
super
.
visitAncestor
(
element
);
}
@override
void
debugFillDescription
(
List
<
String
>
description
)
{
super
.
debugFillDescription
(
description
);
...
...
@@ -38,6 +31,22 @@ mixin ViewportNotificationMixin on Notification {
}
}
/// A mixin that allows [Element]s containing [Viewport] like widgets to correctly
/// modify the notification depth of a [ViewportNotificationMixin].
///
/// See also:
/// * [Viewport], which creates a custom [MultiChildRenderObjectElement] that mixes
/// this in.
mixin
ViewportElementMixin
on
NotifiableElementMixin
{
@override
bool
onNotification
(
Notification
notification
)
{
if
(
notification
is
ViewportNotificationMixin
)
{
notification
.
_depth
+=
1
;
}
return
false
;
}
}
/// A [Notification] related to scrolling.
///
/// [Scrollable] widgets notify their ancestors about scrolling-related changes.
...
...
packages/flutter/lib/src/widgets/single_child_scroll_view.dart
View file @
1dd24b8a
...
...
@@ -318,6 +318,15 @@ class _SingleChildViewport extends SingleChildRenderObjectWidget {
..
offset
=
offset
..
clipBehavior
=
clipBehavior
;
}
@override
SingleChildRenderObjectElement
createElement
()
{
return
_SingleChildViewportElement
(
this
);
}
}
class
_SingleChildViewportElement
extends
SingleChildRenderObjectElement
with
NotifiableElementMixin
,
ViewportElementMixin
{
_SingleChildViewportElement
(
_SingleChildViewport
widget
)
:
super
(
widget
);
}
class
_RenderSingleChildViewport
extends
RenderBox
with
RenderObjectWithChildMixin
<
RenderBox
>
implements
RenderAbstractViewport
{
...
...
packages/flutter/lib/src/widgets/size_changed_layout_notifier.dart
View file @
1dd24b8a
...
...
@@ -28,7 +28,10 @@ import 'notification_listener.dart';
///
/// * [SizeChangedLayoutNotifier], which sends this notification.
/// * [LayoutChangedNotification], of which this is a subclass.
class
SizeChangedLayoutNotification
extends
LayoutChangedNotification
{
}
class
SizeChangedLayoutNotification
extends
LayoutChangedNotification
{
/// Create a new [SizeChangedLayoutNotification].
const
SizeChangedLayoutNotification
();
}
/// A widget that automatically dispatches a [SizeChangedLayoutNotification]
/// when the layout dimensions of its child change.
...
...
@@ -61,7 +64,7 @@ class SizeChangedLayoutNotifier extends SingleChildRenderObjectWidget {
RenderObject
createRenderObject
(
BuildContext
context
)
{
return
_RenderSizeChangedWithCallback
(
onLayoutChangedCallback:
()
{
SizeChangedLayoutNotification
().
dispatch
(
context
);
const
SizeChangedLayoutNotification
().
dispatch
(
context
);
},
);
}
...
...
packages/flutter/lib/src/widgets/viewport.dart
View file @
1dd24b8a
...
...
@@ -7,6 +7,7 @@ import 'package:flutter/rendering.dart';
import
'basic.dart'
;
import
'debug.dart'
;
import
'framework.dart'
;
import
'scroll_notification.dart'
;
export
'package:flutter/rendering.dart'
show
AxisDirection
,
...
...
@@ -43,6 +44,8 @@ export 'package:flutter/rendering.dart' show
/// sliver context (the opposite of this widget).
/// * [ShrinkWrappingViewport], a variant of [Viewport] that shrink-wraps its
/// contents along the main axis.
/// * [ViewportElementMixin], which should be mixed in to the [Element] type used
/// by viewport-like widgets to correctly handle scroll notifications.
class
Viewport
extends
MultiChildRenderObjectWidget
{
/// Creates a widget that is bigger on the inside.
///
...
...
@@ -207,7 +210,7 @@ class Viewport extends MultiChildRenderObjectWidget {
}
}
class
_ViewportElement
extends
MultiChildRenderObjectElement
{
class
_ViewportElement
extends
MultiChildRenderObjectElement
with
NotifiableElementMixin
,
ViewportElementMixin
{
/// Creates an element that uses the given widget as its configuration.
_ViewportElement
(
Viewport
widget
)
:
super
(
widget
);
...
...
packages/flutter/test/material/material_test.dart
View file @
1dd24b8a
...
...
@@ -17,7 +17,7 @@ class NotifyMaterial extends StatelessWidget {
const
NotifyMaterial
({
Key
?
key
})
:
super
(
key:
key
);
@override
Widget
build
(
BuildContext
context
)
{
LayoutChangedNotification
().
dispatch
(
context
);
const
LayoutChangedNotification
().
dispatch
(
context
);
return
Container
();
}
}
...
...
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