import 'package:flutter/widgets.dart';
import '../leak_detector.dart';
const int _defaultCheckLeakDelay = 500;
typedef ShouldAddedRoute = bool Function(Route route);
class LeakNavigatorObserver extends NavigatorObserver {
final ShouldAddedRoute? shouldCheck;
final int checkLeakDelay;
LeakNavigatorObserver(
{this.checkLeakDelay = _defaultCheckLeakDelay, this.shouldCheck});
@override
void didPop(Route route, Route? previousRoute) {
_remove(route);
}
@override
void didPush(Route route, Route? previousRoute) {
_add(route);
}
@override
void didRemove(Route route, Route? previousRoute) {
_remove(route);
}
@override
void didReplace({Route? newRoute, Route? oldRoute}) {
if (newRoute != null) {
_add(newRoute);
}
if (oldRoute != null) {
_remove(oldRoute);
}
}
void _add(Route route) {
assert(() {
if (route is ModalRoute &&
(shouldCheck == null || shouldCheck!.call(route))) {
route.didPush().then((_) {
final element = _getElementByRoute(route);
if (element != null) {
final key = _getRouteKey(route);
watchObjectLeak(element, key);
watchObjectLeak(element.widget, key);
if (element is StatefulElement) {
watchObjectLeak(element.state, key);
}
}
});
}
return true;
}());
}
void _remove(Route route) {
assert(() {
final element = _getElementByRoute(route);
if (element != null) {
final key = _getRouteKey(route);
if (element is StatefulElement || element is StatelessElement) {
LeakDetector().ensureReleaseAsync(key, delay: checkLeakDelay);
}
}
return true;
}());
}
watchObjectLeak(Object obj, String name) {
assert(() {
LeakDetector().addWatchObject(obj, name);
return true;
}());
}
Element? _getElementByRoute(Route route) {
Element? element;
if (route is ModalRoute &&
(shouldCheck == null || shouldCheck!.call(route))) {
route.subtreeContext?.visitChildElements((child) {
child.visitChildElements((child) {
if (child.widget is Semantics) {
child.visitChildElements((child) {
element = child;
});
} else {
element = child;
}
});
});
}
return element;
}
String _getRouteKey(Route route) {
final hasCode = route.hashCode.toString();
String? key = route.settings.name;
if (key == null || key.isEmpty) {
key = route.hashCode.toString();
} else {
key = '$key($hasCode)';
}
return key;
}
}