Open khwgit opened 2 years ago
Issue is reproducible using the code sample provided.
It seems like this is an issue with using $extra
specifically.
I tried $anyRouteExtra
and extra
, but it didn't reproduce in either case.
The $extra
param is used to pass an extra object while $anyRouteExtra
is treated as a query param. You may find the differences with the generated code:
Using $extra
:
extension $ParentRouteExtension on ParentRoute {
static ParentRoute _fromState(GoRouterState state) => ParentRoute(
$extra: state.extra as int?, // When state.extra is [ChildRoute], it will throw exception.
);
...
}
Using $anyRouteExtra
extension $ParentRouteExtension on ParentRoute {
static ParentRoute _fromState(GoRouterState state) => ParentRoute(
$anyRouteExtra: _$convertMapValue(
r'$any-route-extra', state.queryParams, int.parse),
);
...
}
Here's another example that requires $extra
:
This works when using push
but throws an exception when using go
, so I reverted to using push
for now but it'd be great to get this fixed 🙏
The above workaround doesn't work with latest versions.
I'm facing the same problem using both push
and go
.
As a workaround I'm currently avoiding nesting such routes like so:
from this:
@TypedGoRoute<EntityListPageData>(
path: 'entity',
routes: [
TypedGoRoute<EntityDetailsPageData>(
path: ':id',
routes: [
TypedGoRoute<RulesPageData>(
path: 'rules',
),
TypedGoRoute<StaffPageData>(
path: 'staff',
),
TypedGoRoute<FeedbackPageData>(
path: 'feedback',
),
]
),
],
)
to this:
@TypedGoRoute<EntityListPageData>(
path: 'entity',
routes: [
TypedGoRoute<EntityDetailsPageData>(
path: ':id',
),
TypedGoRoute<RulesPageData>(
path: ':id/rules',
),
TypedGoRoute<StaffPageData>(
path: ':id/staff',
),
TypedGoRoute<FeedbackPageData>(
path: ':id/feedback',
),
],
)
Same issue here with go_router: ^5.1.1
and go_router_builder: ^1.0.14
and . When using $extra
on a route and an other $extra
on the sub route the sub route is no longer working. With version go_router: ^4.2.8
and go_router_builder: ^1.0.8
it's working.
An attempt is made to use the type of the parent route for the $extra
cast.
[GoRouter] pushing /xbox-games/xbox-game-achievements/306230746/xbox-achievement/1
[log] type 'XboxGameAchievementRoute' is not a subtype of type 'XboxGameAchievementsExtra?' in type cast
@TypedGoRoute<XboxGameListRoute>(
path: 'xbox-games',
routes: [
TypedGoRoute<XboxGameAchievementsRoute>(
path: 'xbox-game-achievements/:gameId',
routes: [
TypedGoRoute<XboxGameAchievementRoute>(
path: 'xbox-achievement/:achievementId',
),
],
),
],
)
class XboxGameListRoute extends GoRouteData {
const XboxGameListRoute();
@override
Widget build(BuildContext context) => Container();
}
class XboxGameAchievementsRoute extends GoRouteData {
const XboxGameAchievementsRoute({this.$extra});
final XboxGameAchievementsExtra? $extra;
@override
Widget build(BuildContext context) => Container();
}
class XboxGameAchievementRoute extends GoRouteData {
const XboxGameAchievementRoute({
this.$extra,
});
final XboxAchievementData? $extra;
@override
Widget build(BuildContext context) => Container();
}
With such an error the generator is currently not usable for extra params in routes.
I am also experiencing the same issue in both .push and .go , update please.
any updates?
any updates
it've been a year, fix it please 😢
Any updates on this? Is there a workaround? I'm also receiving this without using the go_router_builder. Here is a minimal code:
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
MyApp({super.key});
final _routerConfig = GoRouter(
initialLocation: '/',
routes: [
GoRoute(
name: 'home',
path: '/',
builder: (_, __) => const HomePage(),
routes: [
GoRoute(
name: 'pageA',
path: 'pageA',
builder: (_, state) => PageA(
arguments: state.extra as PageAArguments,
),
routes: [
GoRoute(
name: 'pageB',
path: 'pageB',
builder: (_, state) => PageB(
arguments: state.extra as PageBArguments,
),
),
],
),
],
),
],
);
@override
Widget build(BuildContext context) {
return MaterialApp.router(
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
routerConfig: _routerConfig,
);
}
}
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('HomePage'),
),
body: Center(
child: TextButton(
onPressed: () => context.goNamed(
'pageA',
extra: const PageAArguments('Page A'),
),
child: const Text('GO'),
),
),
);
}
}
class PageAArguments {
const PageAArguments(this.value);
final String value;
}
class PageA extends StatelessWidget {
const PageA({required this.arguments, super.key});
final PageAArguments arguments;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Page A'),
),
body: Center(
child: TextButton(
onPressed: () => context.goNamed(
'pageB',
extra: const PageBArguments('Page B'),
),
child: const Text('GO'),
),
),
);
}
}
class PageBArguments {
const PageBArguments(this.value);
final String value;
}
class PageB extends StatelessWidget {
const PageB({required this.arguments, super.key});
final PageBArguments arguments;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Page B'),
),
body: Center(
child: TextButton(
onPressed: () => context.goNamed('home'),
child: const Text('GO'),
),
),
);
}
}
It's been quite a long time. There were ways to solve this issue. Saving the instance we made in state management and then getting it on the next page, or calling rest API twice is never acceptable to me. I came up with a solution inspired by an API call. What if we pass the instance as a JSON string? Because go_router_builder supports primary types.
BenefitResultRoute(lastUsedStoreJsonStr: jsonEncode(lastUsedStore.toJson())).go(context); // 1, 2
class HomeRoute extends GoRouteData {
final String? benefitPackResultsJsonStr;
const HomeRoute({this.benefitPackResultsJsonStr});
@override
Widget build(BuildContext context, GoRouterState state) {
final jsonDecodeResult =
jsonDecode(benefitPackResultsJsonStr ?? '[]') as List; // 3
final benefitPackResults = jsonDecodeResult
.map((e) =>
BenefitPackageSuccessEntity.fromJson(e as Map<String, dynamic>))
.toList(); // 4
return HomeView(
benefitPackResults: benefitPackResults,
);
}
}
class HomeView extends ConsumerStatefulWidget {
final List<BenefitPackageSuccessEntity> benefitPackResults;
const HomeView({
super.key,
required this.benefitPackResults,
});
@override
ConsumerState<HomeView> createState() => _HomeViewState();
}
Same issue
So i was trying to fix something for the last 3 hours, just to come here and see that its an issue. Is there any Update?
It's not the best solution, but you can just make the parent extra field dynamic
, it should work.
final dynamic $extra;
Here's an updated example that still produces the issue with latest version 14.2.7
:
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
part 'main.g.dart';
@TypedGoRoute<HomeRoute>(
path: '/',
routes: <TypedGoRoute<GoRouteData>>[
TypedGoRoute<ScreenARoute>(
path: 'screen-a',
routes: <TypedGoRoute<GoRouteData>>[
TypedGoRoute<ScreenBRoute>(
path: 'screen-B',
)
],
)
],
)
class HomeRoute extends GoRouteData {
const HomeRoute();
@override
Widget build(BuildContext context, GoRouterState state) => const HomeScreen();
}
class ScreenARoute extends GoRouteData {
const ScreenARoute(this.$extra);
final ScreenAParams $extra;
@override
Widget build(BuildContext context, GoRouterState state) => ScreenA(params: $extra);
}
class ScreenBRoute extends GoRouteData {
const ScreenBRoute(this.$extra);
final ScreenBParams $extra;
@override
Widget build(BuildContext context, GoRouterState state) => ScreenB(params: $extra);
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
MyApp({Key? key}) : super(key: key);
late final _router = GoRouter(
debugLogDiagnostics: true,
routes: $appRoutes,
);
@override
Widget build(BuildContext context) {
return MaterialApp.router(routerConfig: _router);
}
}
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('HomeScreen'),
),
body: Center(
child: TextButton(
onPressed: () {
const ScreenARoute(ScreenAParams('a-param')).go(context);
},
child: const Text('GO to Screen-A'),
),
),
);
}
}
class ScreenAParams {
const ScreenAParams(this.value);
final String value;
}
class ScreenA extends StatelessWidget {
const ScreenA({required this.params, super.key});
final ScreenAParams params;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Screen A'),
),
body: Center(
child: TextButton(
onPressed: () {
const ScreenBRoute(ScreenBParams('B-param')).go(context);
},
child: const Text('GO to Screen-B'),
),
),
);
}
}
class ScreenBParams {
const ScreenBParams(this.value);
final String value;
}
class ScreenB extends StatelessWidget {
const ScreenB({required this.params, super.key});
final ScreenBParams params;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Screen B'),
),
body: Center(
child: TextButton(
onPressed: () => const HomeRoute().go(context),
child: const Text('GO to Home'),
),
),
);
}
}
The generated extensions will pass route object to
extra
and read extra by casting it directly, which may cause type casting exception. The generatedParentRoute
extension code of the example:The example is using
go_router 4.0.0
andgo_router_builder 1.0.5
.(Another issue found when writing this example, if I add
const
toParentRoute
, it will generate something like this: :'(Steps to Reproduce
flutter pub run build_runner watch --delete-conflicting-outputs
to generate extensionsflutter run
on the code sampleGo to child page
buttonExpected results: Display
ChildPage
Actual results: Exception thrown.
Code sample
```dart import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; part 'main.g.dart'; @TypedGoRouteLogs
``` Launching lib/main.dart on Chrome in debug mode... This app is linked to the debug service: ws://127.0.0.1:62216/VJ0Ejc_XtBE=/ws Debug service listening on ws://127.0.0.1:62216/VJ0Ejc_XtBE=/ws 💪 Running with sound null safety 💪 Connecting to VM Service at ws://127.0.0.1:62216/VJ0Ejc_XtBE=/ws Flutter Web Bootstrap: Programmatic Restarted application in 983ms. [GoRouter] known full paths for routes: [GoRouter] => / [GoRouter] => /child [GoRouter] setting initial location null [GoRouter] MaterialApp found [GoRouter] going to /child ══╡ EXCEPTION CAUGHT BY GOROUTER ╞══════════════════════════════════════════════════════════════════ The following _Exception was thrown Exception during GoRouter navigation: Exception: Expected a value of type 'int?', but got one of type 'ChildRoute' When the exception was thrown, this was the stack: dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart 251:49 throw_ dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart 84:3 castError dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart 452:10 cast dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/types.dart 367:9 as packages/flutter_application_1/main.g.dart 26:29 $36ParentRouteExtension$124_fromState packages/go_router/src/route_data.dart 79:53 factoryImpl packages/go_router/src/route_data.dart 86:28 pageBuilder packages/go_router/src/go_router_delegate.dart 239:27 getPages dart-sdk/lib/_internal/js_dev_runtime/patch/core_patch.dart 586:14 of dart-sdk/lib/core/iterable.dart 470:12 toList packages/go_router/src/go_router_delegate.dart 113:42 [_builder] packages/go_router/src/go_router_delegate.dart 90:41 build packages/flutter/src/widgets/basic.dart 7371:48 build packages/flutter/src/widgets/framework.dart 4876:22 build packages/flutter/src/widgets/framework.dart 4806:15 performRebuild packages/flutter/src/widgets/framework.dart 4529:5 rebuild packages/flutter/src/widgets/framework.dart 4883:5 update packages/flutter/src/widgets/framework.dart 3530:14 updateChild packages/flutter/src/widgets/framework.dart 4832:16 performRebuild packages/flutter/src/widgets/framework.dart 4529:5 rebuild packages/flutter/src/widgets/framework.dart 5154:5 update packages/flutter/src/widgets/framework.dart 3530:14 updateChild packages/flutter/src/widgets/framework.dart 4832:16 performRebuild packages/flutter/src/widgets/framework.dart 4529:5 rebuild packages/flutter/src/widgets/framework.dart 5154:5 update packages/flutter/src/widgets/framework.dart 3530:14 updateChild packages/flutter/src/widgets/framework.dart 4832:16 performRebuild packages/flutter/src/widgets/framework.dart 4977:11 performRebuild packages/flutter/src/widgets/framework.dart 4529:5 rebuild packages/flutter/src/widgets/framework.dart 2659:18 buildScope packages/flutter/src/widgets/binding.dart 891:9 drawFrame packages/flutter/src/rendering/binding.dart 370:5 [_handlePersistentFrameCallback] packages/flutter/src/scheduler/binding.dart 1146:15 [_invokeFrameCallback] packages/flutter/src/scheduler/binding.dart 1083:9 handleDrawFrame packages/flutter/src/scheduler/binding.dart 997:5 [_handleDrawFrame] lib/_engine/engine/platform_dispatcher.dart 1090:13 invoke lib/_engine/engine/platform_dispatcher.dart 160:5 invokeOnDrawFrame lib/_engine/engine/initialization.dart 194:45