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
2640bfeb
Unverified
Commit
2640bfeb
authored
Sep 05, 2019
by
Mouad Debbar
Committed by
GitHub
Sep 05, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Allow gaps in the initial route (#39440)
parent
0f2af976
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
78 additions
and
15 deletions
+78
-15
app.dart
packages/flutter/lib/src/widgets/app.dart
+6
-4
navigator.dart
packages/flutter/lib/src/widgets/navigator.dart
+12
-11
navigator_test.dart
packages/flutter/test/widgets/navigator_test.dart
+60
-0
No files found.
packages/flutter/lib/src/widgets/app.dart
View file @
2640bfeb
...
...
@@ -361,10 +361,12 @@ class WidgetsApp extends StatefulWidget {
/// also. For example, if the route was `/a/b/c`, then the app would start
/// with the three routes `/a`, `/a/b`, and `/a/b/c` loaded, in that order.
///
/// If any part of this process fails to generate routes, then the
/// [initialRoute] is ignored and [Navigator.defaultRouteName] is used instead
/// (`/`). This can happen if the app is started with an intent that specifies
/// a non-existent route.
/// Intermediate routes aren't required to exist. In the example above, `/a`
/// and `/a/b` could be skipped if they have no matching route. But `/a/b/c` is
/// required to have a route, else [initialRoute] is ignored and
/// [Navigator.defaultRouteName] is used instead (`/`). This can happen if the
/// app is started with an intent that specifies a non-existent route.
///
/// The [Navigator] is only built if routes are provided (either via [home],
/// [routes], [onGenerateRoute], or [onUnknownRoute]); if they are not,
/// [initialRoute] must be null and [builder] must not be null.
...
...
packages/flutter/lib/src/widgets/navigator.dart
View file @
2640bfeb
...
...
@@ -783,6 +783,15 @@ class Navigator extends StatefulWidget {
/// then the [Navigator] would push the following routes on startup: `/`,
/// `/stocks`, `/stocks/HOOLI`. This enables deep linking while allowing the
/// application to maintain a predictable route history.
///
/// If any of the intermediate routes doesn't exist, it'll simply be skipped.
/// In the example above, if `/stocks` doesn't have a corresponding route in
/// the app, it'll be skipped and only `/` and `/stocks/HOOLI` will be pushed.
///
/// That said, the full route has to map to something in the app in order for
/// this to work. In our example, `/stocks/HOOLI` has to map to a route in the
/// app. Otherwise, [initialRoute] will be ignored and [defaultRouteName] will
/// be used instead.
final
String
initialRoute
;
/// Called to generate a route for a given [RouteSettings].
...
...
@@ -1509,9 +1518,6 @@ class NavigatorState extends State<Navigator> with TickerProviderStateMixin {
if
(
initialRouteName
.
startsWith
(
'/'
)
&&
initialRouteName
.
length
>
1
)
{
initialRouteName
=
initialRouteName
.
substring
(
1
);
// strip leading '/'
assert
(
Navigator
.
defaultRouteName
==
'/'
);
final
List
<
String
>
plannedInitialRouteNames
=
<
String
>[
Navigator
.
defaultRouteName
,
];
final
List
<
Route
<
dynamic
>>
plannedInitialRoutes
=
<
Route
<
dynamic
>>[
_routeNamed
<
dynamic
>(
Navigator
.
defaultRouteName
,
allowNull:
true
,
arguments:
null
),
];
...
...
@@ -1520,22 +1526,17 @@ class NavigatorState extends State<Navigator> with TickerProviderStateMixin {
String
routeName
=
''
;
for
(
String
part
in
routeParts
)
{
routeName
+=
'/
$part
'
;
plannedInitialRouteNames
.
add
(
routeName
);
plannedInitialRoutes
.
add
(
_routeNamed
<
dynamic
>(
routeName
,
allowNull:
true
,
arguments:
null
));
}
}
if
(
plannedInitialRoutes
.
contains
(
null
)
)
{
if
(
plannedInitialRoutes
.
last
==
null
)
{
assert
(()
{
FlutterError
.
reportError
(
FlutterErrorDetails
(
exception:
'Could not navigate to initial route.
\n
'
'The requested route name was: "/
$initialRouteName
"
\n
'
'The following routes were therefore attempted:
\n
'
' *
${plannedInitialRouteNames.join("\n * ")}
\n
'
'This resulted in the following objects:
\n
'
' *
${plannedInitialRoutes.join("\n * ")}
\n
'
'One or more of those objects was null, and therefore the initial route specified will be '
'There was no corresponding route in the app, and therefore the initial route specified will be '
'ignored and "
${Navigator.defaultRouteName}
" will be used instead.'
),
);
...
...
@@ -1543,7 +1544,7 @@ class NavigatorState extends State<Navigator> with TickerProviderStateMixin {
}());
push
(
_routeNamed
<
Object
>(
Navigator
.
defaultRouteName
,
arguments:
null
));
}
else
{
plannedInitialRoutes
.
forEach
(
push
);
plannedInitialRoutes
.
where
((
Route
<
dynamic
>
route
)
=>
route
!=
null
).
forEach
(
push
);
}
}
else
{
Route
<
Object
>
route
;
...
...
packages/flutter/test/widgets/navigator_test.dart
View file @
2640bfeb
...
...
@@ -973,4 +973,64 @@ void main() {
expect
(
arguments
.
single
,
'pushReplacementNamed'
);
arguments
.
clear
();
});
testWidgets
(
'Initial route can have gaps'
,
(
WidgetTester
tester
)
async
{
final
GlobalKey
<
NavigatorState
>
keyNav
=
GlobalKey
<
NavigatorState
>();
const
Key
keyRoot
=
Key
(
'Root'
);
const
Key
keyA
=
Key
(
'A'
);
const
Key
keyABC
=
Key
(
'ABC'
);
await
tester
.
pumpWidget
(
MaterialApp
(
navigatorKey:
keyNav
,
initialRoute:
'/A/B/C'
,
routes:
<
String
,
WidgetBuilder
>{
'/'
:
(
BuildContext
context
)
=>
Container
(
key:
keyRoot
),
'/A'
:
(
BuildContext
context
)
=>
Container
(
key:
keyA
),
// The route /A/B is intentionally left out.
'/A/B/C'
:
(
BuildContext
context
)
=>
Container
(
key:
keyABC
),
},
),
);
// The initial route /A/B/C should've been pushed successfully.
expect
(
find
.
byKey
(
keyRoot
),
findsOneWidget
);
expect
(
find
.
byKey
(
keyA
),
findsOneWidget
);
expect
(
find
.
byKey
(
keyABC
),
findsOneWidget
);
keyNav
.
currentState
.
pop
();
await
tester
.
pumpAndSettle
();
expect
(
find
.
byKey
(
keyRoot
),
findsOneWidget
);
expect
(
find
.
byKey
(
keyA
),
findsOneWidget
);
expect
(
find
.
byKey
(
keyABC
),
findsNothing
);
});
testWidgets
(
'The full initial route has to be matched'
,
(
WidgetTester
tester
)
async
{
final
GlobalKey
<
NavigatorState
>
keyNav
=
GlobalKey
<
NavigatorState
>();
const
Key
keyRoot
=
Key
(
'Root'
);
const
Key
keyA
=
Key
(
'A'
);
const
Key
keyAB
=
Key
(
'AB'
);
await
tester
.
pumpWidget
(
MaterialApp
(
navigatorKey:
keyNav
,
initialRoute:
'/A/B/C'
,
routes:
<
String
,
WidgetBuilder
>{
'/'
:
(
BuildContext
context
)
=>
Container
(
key:
keyRoot
),
'/A'
:
(
BuildContext
context
)
=>
Container
(
key:
keyA
),
'/A/B'
:
(
BuildContext
context
)
=>
Container
(
key:
keyAB
),
// The route /A/B/C is intentionally left out.
},
),
);
final
dynamic
exception
=
tester
.
takeException
();
expect
(
exception
is
String
,
isTrue
);
expect
(
exception
.
startsWith
(
'Could not navigate to initial route.'
),
isTrue
);
// Only the root route should've been pushed.
expect
(
find
.
byKey
(
keyRoot
),
findsOneWidget
);
expect
(
find
.
byKey
(
keyA
),
findsNothing
);
expect
(
find
.
byKey
(
keyAB
),
findsNothing
);
});
}
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