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
c38feb3b
Unverified
Commit
c38feb3b
authored
Aug 04, 2021
by
chunhtai
Committed by
GitHub
Aug 04, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Router replaces browser history entry if state changes (#83509)
parent
28f311ad
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
255 additions
and
54 deletions
+255
-54
system_navigator.dart
packages/flutter/lib/src/services/system_navigator.dart
+18
-4
router.dart
packages/flutter/lib/src/widgets/router.dart
+66
-38
system_navigator_test.dart
packages/flutter/test/services/system_navigator_test.dart
+6
-2
route_notification_messages_test.dart
...lutter/test/widgets/route_notification_messages_test.dart
+8
-0
router_test.dart
packages/flutter/test/widgets/router_test.dart
+157
-10
No files found.
packages/flutter/lib/src/services/system_navigator.dart
View file @
c38feb3b
...
@@ -67,20 +67,34 @@ class SystemNavigator {
...
@@ -67,20 +67,34 @@ class SystemNavigator {
/// Notifies the platform for a route information change.
/// Notifies the platform for a route information change.
///
///
/// On web,
creates a new browser history entry and update URL with the route
/// On web,
this method behaves differently based on the single-entry or
///
information. Whether the history holds one entry or multiple entries is
///
multiple-entries history mode. Use the [selectSingleEntryHistory] and
///
determined by [selectSingleEntryHistory] and [selectMultiEntryHistory]
.
///
[selectMultiEntryHistory] to toggle between modes
.
///
///
/// Currently, this is ignored on other platforms.
/// For single-entry mode, this method replaces the current URL and state in
/// the current history entry. The flag `replace` is ignored.
///
/// For multiple-entries mode, this method creates a new history entry on top
/// of the current entry if the `replace` is false, thus the user will
/// be on a new history entry as if the user has visited a new page, and the
/// browser back button brings the user back to the previous entry. If
/// `replace` is true, this method only updates the URL and the state in the
/// current history entry without pushing a new one.
///
/// This method is ignored on other platforms.
///
/// The `replace` flag defaults to false.
static
Future
<
void
>
routeInformationUpdated
({
static
Future
<
void
>
routeInformationUpdated
({
required
String
location
,
required
String
location
,
Object
?
state
,
Object
?
state
,
bool
replace
=
false
,
})
{
})
{
return
SystemChannels
.
navigation
.
invokeMethod
<
void
>(
return
SystemChannels
.
navigation
.
invokeMethod
<
void
>(
'routeInformationUpdated'
,
'routeInformationUpdated'
,
<
String
,
dynamic
>{
<
String
,
dynamic
>{
'location'
:
location
,
'location'
:
location
,
'state'
:
state
,
'state'
:
state
,
'replace'
:
replace
,
},
},
);
);
}
}
...
...
packages/flutter/lib/src/widgets/router.dart
View file @
c38feb3b
...
@@ -198,16 +198,28 @@ class RouteInformation {
...
@@ -198,16 +198,28 @@ class RouteInformation {
/// retrieve the new route information from the [routerDelegate]'s
/// retrieve the new route information from the [routerDelegate]'s
/// [RouterDelegate.currentConfiguration] method and the
/// [RouterDelegate.currentConfiguration] method and the
/// [routeInformationParser]'s [RouteInformationParser.restoreRouteInformation]
/// [routeInformationParser]'s [RouteInformationParser.restoreRouteInformation]
/// method. If the location in the new route information is different from the
/// method.
/// current location, the router sends the new route information to the
///
/// [routeInformationProvider]'s
/// If the location in the new route information is different from the
/// [RouteInformationProvider.routerReportsNewRouteInformation] method. That
/// current location, this is considered to be a navigation event, the router
/// method as implemented in [PlatformRouteInformationProvider] uses
/// sends the new route information to the [routeInformationProvider]'s
/// [RouteInformationProvider.routerReportsNewRouteInformation] method with
/// `isNavigation` equals to true. That method as implemented in
/// [PlatformRouteInformationProvider] uses
/// [SystemNavigator.routeInformationUpdated] to notify the engine, and through
/// [SystemNavigator.routeInformationUpdated] to notify the engine, and through
/// that the browser, of the new URL.
/// that the browser, to create a history entry with the new url if the
/// `isNavigation` is true.
///
/// If the location is the same as the current location but different state,
/// the router still sends the new route information to the
/// [routeInformationProvider]'s
/// [RouteInformationProvider.routerReportsNewRouteInformation] but with
/// `isNavigation` equals to false. This causes
/// [PlatformRouteInformationProvider] replace current history entry instead
/// of creating a new one.
///
///
/// One can force the [Router] to report new route information
to the
/// One can force the [Router] to report new route information
as navigation
/// [routeInformationProvider] (and thus the browser) even if the
///
event to the
[routeInformationProvider] (and thus the browser) even if the
/// [RouteInformation.location] has not changed by calling the [Router.navigate]
/// [RouteInformation.location] has not changed by calling the [Router.navigate]
/// method with a callback that performs the state change. This allows one to
/// method with a callback that performs the state change. This allows one to
/// support the browser's back and forward buttons without changing the URL. For
/// support the browser's back and forward buttons without changing the URL. For
...
@@ -218,10 +230,10 @@ class RouteInformation {
...
@@ -218,10 +230,10 @@ class RouteInformation {
/// clicks the back button, the app will go back to the previous scroll position
/// clicks the back button, the app will go back to the previous scroll position
/// without changing the URL in the location bar.
/// without changing the URL in the location bar.
///
///
/// One can also force the [Router] to ignore a
pplication state changes by
/// One can also force the [Router] to ignore a
navigation event by making
///
making those changes during a callback passed to [Router.neglect]. The
///
those changes during a callback passed to [Router.neglect]. The [Router]
///
[Router] will not report any route information even if it detects location
///
will not report the route information with `isNavigation` equals to false
///
change as a
result of running the callback.
///
even if it detects location change as the
result of running the callback.
///
///
/// To opt out of URL updates entirely, pass null for [routeInformationProvider]
/// To opt out of URL updates entirely, pass null for [routeInformationProvider]
/// and [routeInformationParser]. This is not recommended in general, but may be
/// and [routeInformationParser]. This is not recommended in general, but may be
...
@@ -392,8 +404,8 @@ class Router<T> extends StatefulWidget {
...
@@ -392,8 +404,8 @@ class Router<T> extends StatefulWidget {
return
scope
?.
routerState
.
widget
as
Router
<
T
>?;
return
scope
?.
routerState
.
widget
as
Router
<
T
>?;
}
}
/// Forces the [Router] to run the [callback] and
reports the route
/// Forces the [Router] to run the [callback] and
create a new history
///
information back to the engine
.
///
entry in the browser
.
///
///
/// The web application relies on the [Router] to report new route information
/// The web application relies on the [Router] to report new route information
/// in order to create browser history entry. The [Router] will only report
/// in order to create browser history entry. The [Router] will only report
...
@@ -414,8 +426,8 @@ class Router<T> extends StatefulWidget {
...
@@ -414,8 +426,8 @@ class Router<T> extends StatefulWidget {
///
///
/// * [Router]: see the "URL updates for web applications" section for more
/// * [Router]: see the "URL updates for web applications" section for more
/// information about route information reporting.
/// information about route information reporting.
/// * [neglect]: which forces the [Router] to not
report the route
/// * [neglect]: which forces the [Router] to not
create a new history entry
///
information
even if location does change.
/// even if location does change.
static
void
navigate
(
BuildContext
context
,
VoidCallback
callback
)
{
static
void
navigate
(
BuildContext
context
,
VoidCallback
callback
)
{
final
_RouterScope
scope
=
context
final
_RouterScope
scope
=
context
.
getElementForInheritedWidgetOfExactType
<
_RouterScope
>()!
.
getElementForInheritedWidgetOfExactType
<
_RouterScope
>()!
...
@@ -423,24 +435,27 @@ class Router<T> extends StatefulWidget {
...
@@ -423,24 +435,27 @@ class Router<T> extends StatefulWidget {
scope
.
routerState
.
_setStateWithExplicitReportStatus
(
_IntentionToReportRouteInformation
.
must
,
callback
);
scope
.
routerState
.
_setStateWithExplicitReportStatus
(
_IntentionToReportRouteInformation
.
must
,
callback
);
}
}
/// Forces the [Router] to run the [callback] without reporting the route
/// Forces the [Router] to run the [callback] without creating a new history
/// information back to the engine.
/// entry in the browser.
///
/// Use this method if you don't want the [Router] to report the new route
/// information even if it detects changes as a result of running the
/// [callback].
///
///
/// The web application relies on the [Router] to report new route information
/// The web application relies on the [Router] to report new route information
/// in order to create browser history entry. The [Router] will report them
/// in order to create browser history entry. The [Router] will report them
/// automatically if it detects the [RouteInformation.location] changes. You
/// automatically if it detects the [RouteInformation.location] changes.
/// can use this method if you want to navigate to a new route without
///
/// creating the browser history entry.
/// Creating a new route history entry makes users feel they have visited a
/// new page, and the browser back button brings them back to previous history
/// entry. Use this method if you don't want the [Router] to create a new
/// route information even if it detects changes as a result of running the
/// [callback].
///
/// Using this method will still update the URL and state in current history
/// entry.
///
///
/// See also:
/// See also:
///
///
/// * [Router]: see the "URL updates for web applications" section for more
/// * [Router]: see the "URL updates for web applications" section for more
/// information about route information reporting.
/// information about route information reporting.
/// * [navigate]: which forces the [Router] to
report the route information
/// * [navigate]: which forces the [Router] to
create a new history entry
/// even if location does not change.
/// even if location does not change.
static
void
neglect
(
BuildContext
context
,
VoidCallback
callback
)
{
static
void
neglect
(
BuildContext
context
,
VoidCallback
callback
)
{
final
_RouterScope
scope
=
context
final
_RouterScope
scope
=
context
...
@@ -497,8 +512,6 @@ class _RouterState<T> extends State<Router<T>> with RestorationMixin {
...
@@ -497,8 +512,6 @@ class _RouterState<T> extends State<Router<T>> with RestorationMixin {
bool
_routeInformationReportingTaskScheduled
=
false
;
bool
_routeInformationReportingTaskScheduled
=
false
;
String
?
_lastSeenLocation
;
void
_scheduleRouteInformationReportingTask
()
{
void
_scheduleRouteInformationReportingTask
()
{
if
(
_routeInformationReportingTaskScheduled
||
widget
.
routeInformationProvider
==
null
)
if
(
_routeInformationReportingTaskScheduled
||
widget
.
routeInformationProvider
==
null
)
return
;
return
;
...
@@ -512,23 +525,29 @@ class _RouterState<T> extends State<Router<T>> with RestorationMixin {
...
@@ -512,23 +525,29 @@ class _RouterState<T> extends State<Router<T>> with RestorationMixin {
_routeInformationReportingTaskScheduled
=
false
;
_routeInformationReportingTaskScheduled
=
false
;
if
(
_routeInformation
.
value
!=
null
)
{
if
(
_routeInformation
.
value
!=
null
)
{
final
RouteInformation
routeInformation
=
_routeInformation
.
value
!;
final
RouteInformation
oldRouteInformation
=
widget
.
routeInformationProvider
!.
value
;
final
RouteInformation
currentRouteInformation
=
_routeInformation
.
value
!;
switch
(
_currentIntentionToReport
)
{
switch
(
_currentIntentionToReport
)
{
case
_IntentionToReportRouteInformation
.
none
:
case
_IntentionToReportRouteInformation
.
none
:
assert
(
false
,
'_reportRouteInformation must not be called with _IntentionToReportRouteInformation.none'
);
assert
(
false
,
'_reportRouteInformation must not be called with _IntentionToReportRouteInformation.none'
);
return
;
return
;
case
_IntentionToReportRouteInformation
.
ignore
:
case
_IntentionToReportRouteInformation
.
ignore
:
if
(
oldRouteInformation
.
location
!=
currentRouteInformation
.
location
||
oldRouteInformation
.
state
!=
currentRouteInformation
.
state
)
{
widget
.
routeInformationProvider
!.
routerReportsNewRouteInformation
(
currentRouteInformation
,
isNavigation:
false
);
}
break
;
break
;
case
_IntentionToReportRouteInformation
.
maybe
:
case
_IntentionToReportRouteInformation
.
maybe
:
if
(
_lastSeenLocation
!=
routeInformation
.
location
)
{
if
(
oldRouteInformation
.
location
!=
currentRouteInformation
.
location
)
{
widget
.
routeInformationProvider
!.
routerReportsNewRouteInformation
(
routeInformation
);
widget
.
routeInformationProvider
!.
routerReportsNewRouteInformation
(
currentRouteInformation
);
}
else
if
(
oldRouteInformation
.
state
!=
currentRouteInformation
.
state
)
{
widget
.
routeInformationProvider
!.
routerReportsNewRouteInformation
(
currentRouteInformation
,
isNavigation:
false
);
}
}
break
;
break
;
case
_IntentionToReportRouteInformation
.
must
:
case
_IntentionToReportRouteInformation
.
must
:
widget
.
routeInformationProvider
!.
routerReportsNewRouteInformation
(
r
outeInformation
);
widget
.
routeInformationProvider
!.
routerReportsNewRouteInformation
(
currentR
outeInformation
);
break
;
break
;
}
}
_lastSeenLocation
=
routeInformation
.
location
;
}
}
_currentIntentionToReport
=
_IntentionToReportRouteInformation
.
none
;
_currentIntentionToReport
=
_IntentionToReportRouteInformation
.
none
;
}
}
...
@@ -621,7 +640,6 @@ class _RouterState<T> extends State<Router<T>> with RestorationMixin {
...
@@ -621,7 +640,6 @@ class _RouterState<T> extends State<Router<T>> with RestorationMixin {
void
_processRouteInformation
(
RouteInformation
information
,
ValueGetter
<
_DelegateRouteSetter
<
T
>>
delegateRouteSetter
)
{
void
_processRouteInformation
(
RouteInformation
information
,
ValueGetter
<
_DelegateRouteSetter
<
T
>>
delegateRouteSetter
)
{
_currentRouteInformationParserTransaction
=
Object
();
_currentRouteInformationParserTransaction
=
Object
();
_currentRouterDelegateTransaction
=
Object
();
_currentRouterDelegateTransaction
=
Object
();
_lastSeenLocation
=
information
.
location
;
widget
.
routeInformationParser
!
widget
.
routeInformationParser
!
.
parseRouteInformation
(
information
)
.
parseRouteInformation
(
information
)
.
then
<
T
>(
_verifyRouteInformationParserStillCurrent
(
_currentRouteInformationParserTransaction
,
widget
))
.
then
<
T
>(
_verifyRouteInformationParserStillCurrent
(
_currentRouteInformationParserTransaction
,
widget
))
...
@@ -1301,8 +1319,7 @@ abstract class RouterDelegate<T> extends Listenable {
...
@@ -1301,8 +1319,7 @@ abstract class RouterDelegate<T> extends Listenable {
/// from the [Router] back to the engine by overriding the
/// from the [Router] back to the engine by overriding the
/// [routerReportsNewRouteInformation].
/// [routerReportsNewRouteInformation].
abstract
class
RouteInformationProvider
extends
ValueListenable
<
RouteInformation
>
{
abstract
class
RouteInformationProvider
extends
ValueListenable
<
RouteInformation
>
{
/// A callback called when the [Router] widget detects any navigation event
/// A callback called when the [Router] widget reports new route information
/// due to state changes.
///
///
/// The subclasses can override this method to update theirs values or trigger
/// The subclasses can override this method to update theirs values or trigger
/// other side effects. For example, the [PlatformRouteInformationProvider]
/// other side effects. For example, the [PlatformRouteInformationProvider]
...
@@ -1310,7 +1327,17 @@ abstract class RouteInformationProvider extends ValueListenable<RouteInformation
...
@@ -1310,7 +1327,17 @@ abstract class RouteInformationProvider extends ValueListenable<RouteInformation
///
///
/// The [routeInformation] is the new route information after the navigation
/// The [routeInformation] is the new route information after the navigation
/// event.
/// event.
void
routerReportsNewRouteInformation
(
RouteInformation
routeInformation
)
{}
///
/// The [isNavigation] denotes whether the new route information is generated
/// as a result of a navigation event. This information can be useful in a
/// web application, for example, the [PlatformRouteInformationProvider] uses
/// this flag to decide whether to create a browser history entry that enables
/// browser backward and forward buttons.
///
/// For more information on how [Router] determines a navigation event, see
/// the "URL updates for web applications" section in the [Router]
/// documentation.
void
routerReportsNewRouteInformation
(
RouteInformation
routeInformation
,
{
bool
isNavigation
=
true
})
{}
}
}
/// The route information provider that propagates the platform route information changes.
/// The route information provider that propagates the platform route information changes.
...
@@ -1333,11 +1360,12 @@ class PlatformRouteInformationProvider extends RouteInformationProvider with Wid
...
@@ -1333,11 +1360,12 @@ class PlatformRouteInformationProvider extends RouteInformationProvider with Wid
})
:
_value
=
initialRouteInformation
;
})
:
_value
=
initialRouteInformation
;
@override
@override
void
routerReportsNewRouteInformation
(
RouteInformation
routeInformation
)
{
void
routerReportsNewRouteInformation
(
RouteInformation
routeInformation
,
{
bool
isNavigation
=
true
}
)
{
SystemNavigator
.
selectMultiEntryHistory
();
SystemNavigator
.
selectMultiEntryHistory
();
SystemNavigator
.
routeInformationUpdated
(
SystemNavigator
.
routeInformationUpdated
(
location:
routeInformation
.
location
!,
location:
routeInformation
.
location
!,
state:
routeInformation
.
state
,
state:
routeInformation
.
state
,
replace:
!
isNavigation
,
);
);
_value
=
routeInformation
;
_value
=
routeInformation
;
}
}
...
...
packages/flutter/test/services/system_navigator_test.dart
View file @
c38feb3b
...
@@ -43,11 +43,15 @@ void main() {
...
@@ -43,11 +43,15 @@ void main() {
]);
]);
await
verify
(()
=>
SystemNavigator
.
routeInformationUpdated
(
location:
'a'
),
<
Object
>[
await
verify
(()
=>
SystemNavigator
.
routeInformationUpdated
(
location:
'a'
),
<
Object
>[
isMethodCall
(
'routeInformationUpdated'
,
arguments:
<
String
,
dynamic
>{
'location'
:
'a'
,
'state'
:
null
}),
isMethodCall
(
'routeInformationUpdated'
,
arguments:
<
String
,
dynamic
>{
'location'
:
'a'
,
'state'
:
null
,
'replace'
:
false
}),
]);
]);
await
verify
(()
=>
SystemNavigator
.
routeInformationUpdated
(
location:
'a'
,
state:
true
),
<
Object
>[
await
verify
(()
=>
SystemNavigator
.
routeInformationUpdated
(
location:
'a'
,
state:
true
),
<
Object
>[
isMethodCall
(
'routeInformationUpdated'
,
arguments:
<
String
,
dynamic
>{
'location'
:
'a'
,
'state'
:
true
}),
isMethodCall
(
'routeInformationUpdated'
,
arguments:
<
String
,
dynamic
>{
'location'
:
'a'
,
'state'
:
true
,
'replace'
:
false
}),
]);
await
verify
(()
=>
SystemNavigator
.
routeInformationUpdated
(
location:
'a'
,
state:
true
,
replace:
true
),
<
Object
>[
isMethodCall
(
'routeInformationUpdated'
,
arguments:
<
String
,
dynamic
>{
'location'
:
'a'
,
'state'
:
true
,
'replace'
:
true
}),
]);
]);
await
verify
(()
=>
SystemNavigator
.
routeUpdated
(
routeName:
'a'
,
previousRouteName:
'b'
),
<
Object
>[
await
verify
(()
=>
SystemNavigator
.
routeUpdated
(
routeName:
'a'
,
previousRouteName:
'b'
),
<
Object
>[
...
...
packages/flutter/test/widgets/route_notification_messages_test.dart
View file @
c38feb3b
...
@@ -69,6 +69,7 @@ void main() {
...
@@ -69,6 +69,7 @@ void main() {
arguments:
<
String
,
dynamic
>{
arguments:
<
String
,
dynamic
>{
'location'
:
'/'
,
'location'
:
'/'
,
'state'
:
null
,
'state'
:
null
,
'replace'
:
false
,
},
},
),
),
]);
]);
...
@@ -86,6 +87,7 @@ void main() {
...
@@ -86,6 +87,7 @@ void main() {
arguments:
<
String
,
dynamic
>{
arguments:
<
String
,
dynamic
>{
'location'
:
'/A'
,
'location'
:
'/A'
,
'state'
:
null
,
'state'
:
null
,
'replace'
:
false
,
},
},
),
),
);
);
...
@@ -103,6 +105,7 @@ void main() {
...
@@ -103,6 +105,7 @@ void main() {
arguments:
<
String
,
dynamic
>{
arguments:
<
String
,
dynamic
>{
'location'
:
'/'
,
'location'
:
'/'
,
'state'
:
null
,
'state'
:
null
,
'replace'
:
false
,
},
},
),
),
);
);
...
@@ -174,6 +177,7 @@ void main() {
...
@@ -174,6 +177,7 @@ void main() {
arguments:
<
String
,
dynamic
>{
arguments:
<
String
,
dynamic
>{
'location'
:
'/'
,
'location'
:
'/'
,
'state'
:
null
,
'state'
:
null
,
'replace'
:
false
,
},
},
),
),
]);
]);
...
@@ -191,6 +195,7 @@ void main() {
...
@@ -191,6 +195,7 @@ void main() {
arguments:
<
String
,
dynamic
>{
arguments:
<
String
,
dynamic
>{
'location'
:
'/A'
,
'location'
:
'/A'
,
'state'
:
null
,
'state'
:
null
,
'replace'
:
false
,
},
},
),
),
);
);
...
@@ -208,6 +213,7 @@ void main() {
...
@@ -208,6 +213,7 @@ void main() {
arguments:
<
String
,
dynamic
>{
arguments:
<
String
,
dynamic
>{
'location'
:
'/B'
,
'location'
:
'/B'
,
'state'
:
null
,
'state'
:
null
,
'replace'
:
false
,
},
},
),
),
);
);
...
@@ -243,6 +249,7 @@ void main() {
...
@@ -243,6 +249,7 @@ void main() {
arguments:
<
String
,
dynamic
>{
arguments:
<
String
,
dynamic
>{
'location'
:
'/home'
,
'location'
:
'/home'
,
'state'
:
null
,
'state'
:
null
,
'replace'
:
false
,
},
},
),
),
]);
]);
...
@@ -294,6 +301,7 @@ void main() {
...
@@ -294,6 +301,7 @@ void main() {
isMethodCall
(
'routeInformationUpdated'
,
arguments:
<
String
,
dynamic
>{
isMethodCall
(
'routeInformationUpdated'
,
arguments:
<
String
,
dynamic
>{
'location'
:
'update'
,
'location'
:
'update'
,
'state'
:
'state'
,
'state'
:
'state'
,
'replace'
:
false
,
}),
}),
]);
]);
});
});
...
...
packages/flutter/test/widgets/router_test.dart
View file @
c38feb3b
...
@@ -477,11 +477,14 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
...
@@ -477,11 +477,14 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
testWidgets
(
'router does report URL change correctly'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'router does report URL change correctly'
,
(
WidgetTester
tester
)
async
{
RouteInformation
?
reportedRouteInformation
;
RouteInformation
?
reportedRouteInformation
;
bool
?
reportedIsNavigation
;
final
SimpleRouteInformationProvider
provider
=
SimpleRouteInformationProvider
(
final
SimpleRouteInformationProvider
provider
=
SimpleRouteInformationProvider
(
onRouterReport:
(
RouteInformation
information
)
{
onRouterReport:
(
RouteInformation
information
,
bool
isNavigation
)
{
// Makes sure we only report once after manually cleaning up.
// Makes sure we only report once after manually cleaning up.
expect
(
reportedRouteInformation
,
isNull
);
expect
(
reportedRouteInformation
,
isNull
);
expect
(
reportedIsNavigation
,
isNull
);
reportedRouteInformation
=
information
;
reportedRouteInformation
=
information
;
reportedIsNavigation
=
isNavigation
;
},
},
);
);
final
SimpleRouterDelegate
delegate
=
SimpleRouterDelegate
(
final
SimpleRouterDelegate
delegate
=
SimpleRouterDelegate
(
...
@@ -519,35 +522,44 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
...
@@ -519,35 +522,44 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
expect
(
find
.
text
(
'initial'
),
findsNothing
);
expect
(
find
.
text
(
'initial'
),
findsNothing
);
expect
(
find
.
text
(
'update'
),
findsOneWidget
);
expect
(
find
.
text
(
'update'
),
findsOneWidget
);
expect
(
reportedRouteInformation
!.
location
,
'update'
);
expect
(
reportedRouteInformation
!.
location
,
'update'
);
expect
(
reportedIsNavigation
,
isTrue
);
// The router should
not repor
t if only state changes.
// The router should
report as non navigation even
t if only state changes.
reportedRouteInformation
=
null
;
reportedRouteInformation
=
null
;
reportedIsNavigation
=
null
;
delegate
.
routeInformation
=
const
RouteInformation
(
delegate
.
routeInformation
=
const
RouteInformation
(
location:
'update'
,
location:
'update'
,
state:
'another state'
,
state:
'another state'
,
);
);
await
tester
.
pump
();
await
tester
.
pump
();
expect
(
find
.
text
(
'update'
),
findsOneWidget
);
expect
(
find
.
text
(
'update'
),
findsOneWidget
);
expect
(
reportedRouteInformation
,
isNull
);
expect
(
reportedRouteInformation
!.
location
,
'update'
);
expect
(
reportedRouteInformation
!.
state
,
'another state'
);
expect
(
reportedIsNavigation
,
isFalse
);
reportedRouteInformation
=
null
;
reportedRouteInformation
=
null
;
reportedIsNavigation
=
null
;
bool
result
=
false
;
bool
result
=
false
;
result
=
await
outerDispatcher
.
invokeCallback
(
SynchronousFuture
<
bool
>(
false
));
result
=
await
outerDispatcher
.
invokeCallback
(
SynchronousFuture
<
bool
>(
false
));
expect
(
result
,
isTrue
);
expect
(
result
,
isTrue
);
await
tester
.
pump
();
await
tester
.
pump
();
expect
(
find
.
text
(
'popped'
),
findsOneWidget
);
expect
(
find
.
text
(
'popped'
),
findsOneWidget
);
expect
(
reportedRouteInformation
!.
location
,
'popped'
);
expect
(
reportedRouteInformation
!.
location
,
'popped'
);
expect
(
reportedIsNavigation
,
isTrue
);
});
});
testWidgets
(
'router can be forced to recognize or ignore navigating events'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'router can be forced to recognize or ignore navigating events'
,
(
WidgetTester
tester
)
async
{
RouteInformation
?
reportedRouteInformation
;
RouteInformation
?
reportedRouteInformation
;
bool
?
reportedIsNavigation
;
bool
isNavigating
=
false
;
bool
isNavigating
=
false
;
late
RouteInformation
nextRouteInformation
;
late
RouteInformation
nextRouteInformation
;
final
SimpleRouteInformationProvider
provider
=
SimpleRouteInformationProvider
(
final
SimpleRouteInformationProvider
provider
=
SimpleRouteInformationProvider
(
onRouterReport:
(
RouteInformation
information
)
{
onRouterReport:
(
RouteInformation
information
,
bool
isNavigation
)
{
// Makes sure we only report once after manually cleaning up.
// Makes sure we only report once after manually cleaning up.
expect
(
reportedRouteInformation
,
isNull
);
expect
(
reportedRouteInformation
,
isNull
);
expect
(
reportedIsNavigation
,
isNull
);
reportedRouteInformation
=
information
;
reportedRouteInformation
=
information
;
reportedIsNavigation
=
isNavigation
;
},
},
);
);
provider
.
value
=
const
RouteInformation
(
provider
.
value
=
const
RouteInformation
(
...
@@ -592,7 +604,10 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
...
@@ -592,7 +604,10 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
await
tester
.
pump
();
await
tester
.
pump
();
expect
(
find
.
text
(
'initial'
),
findsNothing
);
expect
(
find
.
text
(
'initial'
),
findsNothing
);
expect
(
find
.
text
(
'update'
),
findsOneWidget
);
expect
(
find
.
text
(
'update'
),
findsOneWidget
);
expect
(
reportedRouteInformation
,
isNull
);
expect
(
reportedIsNavigation
,
isFalse
);
expect
(
reportedRouteInformation
!.
location
,
'update'
);
reportedIsNavigation
=
null
;
reportedRouteInformation
=
null
;
isNavigating
=
true
;
isNavigating
=
true
;
// This should not trigger any real navigating event because the
// This should not trigger any real navigating event because the
...
@@ -600,13 +615,112 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
...
@@ -600,13 +615,112 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
// report a route information because isNavigating = true.
// report a route information because isNavigating = true.
await
tester
.
tap
(
find
.
byType
(
ElevatedButton
));
await
tester
.
tap
(
find
.
byType
(
ElevatedButton
));
await
tester
.
pump
();
await
tester
.
pump
();
expect
(
reportedIsNavigation
,
isTrue
);
expect
(
reportedRouteInformation
!.
location
,
'update'
);
expect
(
reportedRouteInformation
!.
location
,
'update'
);
reportedIsNavigation
=
null
;
reportedRouteInformation
=
null
;
});
testWidgets
(
'router ignore navigating events updates RouteInformationProvider'
,
(
WidgetTester
tester
)
async
{
RouteInformation
?
updatedRouteInformation
;
late
RouteInformation
nextRouteInformation
;
final
SimpleRouteInformationProvider
provider
=
SimpleRouteInformationProvider
(
onRouterReport:
(
RouteInformation
information
,
bool
isNavigation
)
{
// This should never be a navigation event.
expect
(
isNavigation
,
false
);
expect
(
updatedRouteInformation
,
isNull
);
updatedRouteInformation
=
information
;
},
);
provider
.
value
=
const
RouteInformation
(
location:
'initial'
,
);
final
SimpleRouterDelegate
delegate
=
SimpleRouterDelegate
(
reportConfiguration:
true
);
delegate
.
builder
=
(
BuildContext
context
,
RouteInformation
?
information
)
{
return
ElevatedButton
(
child:
Text
(
information
!.
location
!),
onPressed:
()
{
Router
.
neglect
(
context
,
()
{
if
(
delegate
.
routeInformation
!=
nextRouteInformation
)
delegate
.
routeInformation
=
nextRouteInformation
;
});
},
);
};
final
BackButtonDispatcher
outerDispatcher
=
RootBackButtonDispatcher
();
await
tester
.
pumpWidget
(
buildBoilerPlate
(
Router
<
RouteInformation
>(
backButtonDispatcher:
outerDispatcher
,
routeInformationProvider:
provider
,
routeInformationParser:
SimpleRouteInformationParser
(),
routerDelegate:
delegate
,
),
));
expect
(
find
.
text
(
'initial'
),
findsOneWidget
);
expect
(
updatedRouteInformation
,
isNull
);
nextRouteInformation
=
const
RouteInformation
(
location:
'update'
,
);
await
tester
.
tap
(
find
.
byType
(
ElevatedButton
));
await
tester
.
pump
();
expect
(
find
.
text
(
'initial'
),
findsNothing
);
expect
(
find
.
text
(
'update'
),
findsOneWidget
);
expect
(
updatedRouteInformation
!.
location
,
'update'
);
});
testWidgets
(
'state change without location changes updates RouteInformationProvider'
,
(
WidgetTester
tester
)
async
{
RouteInformation
?
updatedRouteInformation
;
late
RouteInformation
nextRouteInformation
;
final
SimpleRouteInformationProvider
provider
=
SimpleRouteInformationProvider
(
onRouterReport:
(
RouteInformation
information
,
bool
isNavigation
)
{
// This should never be a navigation event.
expect
(
isNavigation
,
false
);
expect
(
updatedRouteInformation
,
isNull
);
updatedRouteInformation
=
information
;
},
);
provider
.
value
=
const
RouteInformation
(
location:
'initial'
,
state:
'state1'
,
);
final
SimpleRouterDelegate
delegate
=
SimpleRouterDelegate
(
reportConfiguration:
true
);
delegate
.
builder
=
(
BuildContext
context
,
RouteInformation
?
information
)
{
return
ElevatedButton
(
child:
Text
(
information
!.
location
!),
onPressed:
()
{
delegate
.
routeInformation
=
nextRouteInformation
;
},
);
};
final
BackButtonDispatcher
outerDispatcher
=
RootBackButtonDispatcher
();
await
tester
.
pumpWidget
(
buildBoilerPlate
(
Router
<
RouteInformation
>(
backButtonDispatcher:
outerDispatcher
,
routeInformationProvider:
provider
,
routeInformationParser:
SimpleRouteInformationParser
(),
routerDelegate:
delegate
,
),
));
expect
(
find
.
text
(
'initial'
),
findsOneWidget
);
expect
(
updatedRouteInformation
,
isNull
);
nextRouteInformation
=
const
RouteInformation
(
location:
'initial'
,
state:
'state2'
,
);
await
tester
.
tap
(
find
.
byType
(
ElevatedButton
));
await
tester
.
pump
();
expect
(
updatedRouteInformation
!.
location
,
'initial'
);
expect
(
updatedRouteInformation
!.
state
,
'state2'
);
});
});
testWidgets
(
'router does not report when route information is up to date with route information provider'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'router does not report when route information is up to date with route information provider'
,
(
WidgetTester
tester
)
async
{
RouteInformation
?
reportedRouteInformation
;
RouteInformation
?
reportedRouteInformation
;
final
SimpleRouteInformationProvider
provider
=
SimpleRouteInformationProvider
(
final
SimpleRouteInformationProvider
provider
=
SimpleRouteInformationProvider
(
onRouterReport:
(
RouteInformation
information
)
{
onRouterReport:
(
RouteInformation
information
,
bool
isNavigation
)
{
reportedRouteInformation
=
information
;
reportedRouteInformation
=
information
;
},
},
);
);
...
@@ -691,6 +805,38 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
...
@@ -691,6 +805,38 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
expect
(
find
.
text
(
'newTestRouteName'
),
findsOneWidget
);
expect
(
find
.
text
(
'newTestRouteName'
),
findsOneWidget
);
});
});
testWidgets
(
'PlatformRouteInformationProvider updates route information'
,
(
WidgetTester
tester
)
async
{
final
List
<
MethodCall
>
log
=
<
MethodCall
>[];
TestDefaultBinaryMessengerBinding
.
instance
!
.
defaultBinaryMessenger
.
setMockMethodCallHandler
(
SystemChannels
.
navigation
,
(
MethodCall
methodCall
)
async
{
log
.
add
(
methodCall
);
}
);
final
RouteInformationProvider
provider
=
PlatformRouteInformationProvider
(
initialRouteInformation:
const
RouteInformation
(
location:
'initial'
,
),
);
log
.
clear
();
provider
.
routerReportsNewRouteInformation
(
const
RouteInformation
(
location:
'a'
,
state:
true
));
expect
(
log
,
<
Object
>[
isMethodCall
(
'selectMultiEntryHistory'
,
arguments:
null
),
isMethodCall
(
'routeInformationUpdated'
,
arguments:
<
String
,
dynamic
>{
'location'
:
'a'
,
'state'
:
true
,
'replace'
:
false
}),
]);
log
.
clear
();
provider
.
routerReportsNewRouteInformation
(
const
RouteInformation
(
location:
'b'
,
state:
false
),
isNavigation:
false
);
expect
(
log
,
<
Object
>[
isMethodCall
(
'selectMultiEntryHistory'
,
arguments:
null
),
isMethodCall
(
'routeInformationUpdated'
,
arguments:
<
String
,
dynamic
>{
'location'
:
'b'
,
'state'
:
false
,
'replace'
:
true
}),
]);
});
testWidgets
(
'RootBackButtonDispatcher works'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'RootBackButtonDispatcher works'
,
(
WidgetTester
tester
)
async
{
final
BackButtonDispatcher
outerDispatcher
=
RootBackButtonDispatcher
();
final
BackButtonDispatcher
outerDispatcher
=
RootBackButtonDispatcher
();
final
RouteInformationProvider
provider
=
PlatformRouteInformationProvider
(
final
RouteInformationProvider
provider
=
PlatformRouteInformationProvider
(
...
@@ -1093,7 +1239,7 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
...
@@ -1093,7 +1239,7 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
testWidgets
(
'Router reports location if it is different from location given by OS'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Router reports location if it is different from location given by OS'
,
(
WidgetTester
tester
)
async
{
final
List
<
RouteInformation
>
reportedRouteInformation
=
<
RouteInformation
>[];
final
List
<
RouteInformation
>
reportedRouteInformation
=
<
RouteInformation
>[];
final
SimpleRouteInformationProvider
provider
=
SimpleRouteInformationProvider
(
final
SimpleRouteInformationProvider
provider
=
SimpleRouteInformationProvider
(
onRouterReport:
reportedRouteInformation
.
add
,
onRouterReport:
(
RouteInformation
info
,
bool
isNavigation
)
=>
reportedRouteInformation
.
add
(
info
)
,
)..
value
=
const
RouteInformation
(
location:
'/home'
);
)..
value
=
const
RouteInformation
(
location:
'/home'
);
await
tester
.
pumpWidget
(
buildBoilerPlate
(
await
tester
.
pumpWidget
(
buildBoilerPlate
(
...
@@ -1131,7 +1277,7 @@ Widget buildBoilerPlate(Widget child) {
...
@@ -1131,7 +1277,7 @@ Widget buildBoilerPlate(Widget child) {
typedef
SimpleRouterDelegateBuilder
=
Widget
Function
(
BuildContext
,
RouteInformation
?);
typedef
SimpleRouterDelegateBuilder
=
Widget
Function
(
BuildContext
,
RouteInformation
?);
typedef
SimpleRouterDelegatePopRoute
=
Future
<
bool
>
Function
();
typedef
SimpleRouterDelegatePopRoute
=
Future
<
bool
>
Function
();
typedef
SimpleNavigatorRouterDelegatePopPage
<
T
>
=
bool
Function
(
Route
<
T
>
route
,
T
result
);
typedef
SimpleNavigatorRouterDelegatePopPage
<
T
>
=
bool
Function
(
Route
<
T
>
route
,
T
result
);
typedef
RouterReportRouterInformation
=
void
Function
(
RouteInformation
);
typedef
RouterReportRouterInformation
=
void
Function
(
RouteInformation
,
bool
);
class
SimpleRouteInformationParser
extends
RouteInformationParser
<
RouteInformation
>
{
class
SimpleRouteInformationParser
extends
RouteInformationParser
<
RouteInformation
>
{
SimpleRouteInformationParser
();
SimpleRouteInformationParser
();
...
@@ -1252,8 +1398,9 @@ class SimpleRouteInformationProvider extends RouteInformationProvider with Chang
...
@@ -1252,8 +1398,9 @@ class SimpleRouteInformationProvider extends RouteInformationProvider with Chang
}
}
@override
@override
void
routerReportsNewRouteInformation
(
RouteInformation
routeInformation
)
{
void
routerReportsNewRouteInformation
(
RouteInformation
routeInformation
,
{
bool
isNavigation
=
true
})
{
onRouterReport
?.
call
(
routeInformation
);
_value
=
routeInformation
;
onRouterReport
?.
call
(
routeInformation
,
isNavigation
);
}
}
}
}
...
...
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