-
Notifications
You must be signed in to change notification settings - Fork 6
fix(router): Missing platform default route information #90
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
3903d88
0674643
ada5501
c86e772
c32343a
5c1b7f8
b39b10b
f88d4bb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -231,14 +231,12 @@ abstract class Coordinator<T extends RouteUnique> extends CoordinatorCore<T> | |
| /// The [RouteInformationProvider] that is used to configure the [Router]. | ||
| /// | ||
| /// ## Relationship | ||
| /// Supplies the initial URI from [initialRoutePath] or defaults to `/`. | ||
| /// Supplies the initial URI, preferring any platform-provided route | ||
| /// (e.g. from [PlatformDispatcher.defaultRouteName]), then falling back to | ||
| /// [initialRoutePath] if set, and finally defaulting to `/`. | ||
| @override | ||
| late final RouteInformationProvider routeInformationProvider = | ||
| PlatformRouteInformationProvider( | ||
| initialRouteInformation: RouteInformation( | ||
| uri: initialRoutePath ?? Uri.parse('/'), | ||
| ), | ||
| ); | ||
| CoordinatorRouteInformationProvider(coordinator: this); | ||
|
Comment on lines
238
to
+239
|
||
|
|
||
| /// Access to the navigator state. | ||
| /// | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| import 'package:flutter/widgets.dart'; | ||
| import 'package:zenrouter/zenrouter.dart'; | ||
|
|
||
|
definev marked this conversation as resolved.
|
||
| /// A [RouteInformationProvider] that derives its initial route from both the | ||
| /// platform and a [Coordinator]. | ||
| /// | ||
| /// The initial [Uri] is resolved by first parsing the platform dispatcher’s | ||
| /// [defaultRouteName]. If that route has no path segments (is effectively | ||
| /// empty) and [Coordinator.initialRoutePath] is non-null, the coordinator’s | ||
| /// [initialRoutePath] is used instead. When an initial URI is chosen but has | ||
| /// an empty path, it is normalized to `/`, and if no usable platform route | ||
| /// can be parsed the URI defaults to `/`. | ||
| class CoordinatorRouteInformationProvider | ||
| extends PlatformRouteInformationProvider { | ||
| CoordinatorRouteInformationProvider({required Coordinator coordinator}) | ||
| : _coordinator = coordinator, | ||
| super( | ||
| initialRouteInformation: RouteInformation( | ||
| uri: resolveInitialUri( | ||
| WidgetsBinding.instance.platformDispatcher.defaultRouteName, | ||
| coordinator.initialRoutePath, | ||
| ), | ||
| ), | ||
| ); | ||
|
|
||
| final Coordinator _coordinator; | ||
|
|
||
| Coordinator get coordinator => _coordinator; | ||
|
|
||
| @visibleForTesting | ||
| static Uri resolveInitialUri(String? platformRouteName, Uri? initialUri) { | ||
| final defaultUri = Uri.tryParse(platformRouteName ?? ''); | ||
|
|
||
| // If the platform route name can't be parsed, fall back to the provided | ||
| // initialUri when available; otherwise, use the root route. | ||
| if (defaultUri == null) { | ||
| return initialUri ?? Uri.parse('/'); | ||
| } | ||
|
|
||
| if (defaultUri.pathSegments.isEmpty && initialUri != null) { | ||
| return initialUri; | ||
| } | ||
|
|
||
| if (defaultUri.hasEmptyPath) { | ||
| return defaultUri.replace(path: '/'); | ||
| } | ||
|
|
||
| return defaultUri; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,171 @@ | ||
| import 'package:flutter/material.dart'; | ||
| import 'package:flutter_test/flutter_test.dart'; | ||
| import 'package:zenrouter/zenrouter.dart'; | ||
|
|
||
| abstract class AppRoute extends RouteTarget with RouteUnique { | ||
| @override | ||
| Uri toUri(); | ||
| } | ||
|
|
||
| class HomeRoute extends AppRoute { | ||
| @override | ||
| Uri toUri() => Uri.parse('/'); | ||
|
|
||
| @override | ||
| Widget build(covariant TestCoordinator coordinator, BuildContext context) { | ||
| return const Scaffold(body: Text('Home')); | ||
| } | ||
|
|
||
| @override | ||
| List<Object?> get props => []; | ||
| } | ||
|
|
||
| class TestCoordinator extends Coordinator<AppRoute> { | ||
| TestCoordinator({super.initialRoutePath}); | ||
|
|
||
| @override | ||
| AppRoute parseRouteFromUri(Uri uri) { | ||
| return HomeRoute(); | ||
| } | ||
| } | ||
|
|
||
| void main() { | ||
| TestWidgetsFlutterBinding.ensureInitialized(); | ||
|
|
||
| group('CoordinatorRouteInformationProvider', () { | ||
| test('creates with coordinator and default initial route', () { | ||
| final coordinator = TestCoordinator(); | ||
| final provider = CoordinatorRouteInformationProvider( | ||
| coordinator: coordinator, | ||
| ); | ||
|
|
||
| expect(provider.coordinator, equals(coordinator)); | ||
| expect(provider.value.uri.toString(), equals('/')); | ||
| }); | ||
|
|
||
| test('reports new route information', () { | ||
| final coordinator = TestCoordinator(); | ||
| final provider = CoordinatorRouteInformationProvider( | ||
| coordinator: coordinator, | ||
| ); | ||
|
|
||
| provider.routerReportsNewRouteInformation( | ||
| RouteInformation(uri: Uri.parse('/new-route')), | ||
| ); | ||
|
|
||
| expect(provider.value.uri.toString(), equals('/new-route')); | ||
| }); | ||
|
|
||
| test('inherits from PlatformRouteInformationProvider', () { | ||
| final coordinator = TestCoordinator(); | ||
| final provider = CoordinatorRouteInformationProvider( | ||
| coordinator: coordinator, | ||
| ); | ||
|
|
||
| expect(provider, isA<PlatformRouteInformationProvider>()); | ||
| expect(provider, isA<RouteInformationProvider>()); | ||
| }); | ||
| }); | ||
|
|
||
| group('resolveInitialUri', () { | ||
| test('returns "/" when platformRouteName and initialUri are null', () { | ||
| final result = CoordinatorRouteInformationProvider.resolveInitialUri( | ||
| null, | ||
| null, | ||
| ); | ||
| expect(result.toString(), equals('/')); | ||
| }); | ||
|
|
||
| test( | ||
| 'returns "/" when platformRouteName is empty and initialUri is null', | ||
| () { | ||
| final result = CoordinatorRouteInformationProvider.resolveInitialUri( | ||
| '', | ||
| null, | ||
| ); | ||
| expect(result.toString(), equals('/')); | ||
| }, | ||
| ); | ||
|
|
||
| test( | ||
| 'returns initialUri when platformRouteName is empty and initialUri is provided', | ||
| () { | ||
| final initialUri = Uri.parse('/initial'); | ||
| final result = CoordinatorRouteInformationProvider.resolveInitialUri( | ||
| '', | ||
| initialUri, | ||
| ); | ||
| expect(result, equals(initialUri)); | ||
| }, | ||
| ); | ||
|
|
||
| test( | ||
| 'returns defaultUri with "/" path when platformRouteName has empty path and initialUri is null', | ||
| () { | ||
| final result = CoordinatorRouteInformationProvider.resolveInitialUri( | ||
| 'https://example.com', | ||
| null, | ||
| ); | ||
| expect(result.toString(), equals('https://example.com/')); | ||
| }, | ||
| ); | ||
|
|
||
| test( | ||
| 'returns initialUri when platformRouteName has empty path and initialUri is provided', | ||
| () { | ||
| final initialUri = Uri.parse('/custom'); | ||
| final result = CoordinatorRouteInformationProvider.resolveInitialUri( | ||
| 'https://example.com', | ||
| initialUri, | ||
| ); | ||
| expect(result, equals(initialUri)); | ||
| }, | ||
| ); | ||
|
|
||
| test( | ||
| 'returns platformRouteName when it has non-empty path and initialUri is null', | ||
| () { | ||
| final result = CoordinatorRouteInformationProvider.resolveInitialUri( | ||
| '/platform', | ||
| null, | ||
| ); | ||
| expect(result.toString(), equals('/platform')); | ||
| }, | ||
| ); | ||
|
|
||
| test( | ||
| 'returns platformRouteName when it has non-empty path and initialUri is provided', | ||
| () { | ||
| final initialUri = Uri.parse('/initial'); | ||
| final result = CoordinatorRouteInformationProvider.resolveInitialUri( | ||
| '/platform', | ||
| initialUri, | ||
| ); | ||
| expect(result.toString(), equals('/platform')); | ||
| }, | ||
| ); | ||
|
|
||
| test( | ||
| 'returns "/" when platformRouteName is invalid and initialUri is null', | ||
| () { | ||
| final result = CoordinatorRouteInformationProvider.resolveInitialUri( | ||
| '::invalid::', | ||
| null, | ||
| ); | ||
| expect(result.toString(), equals('/')); | ||
| }, | ||
| ); | ||
|
|
||
| test( | ||
| 'returns "/" when platformRouteName is invalid and initialUri is provided', | ||
| () { | ||
| final initialUri = Uri.parse('/initial'); | ||
| final result = CoordinatorRouteInformationProvider.resolveInitialUri( | ||
| '::invalid::', | ||
| initialUri, | ||
| ); | ||
| expect(result.toString(), equals('/initial')); | ||
| }, | ||
| ); | ||
| }); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CHANGELOG entry says
hasEmptyPathwas reverted back topathSegments.isEmptyinresolveInitialUri, but the current implementation still useshasEmptyPath(for the empty-path normalization branch). Please adjust the wording to match what the code actually does so readers aren’t misled.