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
efc07965
Unverified
Commit
efc07965
authored
May 07, 2021
by
Todd Volkert
Committed by
GitHub
May 07, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Allow reuse of NavigatorObserver in Navigator.observers (#81601)
parent
04bb954a
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
139 additions
and
5 deletions
+139
-5
framework.dart
packages/flutter/lib/src/widgets/framework.dart
+40
-3
navigator.dart
packages/flutter/lib/src/widgets/navigator.dart
+21
-2
framework_test.dart
packages/flutter/test/widgets/framework_test.dart
+42
-0
navigator_test.dart
packages/flutter/test/widgets/navigator_test.dart
+36
-0
No files found.
packages/flutter/lib/src/widgets/framework.dart
View file @
efc07965
...
@@ -1113,9 +1113,11 @@ abstract class State<T extends StatefulWidget> with Diagnosticable {
...
@@ -1113,9 +1113,11 @@ abstract class State<T extends StatefulWidget> with Diagnosticable {
/// The framework calls this method whenever it removes this [State] object
/// The framework calls this method whenever it removes this [State] object
/// from the tree. In some cases, the framework will reinsert the [State]
/// from the tree. In some cases, the framework will reinsert the [State]
/// object into another part of the tree (e.g., if the subtree containing this
/// object into another part of the tree (e.g., if the subtree containing this
/// [State] object is grafted from one location in the tree to another). If
/// [State] object is grafted from one location in the tree to another due to
/// that happens, the framework will ensure that it calls [build] to give the
/// the use of a [GlobalKey]). If that happens, the framework will call
/// [State] object a chance to adapt to its new location in the tree. If
/// [activate] to give the [State] object a chance to reacquire any resources
/// that it released in [deactivate]. It will then also call [build] to give
/// the [State] object a chance to adapt to its new location in the tree. If
/// the framework does reinsert this subtree, it will do so before the end of
/// the framework does reinsert this subtree, it will do so before the end of
/// the animation frame in which the subtree was removed from the tree. For
/// the animation frame in which the subtree was removed from the tree. For
/// this reason, [State] objects can defer releasing most resources until the
/// this reason, [State] objects can defer releasing most resources until the
...
@@ -1136,6 +1138,40 @@ abstract class State<T extends StatefulWidget> with Diagnosticable {
...
@@ -1136,6 +1138,40 @@ abstract class State<T extends StatefulWidget> with Diagnosticable {
@mustCallSuper
@mustCallSuper
void
deactivate
()
{
}
void
deactivate
()
{
}
/// Called when this object is reinserted into the tree after having been
/// removed via [deactivate].
///
/// In most cases, after a [State] object has been deactivated, it is _not_
/// reinserted into the tree, and its [dispose] method will be called to
/// signal that it is ready to be garbage collected.
///
/// In some cases, however, after a [State] object has been deactivated, the
/// framework will reinsert it into another part of the tree (e.g., if the
/// subtree containing this [State] object is grafted from one location in
/// the tree to another due to the use of a [GlobalKey]). If that happens,
/// the framework will call [activate] to give the [State] object a chance to
/// reacquire any resources that it released in [deactivate]. It will then
/// also call [build] to give the object a chance to adapt to its new
/// location in the tree. If the framework does reinsert this subtree, it
/// will do so before the end of the animation frame in which the subtree was
/// removed from the tree. For this reason, [State] objects can defer
/// releasing most resources until the framework calls their [dispose] method.
///
/// The framework does not call this method the first time a [State] object
/// is inserted into the tree. Instead, the framework calls [initState] in
/// that situation.
///
/// Implementations of this method should start with a call to the inherited
/// method, as in `super.activate()`.
///
/// See also:
///
/// * [Element.activate], the corresponding method when an element
/// transitions from the "inactive" to the "active" lifecycle state.
@protected
@mustCallSuper
void
activate
()
{
}
/// Called when this object is removed from the tree permanently.
/// Called when this object is removed from the tree permanently.
///
///
/// The framework calls this method when this [State] object will never
/// The framework calls this method when this [State] object will never
...
@@ -4804,6 +4840,7 @@ class StatefulElement extends ComponentElement {
...
@@ -4804,6 +4840,7 @@ class StatefulElement extends ComponentElement {
@override
@override
void
activate
()
{
void
activate
()
{
super
.
activate
();
super
.
activate
();
state
.
activate
();
// Since the State could have observed the deactivate() and thus disposed of
// Since the State could have observed the deactivate() and thus disposed of
// resources allocated in the build method, we have to rebuild the widget
// resources allocated in the build method, we have to rebuild the widget
// so that its State can reallocate its resources.
// so that its State can reallocate its resources.
...
...
packages/flutter/lib/src/widgets/navigator.dart
View file @
efc07965
...
@@ -3628,6 +3628,22 @@ class NavigatorState extends State<Navigator> with TickerProviderStateMixin, Res
...
@@ -3628,6 +3628,22 @@ class NavigatorState extends State<Navigator> with TickerProviderStateMixin, Res
}());
}());
}
}
@override
void
deactivate
()
{
for
(
final
NavigatorObserver
observer
in
_effectiveObservers
)
observer
.
_navigator
=
null
;
super
.
deactivate
();
}
@override
void
activate
()
{
super
.
activate
();
for
(
final
NavigatorObserver
observer
in
_effectiveObservers
)
{
assert
(
observer
.
navigator
==
null
);
observer
.
_navigator
=
this
;
}
}
@override
@override
void
dispose
()
{
void
dispose
()
{
assert
(!
_debugLocked
);
assert
(!
_debugLocked
);
...
@@ -3635,9 +3651,12 @@ class NavigatorState extends State<Navigator> with TickerProviderStateMixin, Res
...
@@ -3635,9 +3651,12 @@ class NavigatorState extends State<Navigator> with TickerProviderStateMixin, Res
_debugLocked
=
true
;
_debugLocked
=
true
;
return
true
;
return
true
;
}());
}());
assert
(()
{
for
(
final
NavigatorObserver
observer
in
_effectiveObservers
)
assert
(
observer
.
_navigator
!=
this
);
return
true
;
}());
_updateHeroController
(
null
);
_updateHeroController
(
null
);
for
(
final
NavigatorObserver
observer
in
_effectiveObservers
)
observer
.
_navigator
=
null
;
focusScopeNode
.
dispose
();
focusScopeNode
.
dispose
();
for
(
final
_RouteEntry
entry
in
_history
)
for
(
final
_RouteEntry
entry
in
_history
)
entry
.
dispose
();
entry
.
dispose
();
...
...
packages/flutter/test/widgets/framework_test.dart
View file @
efc07965
...
@@ -1589,6 +1589,40 @@ void main() {
...
@@ -1589,6 +1589,40 @@ void main() {
expect
(()
=>
element
.
state
,
throwsA
(
isA
<
TypeError
>()));
expect
(()
=>
element
.
state
,
throwsA
(
isA
<
TypeError
>()));
expect
(()
=>
element
.
widget
,
throwsA
(
isA
<
TypeError
>()));
expect
(()
=>
element
.
widget
,
throwsA
(
isA
<
TypeError
>()));
},
skip:
kIsWeb
);
},
skip:
kIsWeb
);
testWidgets
(
'Deactivate and activate are called correctly'
,
(
WidgetTester
tester
)
async
{
final
List
<
String
>
states
=
<
String
>[];
Widget
build
([
Key
?
key
])
{
return
StatefulWidgetSpy
(
key:
key
,
onInitState:
(
BuildContext
context
)
{
states
.
add
(
'initState'
);
},
onDidUpdateWidget:
(
BuildContext
context
)
{
states
.
add
(
'didUpdateWidget'
);
},
onDeactivate:
(
BuildContext
context
)
{
states
.
add
(
'deactivate'
);
},
onActivate:
(
BuildContext
context
)
{
states
.
add
(
'activate'
);
},
onBuild:
(
BuildContext
context
)
{
states
.
add
(
'build'
);
},
onDispose:
(
BuildContext
context
)
{
states
.
add
(
'dispose'
);
},
);
}
Future
<
void
>
pumpWidget
(
Widget
widget
)
{
states
.
clear
();
return
tester
.
pumpWidget
(
widget
);
}
await
pumpWidget
(
build
());
expect
(
states
,
<
String
>[
'initState'
,
'build'
]);
await
pumpWidget
(
Container
(
child:
build
()));
expect
(
states
,
<
String
>[
'deactivate'
,
'initState'
,
'build'
,
'dispose'
]);
await
pumpWidget
(
Container
());
expect
(
states
,
<
String
>[
'deactivate'
,
'dispose'
]);
final
GlobalKey
key
=
GlobalKey
();
await
pumpWidget
(
build
(
key
));
expect
(
states
,
<
String
>[
'initState'
,
'build'
]);
await
pumpWidget
(
Container
(
child:
build
(
key
)));
expect
(
states
,
<
String
>[
'deactivate'
,
'activate'
,
'didUpdateWidget'
,
'build'
]);
await
pumpWidget
(
Container
());
expect
(
states
,
<
String
>[
'deactivate'
,
'dispose'
]);
});
}
}
class
_WidgetWithNoVisitChildren
extends
StatelessWidget
{
class
_WidgetWithNoVisitChildren
extends
StatelessWidget
{
...
@@ -1827,6 +1861,7 @@ class StatefulWidgetSpy extends StatefulWidget {
...
@@ -1827,6 +1861,7 @@ class StatefulWidgetSpy extends StatefulWidget {
this
.
onDidChangeDependencies
,
this
.
onDidChangeDependencies
,
this
.
onDispose
,
this
.
onDispose
,
this
.
onDeactivate
,
this
.
onDeactivate
,
this
.
onActivate
,
this
.
onDidUpdateWidget
,
this
.
onDidUpdateWidget
,
})
:
super
(
key:
key
);
})
:
super
(
key:
key
);
...
@@ -1835,6 +1870,7 @@ class StatefulWidgetSpy extends StatefulWidget {
...
@@ -1835,6 +1870,7 @@ class StatefulWidgetSpy extends StatefulWidget {
final
void
Function
(
BuildContext
)?
onDidChangeDependencies
;
final
void
Function
(
BuildContext
)?
onDidChangeDependencies
;
final
void
Function
(
BuildContext
)?
onDispose
;
final
void
Function
(
BuildContext
)?
onDispose
;
final
void
Function
(
BuildContext
)?
onDeactivate
;
final
void
Function
(
BuildContext
)?
onDeactivate
;
final
void
Function
(
BuildContext
)?
onActivate
;
final
void
Function
(
BuildContext
)?
onDidUpdateWidget
;
final
void
Function
(
BuildContext
)?
onDidUpdateWidget
;
@override
@override
...
@@ -1854,6 +1890,12 @@ class _StatefulWidgetSpyState extends State<StatefulWidgetSpy> {
...
@@ -1854,6 +1890,12 @@ class _StatefulWidgetSpyState extends State<StatefulWidgetSpy> {
widget
.
onDeactivate
?.
call
(
context
);
widget
.
onDeactivate
?.
call
(
context
);
}
}
@override
void
activate
()
{
super
.
activate
();
widget
.
onActivate
?.
call
(
context
);
}
@override
@override
void
dispose
()
{
void
dispose
()
{
super
.
dispose
();
super
.
dispose
();
...
...
packages/flutter/test/widgets/navigator_test.dart
View file @
efc07965
...
@@ -3560,6 +3560,42 @@ void main() {
...
@@ -3560,6 +3560,42 @@ void main() {
expect
(
observations
[
7
].
previous
,
isNull
);
expect
(
observations
[
7
].
previous
,
isNull
);
});
});
});
});
testWidgets
(
'Can reuse NavigatorObserver in rebuilt tree'
,
(
WidgetTester
tester
)
async
{
final
NavigatorObserver
observer
=
NavigatorObserver
();
Widget
build
([
Key
?
key
])
{
return
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
Navigator
(
key:
key
,
observers:
<
NavigatorObserver
>[
observer
],
onGenerateRoute:
(
RouteSettings
settings
)
{
return
PageRouteBuilder
<
void
>(
settings:
settings
,
pageBuilder:
(
BuildContext
_
,
Animation
<
double
>
__
,
Animation
<
double
>
___
)
{
return
Container
();
},
);
},
),
);
}
// Test without reinsertion
await
tester
.
pumpWidget
(
build
());
await
tester
.
pumpWidget
(
Container
(
child:
build
()));
expect
(
observer
.
navigator
,
tester
.
state
<
NavigatorState
>(
find
.
byType
(
Navigator
)));
// Clear the tree
await
tester
.
pumpWidget
(
Container
());
expect
(
observer
.
navigator
,
isNull
);
// Test with reinsertion
final
GlobalKey
key
=
GlobalKey
();
await
tester
.
pumpWidget
(
build
(
key
));
await
tester
.
pumpWidget
(
Container
(
child:
build
(
key
)));
expect
(
observer
.
navigator
,
tester
.
state
<
NavigatorState
>(
find
.
byType
(
Navigator
)));
});
}
}
typedef
AnnouncementCallBack
=
void
Function
(
Route
<
dynamic
>?);
typedef
AnnouncementCallBack
=
void
Function
(
Route
<
dynamic
>?);
...
...
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