RouteObserver supports multiple RouteAware per Route. (#13757)

Unsubscribe should remove RouteAware for all routes.
This commit is contained in:
Volodymyr Lykhonis 2018-01-05 12:52:43 -08:00 committed by Ian Hickson
parent 107c812f64
commit 467daba419
2 changed files with 56 additions and 8 deletions

View file

@ -1116,7 +1116,7 @@ abstract class PopupRoute<T> extends ModalRoute<T> {
/// }
/// ```
class RouteObserver<T extends Route<dynamic>> extends NavigatorObserver {
final Map<T, RouteAware> _listeners = <T, RouteAware>{};
final Map<T, Set<RouteAware>> _listeners = <T, Set<RouteAware>>{};
/// Subscribe [routeAware] to be informed about changes to [route].
///
@ -1126,32 +1126,55 @@ class RouteObserver<T extends Route<dynamic>> extends NavigatorObserver {
void subscribe(RouteAware routeAware, T route) {
assert(routeAware != null);
assert(route != null);
if (!_listeners.containsKey(route)) {
final Set<RouteAware> subscribers = _listeners.putIfAbsent(route, () => new Set<RouteAware>());
if (subscribers.add(routeAware)) {
routeAware.didPush();
_listeners[route] = routeAware;
}
}
/// Unsubscribe [routeAware].
///
/// [routeAware] is no longer informed about changes to its route.
/// [routeAware] is no longer informed about changes to its route. If the given argument was
/// subscribed to multiple types, this will unregister it (once) from each type.
void unsubscribe(RouteAware routeAware) {
assert(routeAware != null);
_listeners.remove(routeAware);
for (T route in _listeners.keys) {
final Set<RouteAware> subscribers = _listeners[route];
subscribers?.remove(routeAware);
}
}
@override
void didPop(Route<dynamic> route, Route<dynamic> previousRoute) {
if (route is T && previousRoute is T) {
_listeners[previousRoute]?.didPopNext();
_listeners[route]?.didPop();
final List<RouteAware> previousSubscribers = _listeners[previousRoute]?.toList();
if (previousSubscribers != null) {
for (RouteAware routeAware in previousSubscribers) {
routeAware.didPopNext();
}
}
final List<RouteAware> subscribers = _listeners[route]?.toList();
if (subscribers != null) {
for (RouteAware routeAware in subscribers) {
routeAware.didPop();
}
}
}
}
@override
void didPush(Route<dynamic> route, Route<dynamic> previousRoute) {
if (route is T && previousRoute is T) {
_listeners[previousRoute]?.didPushNext();
final Set<RouteAware> previousSubscribers = _listeners[previousRoute];
if (previousSubscribers != null) {
for (RouteAware routeAware in previousSubscribers) {
routeAware.didPushNext();
}
}
}
}
}

View file

@ -453,6 +453,31 @@ void main() {
observer.didPop(route, pageRoute);
verifyNoMoreInteractions(pageRouteAware);
});
test('does not call listeners when already subscribed', () {
final RouteObserver<PageRoute<dynamic>> observer = new RouteObserver<PageRoute<dynamic>>();
final RouteAware pageRouteAware = new MockRouteAware();
final MockPageRoute pageRoute = new MockPageRoute();
observer.subscribe(pageRouteAware, pageRoute);
observer.subscribe(pageRouteAware, pageRoute);
verify(pageRouteAware.didPush()).called(1);
});
test('does not call listeners when unsubscribed', () {
final RouteObserver<PageRoute<dynamic>> observer = new RouteObserver<PageRoute<dynamic>>();
final RouteAware pageRouteAware = new MockRouteAware();
final MockPageRoute pageRoute = new MockPageRoute();
final MockPageRoute nextPageRoute = new MockPageRoute();
observer.subscribe(pageRouteAware, pageRoute);
observer.subscribe(pageRouteAware, nextPageRoute);
verify(pageRouteAware.didPush()).called(2);
observer.unsubscribe(pageRouteAware);
observer.didPush(nextPageRoute, pageRoute);
observer.didPop(nextPageRoute, pageRoute);
verifyNoMoreInteractions(pageRouteAware);
});
});
}