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
fc85492d
Unverified
Commit
fc85492d
authored
Oct 02, 2020
by
Michael Goderbauer
Committed by
GitHub
Oct 02, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Make Navigator restorable (inkl. WidgetsApp, MaterialApp, CupertinoApp) (#65658)
parent
5d6321b5
Changes
13
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
3171 additions
and
170 deletions
+3171
-170
app.dart
packages/flutter/lib/src/cupertino/app.dart
+7
-0
app.dart
packages/flutter/lib/src/material/app.dart
+7
-0
app.dart
packages/flutter/lib/src/widgets/app.dart
+37
-12
navigator.dart
packages/flutter/lib/src/widgets/navigator.dart
+1712
-68
restoration.dart
packages/flutter/lib/src/widgets/restoration.dart
+9
-9
routes.dart
packages/flutter/lib/src/widgets/routes.dart
+56
-45
tab_test.dart
packages/flutter/test/cupertino/tab_test.dart
+4
-0
text_field_restoration_test.dart
...s/flutter/test/cupertino/text_field_restoration_test.dart
+15
-17
app_test.dart
packages/flutter/test/material/app_test.dart
+4
-0
debug_test.dart
packages/flutter/test/material/debug_test.dart
+8
-0
text_field_restoration_test.dart
...es/flutter/test/material/text_field_restoration_test.dart
+15
-17
navigator_restoration_test.dart
...ages/flutter/test/widgets/navigator_restoration_test.dart
+1295
-0
navigator_test.dart
packages/flutter/test/widgets/navigator_test.dart
+2
-2
No files found.
packages/flutter/lib/src/cupertino/app.dart
View file @
fc85492d
...
...
@@ -94,6 +94,7 @@ class CupertinoApp extends StatefulWidget {
this
.
debugShowCheckedModeBanner
=
true
,
this
.
shortcuts
,
this
.
actions
,
this
.
restorationScopeId
,
})
:
assert
(
routes
!=
null
),
assert
(
navigatorObservers
!=
null
),
assert
(
title
!=
null
),
...
...
@@ -132,6 +133,7 @@ class CupertinoApp extends StatefulWidget {
this
.
debugShowCheckedModeBanner
=
true
,
this
.
shortcuts
,
this
.
actions
,
this
.
restorationScopeId
,
})
:
assert
(
title
!=
null
),
assert
(
showPerformanceOverlay
!=
null
),
assert
(
checkerboardRasterCacheImages
!=
null
),
...
...
@@ -315,6 +317,9 @@ class CupertinoApp extends StatefulWidget {
/// {@macro flutter.widgets.widgetsApp.actions.seeAlso}
final
Map
<
Type
,
Action
<
Intent
>>?
actions
;
/// {@macro flutter.widgets.widgetsApp.restorationScopeId}
final
String
?
restorationScopeId
;
@override
_CupertinoAppState
createState
()
=>
_CupertinoAppState
();
...
...
@@ -400,6 +405,7 @@ class _CupertinoAppState extends State<CupertinoApp> {
inspectorSelectButtonBuilder:
_inspectorSelectButtonBuilder
,
shortcuts:
widget
.
shortcuts
,
actions:
widget
.
actions
,
restorationScopeId:
widget
.
restorationScopeId
,
);
}
return
WidgetsApp
(
...
...
@@ -433,6 +439,7 @@ class _CupertinoAppState extends State<CupertinoApp> {
inspectorSelectButtonBuilder:
_inspectorSelectButtonBuilder
,
shortcuts:
widget
.
shortcuts
,
actions:
widget
.
actions
,
restorationScopeId:
widget
.
restorationScopeId
,
);
}
...
...
packages/flutter/lib/src/material/app.dart
View file @
fc85492d
...
...
@@ -197,6 +197,7 @@ class MaterialApp extends StatefulWidget {
this
.
debugShowCheckedModeBanner
=
true
,
this
.
shortcuts
,
this
.
actions
,
this
.
restorationScopeId
,
})
:
assert
(
routes
!=
null
),
assert
(
navigatorObservers
!=
null
),
assert
(
title
!=
null
),
...
...
@@ -241,6 +242,7 @@ class MaterialApp extends StatefulWidget {
this
.
debugShowCheckedModeBanner
=
true
,
this
.
shortcuts
,
this
.
actions
,
this
.
restorationScopeId
,
})
:
assert
(
routeInformationParser
!=
null
),
assert
(
routerDelegate
!=
null
),
assert
(
title
!=
null
),
...
...
@@ -620,6 +622,9 @@ class MaterialApp extends StatefulWidget {
/// {@macro flutter.widgets.widgetsApp.actions.seeAlso}
final
Map
<
Type
,
Action
<
Intent
>>
actions
;
/// {@macro flutter.widgets.widgetsApp.restorationScopeId}
final
String
restorationScopeId
;
/// Turns on a [GridPaper] overlay that paints a baseline grid
/// Material apps.
///
...
...
@@ -780,6 +785,7 @@ class _MaterialAppState extends State<MaterialApp> {
inspectorSelectButtonBuilder:
_inspectorSelectButtonBuilder
,
shortcuts:
widget
.
shortcuts
,
actions:
widget
.
actions
,
restorationScopeId:
widget
.
restorationScopeId
,
);
}
...
...
@@ -814,6 +820,7 @@ class _MaterialAppState extends State<MaterialApp> {
inspectorSelectButtonBuilder:
_inspectorSelectButtonBuilder
,
shortcuts:
widget
.
shortcuts
,
actions:
widget
.
actions
,
restorationScopeId:
widget
.
restorationScopeId
,
);
}
...
...
packages/flutter/lib/src/widgets/app.dart
View file @
fc85492d
...
...
@@ -19,6 +19,7 @@ import 'media_query.dart';
import
'navigator.dart'
;
import
'pages.dart'
;
import
'performance_overlay.dart'
;
import
'restoration.dart'
;
import
'router.dart'
;
import
'scrollable.dart'
;
import
'semantics_debugger.dart'
;
...
...
@@ -195,6 +196,7 @@ class WidgetsApp extends StatefulWidget {
this
.
inspectorSelectButtonBuilder
,
this
.
shortcuts
,
this
.
actions
,
this
.
restorationScopeId
,
})
:
assert
(
navigatorObservers
!=
null
),
assert
(
routes
!=
null
),
assert
(
...
...
@@ -290,6 +292,7 @@ class WidgetsApp extends StatefulWidget {
this
.
inspectorSelectButtonBuilder
,
this
.
shortcuts
,
this
.
actions
,
this
.
restorationScopeId
,
})
:
assert
(
routeInformationParser
!=
null
&&
routerDelegate
!=
null
,
...
...
@@ -945,6 +948,24 @@ class WidgetsApp extends StatefulWidget {
/// {@endtemplate}
final
Map
<
Type
,
Action
<
Intent
>>?
actions
;
/// {@template flutter.widgets.widgetsApp.restorationScopeId}
/// The identifier to use for state restoration of this app.
///
/// Providing a restoration ID inserts a [RootRestorationScope] into the
/// widget hierarchy, which enables state restoration for descendant widgets.
///
/// Providing a restoration ID also enables the [Navigator] built by the
/// [WidgetsApp] to restore its state (i.e. to restore the history stack of
/// active [Route]s). See the documentation on [Navigator] for more details
/// around state restoration of [Route]s.
///
/// See also:
///
/// * [RestorationManager], which explains how state restoration works in
/// Flutter.
/// {@endtemplate}
final
String
?
restorationScopeId
;
/// If true, forces the performance overlay to be visible in all instances.
///
/// Used by the `showPerformanceOverlay` observatory extension.
...
...
@@ -1467,6 +1488,7 @@ class _WidgetsAppState extends State<WidgetsApp> with WidgetsBindingObserver {
}
else
{
assert
(
_navigator
!=
null
);
routing
=
Navigator
(
restorationScopeId:
'nav'
,
key:
_navigator
,
initialRoute:
_initialRouteName
,
onGenerateRoute:
_onGenerateRoute
,
...
...
@@ -1573,18 +1595,21 @@ class _WidgetsAppState extends State<WidgetsApp> with WidgetsBindingObserver {
:
_locale
!;
assert
(
_debugCheckLocalizations
(
appLocale
));
return
Shortcuts
(
shortcuts:
widget
.
shortcuts
??
WidgetsApp
.
defaultShortcuts
,
debugLabel:
'<Default WidgetsApp Shortcuts>'
,
child:
Actions
(
actions:
widget
.
actions
??
WidgetsApp
.
defaultActions
,
child:
FocusTraversalGroup
(
policy:
ReadingOrderTraversalPolicy
(),
child:
_MediaQueryFromWindow
(
child:
Localizations
(
locale:
appLocale
,
delegates:
_localizationsDelegates
.
toList
(),
child:
title
,
return
RootRestorationScope
(
restorationId:
widget
.
restorationScopeId
,
child:
Shortcuts
(
shortcuts:
widget
.
shortcuts
??
WidgetsApp
.
defaultShortcuts
,
debugLabel:
'<Default WidgetsApp Shortcuts>'
,
child:
Actions
(
actions:
widget
.
actions
??
WidgetsApp
.
defaultActions
,
child:
FocusTraversalGroup
(
policy:
ReadingOrderTraversalPolicy
(),
child:
_MediaQueryFromWindow
(
child:
Localizations
(
locale:
appLocale
,
delegates:
_localizationsDelegates
.
toList
(),
child:
title
,
),
),
),
),
...
...
packages/flutter/lib/src/widgets/navigator.dart
View file @
fc85492d
This diff is collapsed.
Click to expand it.
packages/flutter/lib/src/widgets/restoration.dart
View file @
fc85492d
...
...
@@ -499,10 +499,13 @@ abstract class RestorableProperty<T> extends ChangeNotifier {
}
/// The [State] object that this property is registered with.
///
/// Must only be called when [isRegistered] is true.
@protected
State
?
get
state
{
State
get
state
{
assert
(
isRegistered
);
assert
(
_debugAssertNotDisposed
());
return
_owner
;
return
_owner
!
;
}
/// Whether this property is currently registered with a [RestorationMixin].
...
...
@@ -609,13 +612,10 @@ abstract class RestorableProperty<T> extends ChangeNotifier {
/// class RestorationExampleApp extends StatelessWidget {
/// @override
/// Widget build(BuildContext context) {
/// // The [RootRestorationScope] can be removed once it is part of [MaterialApp].
/// return RootRestorationScope(
/// restorationId: 'root',
/// child: MaterialApp(
/// title: 'Restorable Counter',
/// home: RestorableCounter(restorationId: 'counter'),
/// ),
/// return MaterialApp(
/// restorationScopeId: 'app',
/// title: 'Restorable Counter',
/// home: RestorableCounter(restorationId: 'counter'),
/// );
/// }
/// }
...
...
packages/flutter/lib/src/widgets/routes.dart
View file @
fc85492d
...
...
@@ -18,6 +18,7 @@ import 'modal_barrier.dart';
import
'navigator.dart'
;
import
'overlay.dart'
;
import
'page_storage.dart'
;
import
'restoration.dart'
;
import
'transitions.dart'
;
// Examples can assume:
...
...
@@ -773,54 +774,64 @@ class _ModalScopeState<T> extends State<_ModalScope<T>> {
@override
Widget
build
(
BuildContext
context
)
{
return
_ModalScopeStatus
(
route:
widget
.
route
,
isCurrent:
widget
.
route
.
isCurrent
,
// _routeSetState is called if this updates
canPop:
widget
.
route
.
canPop
,
// _routeSetState is called if this updates
child:
Offstage
(
offstage:
widget
.
route
.
offstage
,
// _routeSetState is called if this updates
child:
PageStorage
(
bucket:
widget
.
route
.
_storageBucket
,
// immutable
child:
Actions
(
actions:
_actionMap
,
child:
FocusScope
(
node:
focusScopeNode
,
// immutable
child:
RepaintBoundary
(
child:
AnimatedBuilder
(
animation:
_listenable
,
// immutable
builder:
(
BuildContext
context
,
Widget
?
child
)
{
return
widget
.
route
.
buildTransitions
(
context
,
widget
.
route
.
animation
!,
widget
.
route
.
secondaryAnimation
!,
// This additional AnimatedBuilder is include because if the
// value of the userGestureInProgressNotifier changes, it's
// only necessary to rebuild the IgnorePointer widget and set
// the focus node's ability to focus.
AnimatedBuilder
(
animation:
widget
.
route
.
navigator
?.
userGestureInProgressNotifier
??
ValueNotifier
<
bool
>(
false
),
builder:
(
BuildContext
context
,
Widget
?
child
)
{
final
bool
ignoreEvents
=
_shouldIgnoreFocusRequest
;
focusScopeNode
.
canRequestFocus
=
!
ignoreEvents
;
return
IgnorePointer
(
ignoring:
ignoreEvents
,
child:
child
,
return
AnimatedBuilder
(
animation:
widget
.
route
.
restorationScopeId
,
builder:
(
BuildContext
context
,
Widget
?
child
)
{
assert
(
child
!=
null
);
return
RestorationScope
(
restorationId:
widget
.
route
.
restorationScopeId
.
value
,
child:
child
!,
);
},
child:
_ModalScopeStatus
(
route:
widget
.
route
,
isCurrent:
widget
.
route
.
isCurrent
,
// _routeSetState is called if this updates
canPop:
widget
.
route
.
canPop
,
// _routeSetState is called if this updates
child:
Offstage
(
offstage:
widget
.
route
.
offstage
,
// _routeSetState is called if this updates
child:
PageStorage
(
bucket:
widget
.
route
.
_storageBucket
,
// immutable
child:
Actions
(
actions:
_actionMap
,
child:
FocusScope
(
node:
focusScopeNode
,
// immutable
child:
RepaintBoundary
(
child:
AnimatedBuilder
(
animation:
_listenable
,
// immutable
builder:
(
BuildContext
context
,
Widget
?
child
)
{
return
widget
.
route
.
buildTransitions
(
context
,
widget
.
route
.
animation
!,
widget
.
route
.
secondaryAnimation
!,
// This additional AnimatedBuilder is include because if the
// value of the userGestureInProgressNotifier changes, it's
// only necessary to rebuild the IgnorePointer widget and set
// the focus node's ability to focus.
AnimatedBuilder
(
animation:
widget
.
route
.
navigator
?.
userGestureInProgressNotifier
??
ValueNotifier
<
bool
>(
false
),
builder:
(
BuildContext
context
,
Widget
?
child
)
{
final
bool
ignoreEvents
=
_shouldIgnoreFocusRequest
;
focusScopeNode
.
canRequestFocus
=
!
ignoreEvents
;
return
IgnorePointer
(
ignoring:
ignoreEvents
,
child:
child
,
);
},
child:
child
,
),
);
},
child:
_page
??=
RepaintBoundary
(
key:
widget
.
route
.
_subtreeKey
,
// immutable
child:
Builder
(
builder:
(
BuildContext
context
)
{
return
widget
.
route
.
buildPage
(
context
,
widget
.
route
.
animation
!,
widget
.
route
.
secondaryAnimation
!,
);
},
child:
child
,
),
);
},
child:
_page
??=
RepaintBoundary
(
key:
widget
.
route
.
_subtreeKey
,
// immutable
child:
Builder
(
builder:
(
BuildContext
context
)
{
return
widget
.
route
.
buildPage
(
context
,
widget
.
route
.
animation
!,
widget
.
route
.
secondaryAnimation
!,
);
},
),
),
),
...
...
packages/flutter/test/cupertino/tab_test.dart
View file @
fc85492d
...
...
@@ -97,6 +97,10 @@ void main() {
expect
(
tester
.
takeException
(),
isFlutterError
);
expect
(
unknownForRouteCalled
,
'/'
);
// Work-around for https://github.com/flutter/flutter/issues/65655.
await
tester
.
pumpWidget
(
Container
());
expect
(
tester
.
takeException
(),
isAssertionError
);
});
testWidgets
(
'Can use navigatorKey to navigate'
,
(
WidgetTester
tester
)
async
{
...
...
packages/flutter/test/cupertino/text_field_restoration_test.dart
View file @
fc85492d
...
...
@@ -16,9 +16,9 @@ const String alternativeText = 'Everything is awesome!!';
void
main
(
)
{
testWidgets
(
'CupertinoTextField restoration'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
const
RootRestorationScope
(
child:
TestWidget
()
,
restorationId:
'root'
,
const
CupertinoApp
(
restorationScopeId:
'app'
,
home:
TestWidget
()
,
),
);
...
...
@@ -27,11 +27,11 @@ void main() {
testWidgets
(
'CupertinoTextField restoration with external controller'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
const
RootRestorationScope
(
child:
TestWidget
(
const
CupertinoApp
(
restorationScopeId:
'app'
,
home:
TestWidget
(
useExternal:
true
,
),
restorationId:
'root'
,
),
);
...
...
@@ -102,17 +102,15 @@ class TestWidgetState extends State<TestWidget> with RestorationMixin {
@override
Widget
build
(
BuildContext
context
)
{
return
MaterialApp
(
home:
Material
(
child:
Align
(
alignment:
Alignment
.
center
,
child:
SizedBox
(
width:
50
,
child:
CupertinoTextField
(
restorationId:
'text'
,
maxLines:
3
,
controller:
widget
.
useExternal
?
controller
.
value
:
null
,
),
return
Material
(
child:
Align
(
alignment:
Alignment
.
center
,
child:
SizedBox
(
width:
50
,
child:
CupertinoTextField
(
restorationId:
'text'
,
maxLines:
3
,
controller:
widget
.
useExternal
?
controller
.
value
:
null
,
),
),
),
...
...
packages/flutter/test/material/app_test.dart
View file @
fc85492d
...
...
@@ -384,6 +384,10 @@ void main() {
);
expect
(
tester
.
takeException
(),
isFlutterError
);
expect
(
log
,
<
String
>[
'onGenerateRoute /'
,
'onUnknownRoute /'
]);
// Work-around for https://github.com/flutter/flutter/issues/65655.
await
tester
.
pumpWidget
(
Container
());
expect
(
tester
.
takeException
(),
isAssertionError
);
});
testWidgets
(
'MaterialApp with builder and no route information works.'
,
(
WidgetTester
tester
)
async
{
...
...
packages/flutter/test/material/debug_test.dart
View file @
fc85492d
...
...
@@ -142,6 +142,9 @@ void main() {
' PageStorage
\n
'
' Offstage
\n
'
' _ModalScopeStatus
\n
'
' UnmanagedRestorationScope
\n
'
' RestorationScope
\n
'
' AnimatedBuilder
\n
'
' _ModalScope<dynamic>-[LabeledGlobalKey<_ModalScopeState<dynamic>>#00000]
\n
'
' Semantics
\n
'
' _EffectiveTickerMode
\n
'
...
...
@@ -149,6 +152,7 @@ void main() {
' _OverlayEntryWidget-[LabeledGlobalKey<_OverlayEntryWidgetState>#00000]
\n
'
' _Theatre
\n
'
' Overlay-[LabeledGlobalKey<OverlayState>#00000]
\n
'
' UnmanagedRestorationScope
\n
'
' _FocusMarker
\n
'
' Semantics
\n
'
' FocusScope
\n
'
...
...
@@ -187,6 +191,10 @@ void main() {
' _FocusMarker
\n
'
' Focus
\n
'
' Shortcuts
\n
'
' UnmanagedRestorationScope
\n
'
' RestorationScope
\n
'
' UnmanagedRestorationScope
\n
'
' RootRestorationScope
\n
'
' WidgetsApp-[GlobalObjectKey _MaterialAppState#00000]
\n
'
' HeroControllerScope
\n
'
' ScrollConfiguration
\n
'
...
...
packages/flutter/test/material/text_field_restoration_test.dart
View file @
fc85492d
...
...
@@ -15,9 +15,9 @@ const String alternativeText = 'Everything is awesome!!';
void
main
(
)
{
testWidgets
(
'TextField restoration'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
const
RootRestorationScope
(
child:
TestWidget
()
,
restorationId:
'root'
,
const
MaterialApp
(
restorationScopeId:
'app'
,
home:
TestWidget
()
,
),
);
...
...
@@ -26,11 +26,11 @@ void main() {
testWidgets
(
'TextField restoration with external controller'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
const
RootRestorationScope
(
child:
TestWidget
(
const
MaterialApp
(
restorationScopeId:
'root'
,
home:
TestWidget
(
useExternal:
true
,
),
restorationId:
'root'
,
),
);
...
...
@@ -101,17 +101,15 @@ class TestWidgetState extends State<TestWidget> with RestorationMixin {
@override
Widget
build
(
BuildContext
context
)
{
return
MaterialApp
(
home:
Material
(
child:
Align
(
alignment:
Alignment
.
center
,
child:
SizedBox
(
width:
50
,
child:
TextField
(
restorationId:
'text'
,
maxLines:
3
,
controller:
widget
.
useExternal
?
controller
.
value
:
null
,
),
return
Material
(
child:
Align
(
alignment:
Alignment
.
center
,
child:
SizedBox
(
width:
50
,
child:
TextField
(
restorationId:
'text'
,
maxLines:
3
,
controller:
widget
.
useExternal
?
controller
.
value
:
null
,
),
),
),
...
...
packages/flutter/test/widgets/navigator_restoration_test.dart
0 → 100644
View file @
fc85492d
This diff is collapsed.
Click to expand it.
packages/flutter/test/widgets/navigator_test.dart
View file @
fc85492d
...
...
@@ -1664,7 +1664,7 @@ void main() {
' The onGenerateRoute callback must never return null, unless an
\n
'
' onUnknownRoute callback is provided as well.
\n
'
' The Navigator was:
\n
'
' NavigatorState#
4d6bf(lifecycle state: created)
\n
'
,
' NavigatorState#
00000(lifecycle state: initialized)
\n
'
),
);
});
...
...
@@ -1690,7 +1690,7 @@ void main() {
' route "/".
\n
'
' The onUnknownRoute callback must never return null.
\n
'
' The Navigator was:
\n
'
' NavigatorState#
38036(lifecycle state: creat
ed)
\n
'
,
' NavigatorState#
00000(lifecycle state: initializ
ed)
\n
'
,
),
);
});
...
...
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