Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 24 additions & 6 deletions lib/src/navigation/outlet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,10 @@ class RouterOutletState extends State<RouterOutlet> {
final List<_OutletEntry> _stack = [];
int _seq = 0;

/// Expose a event stream of the routes change

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The stream emits the raw path argument here, which is the push/navigate target (_stack.last) and may be relative (push('dashboard')). But _reportLocation reports _stack.first.uri (the absolute base) to the delegate / URL. These two should not disagree — consumers will see a relative or last-route Uri that doesn't match the actual outlet location. Prefer emitting the same resolved absolute Uri the delegate gets.

final _routeController = StreamController<Uri>.broadcast();
Stream<Uri> get onRouteChanged => _routeController.stream;

/// Whether this outlet has a sub-route above its seed to pop.
bool get canPop => _stack.length > 1;

Expand Down Expand Up @@ -146,6 +150,12 @@ class RouterOutletState extends State<RouterOutlet> {
}
}

@override
void dispose() {
_routeController.close();
super.dispose();
}

_OutletEntry _entry(Uri uri, Object? arguments) => _OutletEntry(
uri,
ValueKey('outlet-${identityHashCode(this)}-${_seq++}'),
Expand All @@ -158,7 +168,7 @@ class RouterOutletState extends State<RouterOutlet> {
Future<T?> push<T extends Object?>(String path, {Object? arguments}) {
final entry = _entry(Uri.parse(path), arguments);
setState(() => _stack.add(entry));
_reportLocation();
_reportLocation(path);
return entry.completer.future.then((value) => value as T?);
}

Expand Down Expand Up @@ -186,7 +196,7 @@ class RouterOutletState extends State<RouterOutlet> {
..clear()
..add(_entry(Uri.parse(path), arguments));
});
_reportLocation();
_reportLocation(path);
}

/// Replaces this outlet's TOP sub-route with [path].
Expand All @@ -197,7 +207,7 @@ class RouterOutletState extends State<RouterOutlet> {
}
final entry = _entry(Uri.parse(path), arguments);
setState(() => _stack.add(entry));
_reportLocation();
_reportLocation(path);
return entry.completer.future.then((value) => value as T?);
}

Expand Down Expand Up @@ -228,7 +238,7 @@ class RouterOutletState extends State<RouterOutlet> {
}
final entry = _entry(Uri.parse(path), arguments);
setState(() => _stack.add(entry));
_reportLocation();
_reportLocation(path);
return entry.completer.future.then((value) => value as T?);
}

Expand All @@ -252,15 +262,23 @@ class RouterOutletState extends State<RouterOutlet> {
if (!removed.completer.isCompleted) removed.completer.complete(null);
}
setState(() {});
_reportLocation();
_reportLocation(path);
return entry.completer.future.then((value) => value as T?);
}

/// Reports this outlet's current base sub-route to the root delegate so the
/// URL reflects the outlet (a tab switch shows up). A push/pop leaves the
/// base unchanged, so it reports the same value and stays out of the URL.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_remove (the real pop path), popUntil, and the reseed in didChangeDependencies all call _reportLocation() with no path, so they emit _stack.last.uri while push/navigate emit the passed path. The emitted value's meaning changes depending on the operation. Whatever shape we settle on, it should be uniform across every mutation — and covered by a test.

void _reportLocation() {
void _reportLocation([String? path]) {
if (_stack.isEmpty) return;

/// Convert the String path to a Uri and notfiy the route stream
if (path != null) {
_routeController.add(Uri.parse(path));
} else {
_routeController.add(_stack.last.uri);
}

final delegate = Router.maybeOf(context)?.routerDelegate;
if (delegate is ModularRouterDelegate) {
delegate.reportNestedLocation(_stack.first.uri);
Expand Down
Loading