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
8c5bee94
Commit
8c5bee94
authored
Jan 24, 2017
by
Hans Muller
Committed by
GitHub
Jan 24, 2017
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add Navigator.pushReplacement() and Navigator.pushReplacementNamed() (#7611)
parent
1bdf3518
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
199 additions
and
21 deletions
+199
-21
navigator.dart
packages/flutter/lib/src/widgets/navigator.dart
+108
-14
routes.dart
packages/flutter/lib/src/widgets/routes.dart
+4
-5
navigator_test.dart
packages/flutter/test/widgets/navigator_test.dart
+85
-0
routes_test.dart
packages/flutter/test/widgets/routes_test.dart
+2
-2
No files found.
packages/flutter/lib/src/widgets/navigator.dart
View file @
8c5bee94
...
...
@@ -53,9 +53,10 @@ abstract class Route<T> {
void
install
(
OverlayEntry
insertionPoint
)
{
}
/// Called after install() when the route is pushed onto the navigator.
///
/// The returned value resolves when the push transition is complete.
@protected
@mustCallSuper
void
didPush
()
{
}
Future
<
Null
>
didPush
()
=>
new
Future
<
Null
>.
value
();
/// When this route is popped (see [Navigator.pop]) if the result isn't
/// specified or if it's null, this value will be used instead.
...
...
@@ -66,7 +67,6 @@ abstract class Route<T> {
@mustCallSuper
void
didReplace
(
Route
<
dynamic
>
oldRoute
)
{
}
/// Returns false if this route wants to veto a [Navigator.pop]. This method is
/// called by [Naviagtor.willPop].
///
...
...
@@ -579,6 +579,41 @@ class Navigator extends StatefulWidget {
return
navigator
.
pushNamed
(
routeName
);
}
/// Replace the current route by pushing the route named [routeName] and then
/// disposing the previous route.
///
/// The route name will be passed to the navigator's [onGenerateRoute]
/// callback. The returned route will be pushed into the navigator.
///
/// Returns a [Future] that completes to the `result` value passed to [pop]
/// when the pushed route is popped off the navigator.
///
/// Typical usage is as follows:
///
/// ```dart
/// Navigator.of(context).pushReplacementNamed('/jouett/1781');
/// ```
static
Future
<
dynamic
>
pushReplacementNamed
(
BuildContext
context
,
String
routeName
,
{
dynamic
result
})
{
return
Navigator
.
of
(
context
).
pushReplacementNamed
(
routeName
,
result:
result
);
}
/// Replace the current route by pushing [route] and then disposing the
/// current route.
///
/// The new route and the route below the new route (if any) are notified
/// (see [Route.didPush] and [Route.didChangeNext]). The navigator observer
/// is not notified about the old route. The old route is disposed (see
/// [Route.dispose]).
///
/// If a [result] is provided, it will be the return value of the old route,
/// as if the old route had been popped.
///
/// Returns a [Future] that completes to the `result` value passed to [pop]
/// when the pushed route is popped off the navigator.
static
Future
<
dynamic
>
pushReplacement
(
BuildContext
context
,
Route
<
dynamic
>
route
,
{
dynamic
result
})
{
return
Navigator
.
of
(
context
).
pushReplacement
(
route
,
result:
result
);
}
/// The state from the closest instance of this class that encloses the given context.
///
/// Typical usage is as follows:
...
...
@@ -660,6 +695,19 @@ class NavigatorState extends State<Navigator> with TickerProviderStateMixin {
bool
_debugLocked
=
false
;
// used to prevent re-entrant calls to push, pop, and friends
Route
<
dynamic
>
_routeNamed
(
String
name
)
{
assert
(!
_debugLocked
);
assert
(
name
!=
null
);
final
RouteSettings
settings
=
new
RouteSettings
(
name:
name
);
Route
<
dynamic
>
route
=
config
.
onGenerateRoute
(
settings
);
if
(
route
==
null
)
{
assert
(
config
.
onUnknownRoute
!=
null
);
route
=
config
.
onUnknownRoute
(
settings
);
assert
(
route
!=
null
);
}
return
route
;
}
/// Push a named route onto the navigator.
///
/// The route name will be passed to [Navigator.onGenerateRoute]. The returned
...
...
@@ -674,16 +722,7 @@ class NavigatorState extends State<Navigator> with TickerProviderStateMixin {
/// Navigator.of(context).pushNamed('/nyc/1776');
/// ```
Future
<
dynamic
>
pushNamed
(
String
name
)
{
assert
(!
_debugLocked
);
assert
(
name
!=
null
);
RouteSettings
settings
=
new
RouteSettings
(
name:
name
);
Route
<
dynamic
>
route
=
config
.
onGenerateRoute
(
settings
);
if
(
route
==
null
)
{
assert
(
config
.
onUnknownRoute
!=
null
);
route
=
config
.
onUnknownRoute
(
settings
);
assert
(
route
!=
null
);
}
return
push
(
route
);
return
push
(
_routeNamed
(
name
));
}
/// Adds the given route to the navigator's history, and transitions to it.
...
...
@@ -740,7 +779,7 @@ class NavigatorState extends State<Navigator> with TickerProviderStateMixin {
assert
(
newRoute
.
overlayEntries
.
isEmpty
);
assert
(!
overlay
.
debugIsVisible
(
oldRoute
.
overlayEntries
.
last
));
setState
(()
{
int
index
=
_history
.
indexOf
(
oldRoute
);
final
int
index
=
_history
.
indexOf
(
oldRoute
);
assert
(
index
>=
0
);
newRoute
.
_navigator
=
this
;
newRoute
.
install
(
oldRoute
.
overlayEntries
.
last
);
...
...
@@ -757,6 +796,61 @@ class NavigatorState extends State<Navigator> with TickerProviderStateMixin {
assert
(()
{
_debugLocked
=
false
;
return
true
;
});
}
/// Push the [newRoute] and dispose the old current Route.
///
/// The new route and the route below the new route (if any) are notified
/// (see [Route.didPush] and [Route.didChangeNext]). The navigator observer
/// is not notified about the old route. The old route is disposed (see
/// [Route.dispose]).
///
/// If a [result] is provided, it will be the return value of the old route,
/// as if the old route had been popped.
Future
<
dynamic
>
pushReplacement
(
Route
<
dynamic
>
newRoute
,
{
dynamic
result
})
{
assert
(!
_debugLocked
);
assert
(()
{
_debugLocked
=
true
;
return
true
;
});
final
Route
<
dynamic
>
oldRoute
=
_history
.
last
;
assert
(
oldRoute
!=
null
&&
oldRoute
.
_navigator
==
this
);
assert
(
oldRoute
.
overlayEntries
.
isNotEmpty
);
assert
(
newRoute
.
_navigator
==
null
);
assert
(
newRoute
.
overlayEntries
.
isEmpty
);
setState
(()
{
int
index
=
_history
.
length
-
1
;
assert
(
index
>=
0
);
assert
(
_history
.
indexOf
(
oldRoute
)
==
index
);
newRoute
.
_navigator
=
this
;
newRoute
.
install
(
_currentOverlayEntry
);
_history
[
index
]
=
newRoute
;
newRoute
.
didPush
().
then
<
dynamic
>((
Null
_
)
{
// The old route's exit is not animated. We're assuming that the
// new route completely obscures the old one.
if
(
mounted
)
{
oldRoute
..
_popCompleter
.
complete
(
result
??
oldRoute
.
currentResult
)
..
dispose
();
}
});
newRoute
.
didChangeNext
(
null
);
if
(
index
>
0
)
_history
[
index
-
1
].
didChangeNext
(
newRoute
);
config
.
observer
?.
didPush
(
newRoute
,
oldRoute
);
});
assert
(()
{
_debugLocked
=
false
;
return
true
;
});
_cancelActivePointers
();
return
newRoute
.
popped
;
}
/// Push the route named [name] and dispose the old current route.
///
/// The route name will be passed to [Navigator.onGenerateRoute]. The returned
/// route will be pushed into the navigator.
///
/// Returns a [Future] that completes to the `result` value passed to [pop]
/// when the pushed route is popped off the navigator.
Future
<
dynamic
>
pushReplacementNamed
(
String
name
,
{
dynamic
result
})
{
return
pushReplacement
(
_routeNamed
(
name
),
result:
result
);
}
/// Replaces a route that is not currently visible with a new route.
///
/// The route to be removed is the one below the given `anchorRoute`. That
...
...
packages/flutter/lib/src/widgets/routes.dart
View file @
8c5bee94
...
...
@@ -164,10 +164,9 @@ abstract class TransitionRoute<T> extends OverlayRoute<T> {
}
@override
void
didPush
()
{
Future
<
Null
>
didPush
()
{
_animation
.
addStatusListener
(
_handleStatusChanged
);
_controller
.
forward
();
super
.
didPush
();
return
_controller
.
forward
();
}
@override
...
...
@@ -559,7 +558,7 @@ abstract class ModalRoute<T> extends TransitionRoute<T> with LocalHistoryRoute<T
}
@override
void
didPush
()
{
Future
<
Null
>
didPush
()
{
if
(!
settings
.
isInitialRoute
)
{
BuildContext
overlayContext
=
navigator
.
overlay
?.
context
;
assert
(()
{
...
...
@@ -574,7 +573,7 @@ abstract class ModalRoute<T> extends TransitionRoute<T> with LocalHistoryRoute<T
});
Focus
.
moveScopeTo
(
focusKey
,
context:
overlayContext
);
}
super
.
didPush
();
return
super
.
didPush
();
}
@override
...
...
packages/flutter/test/widgets/navigator_test.dart
View file @
8c5bee94
...
...
@@ -89,6 +89,26 @@ class OnTapPage extends StatelessWidget {
}
}
class
StringRoute
extends
PageRoute
<
String
>
{
StringRoute
(
RouteSettings
settings
,
this
.
builder
)
:
super
(
settings:
settings
);
final
WidgetBuilder
builder
;
@override
bool
get
maintainState
=>
true
;
@override
Duration
get
transitionDuration
=>
const
Duration
(
milliseconds:
300
);
@override
Color
get
barrierColor
=>
null
;
@override
Widget
buildPage
(
BuildContext
context
,
Animation
<
double
>
__
,
Animation
<
double
>
___
)
{
return
builder
(
context
);
}
}
void
main
(
)
{
testWidgets
(
'Can navigator navigate to and from a stateful widget'
,
(
WidgetTester
tester
)
async
{
final
Map
<
String
,
WidgetBuilder
>
routes
=
<
String
,
WidgetBuilder
>{
...
...
@@ -258,6 +278,71 @@ void main() {
expect
(
find
.
text
(
'/'
),
findsNothing
);
expect
(
find
.
text
(
'A'
),
findsNothing
);
expect
(
find
.
text
(
'B'
),
findsOneWidget
);
});
testWidgets
(
'replaceNamed'
,
(
WidgetTester
tester
)
async
{
final
Map
<
String
,
WidgetBuilder
>
routes
=
<
String
,
WidgetBuilder
>{
'/'
:
(
BuildContext
context
)
=>
new
OnTapPage
(
id:
'/'
,
onTap:
()
{
Navigator
.
pushReplacementNamed
(
context
,
'/A'
);
}),
'/A'
:
(
BuildContext
context
)
=>
new
OnTapPage
(
id:
'A'
,
onTap:
()
{
Navigator
.
pushReplacementNamed
(
context
,
'/B'
);
}),
'/B'
:
(
BuildContext
context
)
=>
new
OnTapPage
(
id:
'B'
),
};
await
tester
.
pumpWidget
(
new
MaterialApp
(
routes:
routes
));
await
tester
.
tap
(
find
.
text
(
'/'
));
// replaceNamed('/A')
await
tester
.
pump
();
await
tester
.
pump
(
const
Duration
(
seconds:
1
));
expect
(
find
.
text
(
'/'
),
findsNothing
);
expect
(
find
.
text
(
'A'
),
findsOneWidget
);
await
tester
.
tap
(
find
.
text
(
'A'
));
// replaceNamed('/B')
await
tester
.
pump
();
await
tester
.
pump
(
const
Duration
(
seconds:
1
));
expect
(
find
.
text
(
'/'
),
findsNothing
);
expect
(
find
.
text
(
'A'
),
findsNothing
);
expect
(
find
.
text
(
'B'
),
findsOneWidget
);
});
testWidgets
(
'replaceNamed returned value'
,
(
WidgetTester
tester
)
async
{
Future
<
String
>
value
;
final
Map
<
String
,
WidgetBuilder
>
routes
=
<
String
,
WidgetBuilder
>{
'/'
:
(
BuildContext
context
)
=>
new
OnTapPage
(
id:
'/'
,
onTap:
()
{
Navigator
.
pushNamed
(
context
,
'/A'
);
}),
'/A'
:
(
BuildContext
context
)
=>
new
OnTapPage
(
id:
'A'
,
onTap:
()
{
value
=
Navigator
.
pushReplacementNamed
(
context
,
'/B'
,
result:
'B'
);
}),
'/B'
:
(
BuildContext
context
)
=>
new
OnTapPage
(
id:
'B'
,
onTap:
()
{
Navigator
.
pop
(
context
,
'B'
);
}),
};
await
tester
.
pumpWidget
(
new
MaterialApp
(
onGenerateRoute:
(
RouteSettings
settings
)
{
return
new
StringRoute
(
settings
,
(
BuildContext
context
)
=>
routes
[
settings
.
name
](
context
));
}
));
expect
(
find
.
text
(
'/'
),
findsOneWidget
);
expect
(
find
.
text
(
'A'
,
skipOffstage:
false
),
findsNothing
);
expect
(
find
.
text
(
'B'
,
skipOffstage:
false
),
findsNothing
);
await
tester
.
tap
(
find
.
text
(
'/'
));
// pushNamed('/A'), stack becomes /, /A
await
tester
.
pump
();
await
tester
.
pump
(
const
Duration
(
seconds:
1
));
expect
(
find
.
text
(
'/'
),
findsNothing
);
expect
(
find
.
text
(
'A'
),
findsOneWidget
);
expect
(
find
.
text
(
'B'
),
findsNothing
);
await
tester
.
tap
(
find
.
text
(
'A'
));
// replaceNamed('/B'), stack becomes /, /B
await
tester
.
pump
();
await
tester
.
pump
(
const
Duration
(
seconds:
1
));
expect
(
find
.
text
(
'/'
),
findsNothing
);
expect
(
find
.
text
(
'A'
),
findsNothing
);
expect
(
find
.
text
(
'B'
),
findsOneWidget
);
await
tester
.
tap
(
find
.
text
(
'B'
));
// pop, stack becomes /
await
tester
.
pump
();
await
tester
.
pump
(
const
Duration
(
seconds:
1
));
expect
(
find
.
text
(
'/'
),
findsOneWidget
);
expect
(
find
.
text
(
'A'
),
findsNothing
);
expect
(
find
.
text
(
'B'
),
findsNothing
);
String
replaceNamedValue
=
await
value
;
// replaceNamed result was 'B'
expect
(
replaceNamedValue
,
'B'
);
});
}
packages/flutter/test/widgets/routes_test.dart
View file @
8c5bee94
...
...
@@ -39,9 +39,9 @@ class TestRoute extends LocalHistoryRoute<String> {
}
@override
void
didPush
()
{
Future
<
Null
>
didPush
()
{
log
(
'didPush'
);
super
.
didPush
();
return
super
.
didPush
();
}
@override
...
...
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