flutter / flutter

Flutter makes it easy and fast to build beautiful apps for mobile and beyond
https://flutter.dev
BSD 3-Clause "New" or "Revised" License
166.66k stars 27.6k forks source link

[go_router_builder][go_router] Exception thrown when parent route has `$extra` param #106121

Open khwgit opened 2 years ago

khwgit commented 2 years ago

The generated extensions will pass route object to extra and read extra by casting it directly, which may cause type casting exception. The generated ParentRoute extension code of the example:

extension $ParentRouteExtension on ParentRoute {
  static ParentRoute _fromState(GoRouterState state) => ParentRoute(
        $extra: state.extra as int?, // When state.extra is [ChildRoute], it will throw exception.
      );

  String get location => GoRouteData.$location(
        '/',
      );

  void go(BuildContext context) => context.go(location, extra: this);

  void push(BuildContext context) => context.push(location, extra: this);
}

The example is using go_router 4.0.0 and go_router_builder 1.0.5.

(Another issue found when writing this example, if I add const to ParentRoute, it will generate something like this: :'(

extension $ParentRouteExtension on ParentRoute {
  static ParentRoute _fromState(GoRouterState state) => const ParentRoute( // Can't be `const` here
        $extra: state.extra as int?,
      );

  ...
}

Steps to Reproduce

  1. Execute flutter pub run build_runner watch --delete-conflicting-outputs to generate extensions
  2. Execute flutter run on the code sample
  3. Click Go to child page button

Expected 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'; @TypedGoRoute( path: '/', routes: >[ TypedGoRoute( path: 'child', ) ], ) class ParentRoute extends GoRouteData { ParentRoute({this.$extra}); final int? $extra; @override Widget build(BuildContext context) => const ParentPage(); } class ChildRoute extends GoRouteData { const ChildRoute(); @override Widget build(BuildContext context) => const ChildPage(); } 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( routeInformationParser: _router.routeInformationParser, routerDelegate: _router.routerDelegate, routeInformationProvider: _router.routeInformationProvider, ); } } class ParentPage extends StatelessWidget { const ParentPage({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Center( child: ElevatedButton( onPressed: () => const ChildRoute().go(context), child: const Text('Go to child page'), ), ); } } class ChildPage extends StatelessWidget { const ChildPage({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return const Center( child: Text('Child page'), ); } } ```
Logs ``` 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 ════════════════════════════════════════════════════════════════════════════════════════════════════ ``` ``` Analyzing flutter_application_1... No issues found! (ran in 1.5s) ``` ``` [✓] Flutter (Channel stable, 3.0.1, on macOS 12.1 21C52 darwin-arm, locale en-HK) • Flutter version 3.0.1 at /Users/hei/Development/sdk/flutter • Upstream repository https://github.com/flutter/flutter.git • Framework revision fb57da5f94 (4 weeks ago), 2022-05-19 15:50:29 -0700 • Engine revision caaafc5604 • Dart version 2.17.1 • DevTools version 2.12.2 [✓] Android toolchain - develop for Android devices (Android SDK version 31.0.0) • Android SDK at /Users/hei/Library/Android/sdk • Platform android-31, build-tools 31.0.0 • Java binary at: /Applications/Android Studio.app/Contents/jre/Contents/Home/bin/java • Java version OpenJDK Runtime Environment (build 11.0.11+0-b60-7772763) • All Android licenses accepted. [✓] Xcode - develop for iOS and macOS (Xcode 13.3) • Xcode at /Applications/Xcode.app/Contents/Developer • CocoaPods version 1.11.2 [✓] Chrome - develop for the web • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome [✓] Android Studio (version 2021.1) • Android Studio at /Applications/Android Studio.app/Contents • Flutter plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/9212-flutter • Dart plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/6351-dart • Java version OpenJDK Runtime Environment (build 11.0.11+0-b60-7772763) [✓] VS Code (version 1.68.1) • VS Code at /Applications/Visual Studio Code.app/Contents • Flutter extension version 3.42.0 [✓] Connected device (2 available) • macOS (desktop) • macos • darwin-arm64 • macOS 12.1 21C52 darwin-arm • Chrome (web) • chrome • web-javascript • Google Chrome 102.0.5005.115 [✓] HTTP Host Availability • All required HTTP hosts are available • No issues found! ```
danagbemava-nc commented 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.

videos | $anyRouteExtra/extra | $extra | | -- | -- | |
code sample ```dart import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; part 'main.g.dart'; @TypedGoRoute( path: '/', routes: >[ TypedGoRoute( path: 'child', ) ], ) class ParentRoute extends GoRouteData { // ParentRoute({this.$anyRouteExtra}); ParentRoute({this.$extra}); // final int? $anyRouteExtra; final int? $extra; @override Widget build(BuildContext context) => const ParentPage(); } class ChildRoute extends GoRouteData { const ChildRoute(); @override Widget build(BuildContext context) => const ChildPage(); } 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( routeInformationParser: _router.routeInformationParser, routerDelegate: _router.routerDelegate, routeInformationProvider: _router.routeInformationProvider, ); } } class ParentPage extends StatelessWidget { const ParentPage({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Center( child: ElevatedButton( onPressed: () => const ChildRoute().go(context), child: const Text('Go to child page'), ), ); } } class ChildPage extends StatelessWidget { const ChildPage({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return const Center( child: Text('Child page'), ); } } ```
flutter doctor -v ``` [✓] Flutter (Channel stable, 3.0.2, on macOS 12.3.1 21E258 darwin-arm, locale en-GB) • Flutter version 3.0.2 at /Users/nexus/dev/sdks/flutter • Upstream repository https://github.com/flutter/flutter.git • Framework revision cd41fdd495 (9 days ago), 2022-06-08 09:52:13 -0700 • Engine revision f15f824b57 • Dart version 2.17.3 • DevTools version 2.12.2 [✓] Android toolchain - develop for Android devices (Android SDK version 31.0.0) • Android SDK at /Users/nexus/Library/Android/sdk • Platform android-32, build-tools 31.0.0 • Java binary at: /Applications/Android Studio.app/Contents/jre/Contents/Home/bin/java • Java version OpenJDK Runtime Environment (build 11.0.12+0-b1504.28-7817840) • All Android licenses accepted. [✓] Xcode - develop for iOS and macOS (Xcode 13.3.1) • Xcode at /Applications/Xcode.app/Contents/Developer • CocoaPods version 1.11.3 [✓] Chrome - develop for the web • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome [✓] Android Studio (version 2021.2) • Android Studio at /Applications/Android Studio.app/Contents • Flutter plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/9212-flutter • Dart plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/6351-dart • Java version OpenJDK Runtime Environment (build 11.0.12+0-b1504.28-7817840) [✓] VS Code (version 1.68.1) • VS Code at /Applications/Visual Studio Code.app/Contents • Flutter extension version 3.42.0 [✓] Connected device (3 available) • Nexus (mobile) • 00008020-001875E83A38002E • ios • iOS 15.5 19F77 • macOS (desktop) • macos • darwin-arm64 • macOS 12.3.1 21E258 darwin-arm • Chrome (web) • chrome • web-javascript • Google Chrome 102.0.5005.115 [✓] HTTP Host Availability • All required HTTP hosts are available • No issues found! ``` ``` [✓] Flutter (Channel master, 3.1.0-0.0.pre.1287, on macOS 12.3.1 21E258 darwin-arm, locale en-GB) • Flutter version 3.1.0-0.0.pre.1287 at /Users/nexus/dev/sdks/flutters • Upstream repository https://github.com/flutter/flutter.git • Framework revision d08a1b02ae (3 hours ago), 2022-06-17 01:38:09 -0400 • Engine revision ee71e31c36 • Dart version 2.18.0 (build 2.18.0-190.0.dev) • DevTools version 2.14.0 [✓] Android toolchain - develop for Android devices (Android SDK version 31.0.0) • Android SDK at /Users/nexus/Library/Android/sdk • Platform android-32, build-tools 31.0.0 • Java binary at: /Applications/Android Studio.app/Contents/jre/Contents/Home/bin/java • Java version OpenJDK Runtime Environment (build 11.0.12+0-b1504.28-7817840) • All Android licenses accepted. [✓] Xcode - develop for iOS and macOS (Xcode 13.3.1) • Xcode at /Applications/Xcode.app/Contents/Developer • Build 13E500a • CocoaPods version 1.11.3 [✓] Chrome - develop for the web • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome [✓] Android Studio (version 2021.2) • Android Studio at /Applications/Android Studio.app/Contents • Flutter plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/9212-flutter • Dart plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/6351-dart • Java version OpenJDK Runtime Environment (build 11.0.12+0-b1504.28-7817840) [✓] VS Code (version 1.68.1) • VS Code at /Applications/Visual Studio Code.app/Contents • Flutter extension version 3.42.0 [✓] Connected device (2 available) • macOS (desktop) • macos • darwin-arm64 • macOS 12.3.1 21E258 darwin-arm • Chrome (web) • chrome • web-javascript • Google Chrome 102.0.5005.115 ! Error: Nexus is not connected. Xcode will continue when Nexus is connected. (code -13) [✓] HTTP Host Availability • All required HTTP hosts are available • No issues found! ```
logs ``` Launching lib/main.dart on macOS in debug mode... objc[95432]: Class AppleTypeCRetimerRestoreInfoHelper is implemented in both /usr/lib/libauthinstall.dylib (0x1ea1d5eb0) and /Library/Apple/System/Library/PrivateFrameworks/MobileDevice.framework/Versions/A/MobileDevice (0x1048604f8). One of the two will be used. Which one is undefined. objc[95432]: Class AppleTypeCRetimerFirmwareAggregateRequestCreator is implemented in both /usr/lib/libauthinstall.dylib (0x1ea1d5f00) and /Library/Apple/System/Library/PrivateFrameworks/MobileDevice.framework/Versions/A/MobileDevice (0x104860548). One of the two will be used. Which one is undefined. objc[95432]: Class AppleTypeCRetimerFirmwareRequestCreator is implemented in both /usr/lib/libauthinstall.dylib (0x1ea1d5f50) and /Library/Apple/System/Library/PrivateFrameworks/MobileDevice.framework/Versions/A/MobileDevice (0x104860598). One of the two will be used. Which one is undefined. objc[95432]: Class ATCRTRestoreInfoFTABFile is implemented in both /usr/lib/libauthinstall.dylib (0x1ea1d5fa0) and /Library/Apple/System/Library/PrivateFrameworks/MobileDevice.framework/Versions/A/MobileDevice (0x1048605e8). One of the two will be used. Which one is undefined. objc[95432]: Class AppleTypeCRetimerFirmwareCopier is implemented in both /usr/lib/libauthinstall.dylib (0x1ea1d5ff0) and /Library/Apple/System/Library/PrivateFrameworks/MobileDevice.framework/Versions/A/MobileDevice (0x104860638). One of the two will be used. Which one is undefined. objc[95432]: Class ATCRTRestoreInfoFTABSubfile is implemented in both /usr/lib/libauthinstall.dylib (0x1ea1d6040) and /Library/Apple/System/Library/PrivateFrameworks/MobileDevice.framework/Versions/A/MobileDevice (0x104860688). One of the two will be used. Which one is undefined. --- xcodebuild: WARNING: Using the first of multiple matching destinations: { platform:macOS, arch:arm64, id:00008103-000329201AD1001E } { platform:macOS, arch:x86_64, id:00008103-000329201AD1001E } Connecting to VM Service at ws://127.0.0.1:53354/y7V7tYos8Mo=/ws [GoRouter] known full paths for routes: [GoRouter] => / [GoRouter] => /child [GoRouter] setting initial location null [GoRouter] MaterialApp found [GoRouter] going to /child flutter: ══╡ EXCEPTION CAUGHT BY GOROUTER ╞══════════════════════════════════════════════════════════════════ flutter: The following _Exception was thrown Exception during GoRouter navigation: flutter: Exception: type 'ChildRoute' is not a subtype of type 'int?' in type cast flutter: flutter: When the exception was thrown, this was the stack: flutter: #0 $ParentRouteExtension._fromState flutter: #1 GoRouteData.$route.factoryImpl flutter: #2 GoRouteData.$route.pageBuilder flutter: #3 GoRouterDelegate.getPages flutter: #4 _SyncIterator.moveNext (dart:core-patch/core_patch.dart:186:26) flutter: #5 new _GrowableList._ofOther (dart:core-patch/growable_array.dart:202:26) flutter: #6 new _GrowableList.of (dart:core-patch/growable_array.dart:152:26) flutter: #7 new List.of (dart:core-patch/array_patch.dart:51:28) flutter: #8 Iterable.toList (dart:core/iterable.dart:470:12) flutter: #9 GoRouterDelegate._builder flutter: #10 GoRouterDelegate.build flutter: #11 Builder.build flutter: #12 StatelessElement.build flutter: #13 ComponentElement.performRebuild flutter: #14 Element.rebuild flutter: #15 StatelessElement.update flutter: #16 Element.updateChild flutter: #17 ComponentElement.performRebuild flutter: #18 Element.rebuild flutter: #19 ProxyElement.update flutter: #20 Element.updateChild flutter: #21 ComponentElement.performRebuild flutter: #22 Element.rebuild flutter: #23 ProxyElement.update flutter: #24 Element.updateChild flutter: #25 ComponentElement.performRebuild flutter: #26 StatefulElement.performRebuild flutter: #27 Element.rebuild flutter: #28 BuildOwner.buildScope flutter: #29 WidgetsBinding.drawFrame flutter: #30 RendererBinding._handlePersistentFrameCallback flutter: #31 SchedulerBinding._invokeFrameCallback flutter: #32 SchedulerBinding.handleDrawFrame flutter: #33 SchedulerBinding._handleDrawFrame flutter: #34 _invoke (dart:ui/hooks.dart:148:13) flutter: #35 PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:318:5) flutter: #36 _drawFrame (dart:ui/hooks.dart:115:31) flutter: ════════════════════════════════════════════════════════════════════════════════════════════════════ ```
khwgit commented 2 years ago

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:

Code sample ``` dart import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; part 'main.g.dart'; class Data { const Data(); } @TypedGoRoute( path: '/', routes: >[ TypedGoRoute( path: 'child', ) ], ) class ParentRoute extends GoRouteData { ParentRoute({this.$extra}); final Data? $extra; @override Widget build(BuildContext context) => const ParentPage(); } class ChildRoute extends GoRouteData { const ChildRoute(); @override Widget build(BuildContext context) => const ChildPage(); } 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( routeInformationParser: _router.routeInformationParser, routerDelegate: _router.routerDelegate, routeInformationProvider: _router.routeInformationProvider, ); } } class ParentPage extends StatelessWidget { const ParentPage({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Center( child: ElevatedButton( onPressed: () => const ChildRoute().go(context), child: const Text('Go to child page'), ), ); } } class ChildPage extends StatelessWidget { const ChildPage({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return const Center( child: Text('Child page'), ); } } ```
logs ``` Launching lib/main.dart on Chrome in debug mode... This app is linked to the debug service: ws://127.0.0.1:53362/uaeZoQ-2jDY=/ws Debug service listening on ws://127.0.0.1:53362/uaeZoQ-2jDY=/ws 💪 Running with sound null safety 💪 Connecting to VM Service at ws://127.0.0.1:53362/uaeZoQ-2jDY=/ws Flutter Web Bootstrap: Programmatic [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 'Data?', 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 ════════════════════════════════════════════════════════════════════════════════════════════════════ ```
Ayman-Barghout commented 2 years ago

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.

Skogsfrae commented 2 years ago

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',
    ),
  ],
)
DFelten commented 2 years ago

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.

devberkay commented 1 year ago

I am also experiencing the same issue in both .push and .go , update please.

joo6077 commented 1 year ago

any updates?

gotecq-phuctran commented 1 year ago

any updates

gotecq-phuctran commented 1 year ago

it've been a year, fix it please 😢

quoc-huynh-cosee commented 1 year ago

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'),
        ),
      ),
    );
  }
}
joo6077 commented 11 months ago

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.

  1. YOUR_INSTANCE to JSON
  2. Encode JSON => string
  3. Decode string to JSON
  4. Convert JSON to YOUR_INSTANCE.fromJson()
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();
}
christopherarm commented 8 months ago

Same issue

iwontknow commented 5 months ago

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?

theSharpestTool commented 5 months ago

It's not the best solution, but you can just make the parent extra field dynamic, it should work.

final dynamic $extra;
AhmedLSayed9 commented 1 month ago

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'),
        ),
      ),
    );
  }
}