Skip to content
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

[go_router] Fail to restore widget states after "restart and restore" when using go_router #117683

Open
miquelbeltran opened this issue Dec 27, 2022 · 5 comments
Labels
found in release: 3.3 Found to occur in 3.3 found in release: 3.7 Found to occur in 3.7 has reproducible steps The issue has been confirmed reproducible and is ready to work on p: go_router The go_router package P2 Important issues not at the top of the work list package flutter/packages repository. See also p: labels. team-go_router Owned by Go Router team triaged-go_router Triaged by Go Router team

Comments

@miquelbeltran
Copy link
Member

Steps to Reproduce

Given two similar MaterialApp, one using go_router and one who doesn't, the one using go_router is unable to restore the screen state when the app is restarted.

The best way to see this problem is by running the included code sample.

Their implementation is very similar:

  MaterialApp.router(
      restorationScopeId: 'app',
      routerConfig: GoRouter(
        restorationScopeId: 'router',
        routes: [
          GoRoute(
            path: '/',
            builder: (context, state) => const ScrollScreen(),
          ),
        ],
      ),
    );

and:

  MaterialApp(
      restorationScopeId: 'app',
      home: ScrollScreen(),
    );

The one using GoRouter is unable to restore the state of the ScrollScreen when the app is restarted.

You can see this behavior with the attached widget tests in the "code" section.

I've looked for similar reported issues, but I couldn't find one that explains this exact problem:

Expected results:

Both cases should work the same way. The screen state should be restored after restart just like when using a MaterialApp without GoRouter

Actual results:

In this case, the state of the screen when using GoRouter is not restored, and this can be seen by using the included widget tests.

Code sample

Add go_router to your project, then paste the following three classes into your main.dart.

Also, copy the two widget tests into your widget_test.dart to run them.

First class is the AppGoRoute, which is a simple MaterialApp that uses GoRouter.

class AppGoRoute extends StatelessWidget {
  const AppGoRoute({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      restorationScopeId: 'app',
      title: 'Flutter Demo',
      routerConfig: GoRouter(
        restorationScopeId: 'router',
        routes: [
          GoRoute(
            path: '/',
            builder: (context, state) => const ScrollScreen(),
          ),
        ],
      ),
    );
  }
}

The second class is AppSimple, which is a simple MaterialApp that doesn't use GoRouter.

class AppSimple extends StatelessWidget {
  const AppSimple({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      restorationScopeId: 'app',
      title: 'Flutter Demo',
      home: ScrollScreen(),
    );
  }
}

ScrollScreen is a simple Scaffold with a ListView of a 1000 items.

class ScrollScreen extends StatelessWidget {
  const ScrollScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      restorationId: 'scroll-screen',
      body: ListView.builder(
        restorationId: 'list',
        itemCount: 1000,
        itemBuilder: (context, index) => ListTile(
          title: Text('Item $index'),
        ),
      ),
    );
  }
}

Test 1: Fails: This test fails, as when the screen state is restored, the scroll position is lost and is back at the item 0.

This test uses the AppGoRoute, which uses GoRouter internally to handle navigation.

  testWidgets('restoration test go_router', (WidgetTester tester) async {
    // Build our app and trigger a frame.
    await tester.pumpWidget(
      const RootRestorationScope(
        restorationId: 'root',
        child: AppGoRoute(),
      ),
    );

    expect(find.text('Item 0'), findsOneWidget);
    expect(find.text('Item 500'), findsNothing);

    await tester.scrollUntilVisible(
      find.text('Item 100'),
      10,
      maxScrolls: 10000,
    );

    expect(find.text('Item 0'), findsNothing);
    expect(find.text('Item 100'), findsOneWidget);

    await tester.restartAndRestore();

    // scroll position restored
    expect(find.text('Item 0'), findsNothing); // <-- fails here
    expect(find.text('Item 100'), findsOneWidget);
  });

Test 2: Passes.

This test uses AppSimple, same test content, and the test passes.

  testWidgets('restoration test without go_router', (WidgetTester tester) async {
    // Build our app and trigger a frame.
    await tester.pumpWidget(
      const RootRestorationScope(
        restorationId: 'root',
        child: AppSimple(),
      ),
    );

    expect(find.text('Item 0'), findsOneWidget);
    expect(find.text('Item 500'), findsNothing);

    await tester.scrollUntilVisible(
      find.text('Item 100'),
      10,
      maxScrolls: 10000,
    );

    expect(find.text('Item 0'), findsNothing);
    expect(find.text('Item 100'), findsOneWidget);

    await tester.restartAndRestore();

    // scroll position restored
    expect(find.text('Item 0'), findsNothing);
    expect(find.text('Item 100'), findsOneWidget);
  });
Logs

These are the test results:

/home/miquel/dev/tools/flutter/bin/flutter --no-color test --machine --start-paused test/widget_test.dart
Testing started at 16:17 ...

══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
The following TestFailure was thrown running a test:
Expected: no matching nodes in the widget tree
  Actual: _TextFinder:<exactly one widget with text "Item 0" (ignoring offstage widgets): Text("Item
0", dependencies: [DefaultSelectionStyle, DefaultTextStyle, MediaQuery])>
   Which: means one was found but none were expected

When the exception was thrown, this was the stack:
#4      main.<anonymous closure> (file:///home/miquel/tmp/go_route_push_shell/test/widget_test.dart:38:5)
<asynchronous suspension>
<asynchronous suspension>
(elided one frame from package:stack_trace)

This was caught by the test expectation on the following line:
  file:///home/miquel/tmp/go_route_push_shell/test/widget_test.dart line 38
The test description was:
  restoration test go_router
════════════════════════════════════════════════════════════════════════════════════════════════════

Test failed. See exception logs above.
The test description was: restoration test go_router
flutter analyze
Analyzing go_route_push_shell...                                        
No issues found! (ran in 1.0s)

flutter doctor -v
[✓] Flutter (Channel stable, 3.3.10, on Ubuntu 22.04.1 LTS 5.15.0-56-generic, locale en_US.UTF-8)
    • Flutter version 3.3.10 on channel stable at /home/miquel/dev/tools/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision 135454af32 (12 days ago), 2022-12-15 07:36:55 -0800
    • Engine revision 3316dd8728
    • Dart version 2.18.6
    • DevTools version 2.15.0

[✓] Android toolchain - develop for Android devices (Android SDK version 33.0.1)
    • Android SDK at /home/miquel/Android/Sdk
    • Platform android-33, build-tools 33.0.1
    • Java binary at:
      /home/miquel/.local/share/JetBrains/Toolbox/apps/AndroidStudio/ch-0/213.7172.25.2113.9123335/jre/bin/java
    • Java version OpenJDK Runtime Environment (build 11.0.13+0-b1751.21-8125866)
    • All Android licenses accepted.

[✓] Chrome - develop for the web
    • Chrome at google-chrome

[✓] Linux toolchain - develop for Linux desktop
    • Homebrew clang version 15.0.5
    • cmake version 3.22.1
    • ninja version 1.10.1
    • pkg-config version 0.29.2

[✓] Android Studio (version 2021.3)
    • Android Studio at /home/miquel/.local/share/JetBrains/Toolbox/apps/AndroidStudio/ch-0/213.7172.25.2113.9123335
    • 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.13+0-b1751.21-8125866)

[✓] IntelliJ IDEA Community Edition (version 2022.3)
    • IntelliJ at /home/miquel/.local/share/JetBrains/Toolbox/apps/IDEA-C/ch-0/223.8214.52
    • Flutter plugin version 71.2.6
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart

[✓] VS Code (version 1.66.2)
    • VS Code at /usr/share/code
    • Flutter extension version 3.40.0

[✓] VS Code
    • VS Code at /snap/code/current
    • Flutter extension version 3.40.0

[✓] Connected device (2 available)
    • Linux (desktop) • linux  • linux-x64      • Ubuntu 22.04.1 LTS 5.15.0-56-generic
    • Chrome (web)    • chrome • web-javascript • Google Chrome 108.0.5359.124

[✓] HTTP Host Availability
    • All required HTTP hosts are available

• No issues found!

@darshankawar darshankawar added the in triage Presently being triaged by the triage team label Dec 28, 2022
@darshankawar
Copy link
Member

Thanks for the detailed report. Seeing the same behavior using the code sample provided.

stable, master flutter doctor -v
[✓] Flutter (Channel stable, 3.3.10, on macOS 12.2.1 21D62 darwin-x64, locale
    en-GB)
    • Flutter version 3.3.10 on channel stable at
      /Users/dhs/documents/fluttersdk/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision 135454af32 (15 hours ago), 2022-12-15 07:36:55 -0800
    • Engine revision 3316dd8728
    • Dart version 2.18.6
    • DevTools version 2.15.0

[!] Xcode - develop for iOS and macOS (Xcode 12.3)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    ! Flutter recommends a minimum Xcode version of 13.
      Download the latest version or update via the Mac App Store.
    • CocoaPods version 1.11.2

[✓] Chrome - develop for the web
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] VS Code (version 1.62.0)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension version 3.21.0

[✓] Connected device (5 available)
    • SM G975F (mobile)       • RZ8M802WY0X • android-arm64   • Android 11 (API 30)
    • Darshan's iphone (mobile)  • 21150b119064aecc249dfcfe05e259197461ce23 •
      ios            • iOS 14.4.1 18D61
    • iPhone 12 Pro Max (mobile) • A5473606-0213-4FD8-BA16-553433949729     •
      ios            • com.apple.CoreSimulator.SimRuntime.iOS-14-3 (simulator)
    • macOS (desktop)            • macos                                    •
      darwin-x64     • Mac OS X 10.15.4 19E2269 darwin-x64
    • Chrome (web)               • chrome                                   •
      web-javascript • Google Chrome 98.0.4758.80

[✓] HTTP Host Availability
    • All required HTTP hosts are available

! Doctor found issues in 1 category.

[!] Flutter (Channel master, 3.7.0-13.0.pre.51, on macOS 12.2.1 21D62
    darwin-x64, locale en-GB)
    • Flutter version 3.7.0-13.0.pre.51 on channel master at
      /Users/dhs/documents/fluttersdk/flutter
    ! Warning: `flutter` on your path resolves to
      /Users/dhs/Documents/Fluttersdk/flutter/bin/flutter, which is not inside
      your current Flutter SDK checkout at
      /Users/dhs/documents/fluttersdk/flutter. Consider adding
      /Users/dhs/documents/fluttersdk/flutter/bin to the front of your path.
    ! Warning: `dart` on your path resolves to
      /Users/dhs/Documents/Fluttersdk/flutter/bin/dart, which is not inside your
      current Flutter SDK checkout at /Users/dhs/documents/fluttersdk/flutter.
      Consider adding /Users/dhs/documents/fluttersdk/flutter/bin to the front
      of your path.
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision 6441a7dc62 (3 hours ago), 2022-12-22 20:38:18 -0500
    • Engine revision 12badb5459
    • Dart version 3.0.0 (build 3.0.0-60.0.dev)
    • DevTools version 2.20.0
    • If those were intentional, you can disregard the above warnings; however
      it is recommended to use "git" directly to perform update checks and
      upgrades.

[!] Xcode - develop for iOS and macOS (Xcode 12.3)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    ! Flutter recommends a minimum Xcode version of 13.
      Download the latest version or update via the Mac App Store.
    • CocoaPods version 1.11.2

[✓] Chrome - develop for the web
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] VS Code (version 1.62.0)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension version 3.21.0

[✓] Connected device (5 available)
    • SM G975F (mobile)       • RZ8M802WY0X • android-arm64   • Android 11 (API 30)
    • Darshan's iphone (mobile)  • 21150b119064aecc249dfcfe05e259197461ce23 •
      ios            • iOS 14.4.1 18D61
    • iPhone 12 Pro Max (mobile) • A5473606-0213-4FD8-BA16-553433949729     •
      ios            • com.apple.CoreSimulator.SimRuntime.iOS-14-3 (simulator)
    • macOS (desktop)            • macos                                    •
      darwin-x64     • Mac OS X 10.15.4 19E2269 darwin-x64
    • Chrome (web)               • chrome                                   •
      web-javascript • Google Chrome 98.0.4758.80

[✓] HTTP Host Availability
    • All required HTTP hosts are available

! Doctor found issues in 1 category.



@darshankawar darshankawar added p: first party package flutter/packages repository. See also p: labels. has reproducible steps The issue has been confirmed reproducible and is ready to work on p: go_router The go_router package found in release: 3.3 Found to occur in 3.3 found in release: 3.7 Found to occur in 3.7 and removed in triage Presently being triaged by the triage team labels Dec 28, 2022
@stuartmorgan stuartmorgan added the P2 Important issues not at the top of the work list label Jan 10, 2023
@Ivan-Lytvynenko
Copy link

Try this, it worked for me. Add pageBuilder instead of builder and set restorationId for MaterialPage:

MaterialApp.router(
  restorationScopeId: 'app',
  routerConfig: GoRouter(
    restorationScopeId: 'router',
    routes: [
      GoRoute(
        path: '/',
        pageBuilder: (context, state) => const MaterialPage(
          restorationId: 'router.scroll',
          child: ScrollScreen()
        )
      ),
    ],
  ),
);

@miquelbeltran
Copy link
Member Author

miquelbeltran commented Feb 20, 2023

Thanks @Ivan-Lytvynenko your example works for me as well.

I think we should update the sample in https://github.com/flutter/packages/blob/main/packages/go_router/example/lib/others/state_restoration.dart to use pageBuilder instead.

@kengu
Copy link

kengu commented Mar 6, 2023

I've found a neat workaround for missing implementation of state restoration on web platform, see #65777 (comment).

@yfakariya
Copy link

I've found another workaround. Use subtype of GoRoute (or ShellRoute) with overriding their hashCode like this (I assume that path uniquely identifies the page):

class _RestorableGoRoute extends GoRoute {
  _RestorableGoRoute ({
    required super.path,
    super.builder,
    super.name,
    super.pageBuilder,
    super.parentNavigatorKey,
    super.redirect,
    super.routes,
  });

  @override
  int get hashCode => path.hashCode;
}

Result of study

The direct cause is restorationId of each route is eventually derived with hashCode of RouteBase and hashCode is changed after restoration and restart. Specifically,

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
found in release: 3.3 Found to occur in 3.3 found in release: 3.7 Found to occur in 3.7 has reproducible steps The issue has been confirmed reproducible and is ready to work on p: go_router The go_router package P2 Important issues not at the top of the work list package flutter/packages repository. See also p: labels. team-go_router Owned by Go Router team triaged-go_router Triaged by Go Router team
Projects
No open projects
Status: No status
Development

No branches or pull requests

7 participants