-
Notifications
You must be signed in to change notification settings - Fork 3.6k
[go_router] fix Popping state and re-rendering scaffold at the same time doesn't update the URL on web [new] #8352
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
Merged
Merged
Changes from 29 commits
Commits
Show all changes
35 commits
Select commit
Hold shift + click to select a range
63ef007
reproduce issue https://github.com/flutter/flutter/issues/150312
ahyangnb b92dc00
fix #150312
ahyangnb 614663c
A new way to fix the #6953 issue.
ahyangnb ae86e7a
Unit test for issues/150312
ahyangnb 15dd5b7
Merge branch 'main' into main
ahyangnb cad1f24
fix"info • example/test/test_stream_listener.dart:41:2 • Missing a ne…
ahyangnb 6030676
license text used by all first-party files in this repository
ahyangnb 545f133
Version 14.6.5: - Fix issue 150312.
ahyangnb 9a53bdf
Merge branch 'main' into main
ahyangnb e208cca
Merge branch 'main' into main
ahyangnb 99f7293
version 14.7.2: - Fix issue 150312.
ahyangnb 653d58e
Merge branch 'main' into main
ahyangnb ec2be8d
Merge branch 'main' into main
ahyangnb 0dae57a
14.7.3
ahyangnb 8cc913b
Merge branch 'main' into main
ahyangnb 00d392c
Merge branch 'main' into main
ahyangnb 6ce16f8
Merge branch 'main' into main
ahyangnb 31d8ddb
Merge branch 'main' into main
ahyangnb 2345943
Merge branch 'main' into main
ahyangnb 67ed112
test: restore() update currentConfiguration in pop()
ahyangnb 3a02f82
Merge branch 'main' into main
ahyangnb de10e04
Merge branch 'main' into main
ahyangnb ec4e34f
Merge branch 'main' into main
ahyangnb b63a7d9
Merge branch 'main' into main
ahyangnb 9440747
Merge branch 'main' into main
ahyangnb 846fcfb
Merge branch 'main' into main
ahyangnb dda78e0
Merge branch 'main' into main
ahyangnb 8c1e543
Merge branch 'main' into main
ahyangnb 6fbb0e0
Merge branch 'flutter:main' into main
ahyangnb 93ee871
* add change logs: Fixes Popping state and re-rendering scaffold at t…
ahyangnb 96320b2
* remove: stream_listener_router.dart and test_stream_listener.dart
ahyangnb dd6dae6
## 15.1.4
ahyangnb 137c5e3
Update version to 15.1.4 in pubspec.yaml
ahyangnb fe1bfae
Merge branch 'main' into main
ahyangnb c6cd8d4
Merge branch 'main' into main
Piinks File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
318 changes: 318 additions & 0 deletions
318
packages/go_router/example/lib/stream_listener_router.dart
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,318 @@ | ||
| // Copyright 2013 The Flutter Authors. All rights reserved. | ||
ahyangnb marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| // Use of this source code is governed by a BSD-style license that can be | ||
| // found in the LICENSE file. | ||
|
|
||
| import 'dart:async'; | ||
| import 'dart:developer'; | ||
|
|
||
| import 'package:flutter/material.dart'; | ||
| import 'package:go_router/go_router.dart'; | ||
|
|
||
| void main() { | ||
| GoRouter.optionURLReflectsImperativeAPIs = true; | ||
|
|
||
| WidgetsFlutterBinding.ensureInitialized(); | ||
|
|
||
| runApp(const MyApp()); | ||
| } | ||
|
|
||
| /// A counter stream that emits a new value when the counter is incremented. | ||
| class CounterStream { | ||
| int _counter = 0; | ||
|
|
||
| final StreamController<int> _streamController = | ||
| StreamController<int>.broadcast(); | ||
|
|
||
| /// The stream that emits a new value when the counter is incremented. | ||
| Stream<int> get stateStream => _streamController.stream.asBroadcastStream(); | ||
|
|
||
| /// Increments the counter and emits a new value. | ||
| void increment() { | ||
| _streamController.sink.add(++_counter); | ||
| } | ||
| } | ||
|
|
||
| /// A counter stream that emits a new value when the counter is incremented. | ||
| final CounterStream counterStream = CounterStream(); | ||
|
|
||
| /// A listener that listens to a stream and refreshes the router when the stream emits a new value. | ||
| class StreamListener extends ChangeNotifier { | ||
| /// Creates a stream listener. | ||
| StreamListener(Stream<dynamic> stream) { | ||
| notifyListeners(); | ||
|
|
||
| _subscription = stream.asBroadcastStream().listen((_) { | ||
| notifyListeners(); | ||
| }); | ||
| } | ||
|
|
||
| late final StreamSubscription<dynamic> _subscription; | ||
|
|
||
| @override | ||
| void notifyListeners() { | ||
| super.notifyListeners(); | ||
| log('refreshing the router'); | ||
| } | ||
|
|
||
| @override | ||
| void dispose() { | ||
| _subscription.cancel(); | ||
| super.dispose(); | ||
| } | ||
| } | ||
|
|
||
| /// The main application widget. | ||
| class MyApp extends StatefulWidget { | ||
| /// Creates the main application widget. | ||
| const MyApp({super.key}); | ||
|
|
||
| @override | ||
| State<MyApp> createState() => _MyAppState(); | ||
| } | ||
|
|
||
| final GlobalKey<NavigatorState> _rootNavigatorKey = GlobalKey<NavigatorState>(); | ||
|
|
||
| final GoRouter _router = GoRouter( | ||
| initialLocation: '/', | ||
| navigatorKey: _rootNavigatorKey, | ||
| refreshListenable: StreamListener(counterStream.stateStream), | ||
| routes: <RouteBase>[ | ||
| ShellRoute( | ||
| builder: (BuildContext context, GoRouterState state, Widget child) { | ||
| return GenericPage(child: child); | ||
| }, | ||
| routes: <RouteBase>[ | ||
| GoRoute( | ||
| path: '/', | ||
| builder: (BuildContext context, GoRouterState state) => | ||
| const GenericPage(showPushButton: true, path: 'a'), | ||
| routes: <RouteBase>[ | ||
| GoRoute( | ||
| path: 'a', | ||
| name: 'a', | ||
| builder: (BuildContext context, GoRouterState state) => | ||
| const GenericPage(showPushButton: true, path: 'b'), | ||
| routes: <RouteBase>[ | ||
| GoRoute( | ||
| path: 'b', | ||
| name: 'b', | ||
| builder: (BuildContext context, GoRouterState state) => | ||
| const GenericPage(showBackButton: true), | ||
| ), | ||
| ], | ||
| ), | ||
| ], | ||
| ), | ||
| ], | ||
| ), | ||
| ], | ||
| ); | ||
|
|
||
| class _MyAppState extends State<MyApp> { | ||
| late StreamSubscription<int> _stateSubscription; | ||
|
|
||
| /// The current state of the counter. | ||
| int _currentState = 0; | ||
|
|
||
| @override | ||
| void initState() { | ||
| super.initState(); | ||
| _stateSubscription = counterStream.stateStream.listen((int state) { | ||
| setState(() { | ||
| _currentState = state; | ||
| log('$_currentState:: "try double place to listen"'); | ||
| }); | ||
| }); | ||
| } | ||
|
|
||
| @override | ||
| void dispose() { | ||
| _stateSubscription.cancel(); | ||
| super.dispose(); | ||
| } | ||
|
|
||
| @override | ||
| Widget build(BuildContext context) { | ||
| return MaterialApp.router( | ||
| title: 'Flutter Demo', | ||
| theme: ThemeData( | ||
| primarySwatch: Colors.blue, | ||
| ), | ||
| routerConfig: _router, | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| /// A dialog test widget. | ||
| class DialogTest extends StatelessWidget { | ||
| /// Creates a dialog test widget. | ||
| const DialogTest({super.key}); | ||
|
|
||
| @override | ||
| Widget build(BuildContext context) { | ||
| return Center( | ||
| child: Container( | ||
| width: 300, | ||
| height: 300, | ||
| alignment: Alignment.center, | ||
| child: Material( | ||
| color: Colors.white, | ||
| child: Column( | ||
| children: | ||
| <String>['Navigator::pop', 'GoRouter::pop'].map((String e) { | ||
| return InkWell( | ||
| child: SizedBox( | ||
| height: 60, | ||
| width: 300, | ||
| child: Row( | ||
| mainAxisAlignment: MainAxisAlignment.center, | ||
| children: <Widget>[ | ||
| Text(e), | ||
| const Icon(Icons.close), | ||
| ], | ||
| ), | ||
| ), | ||
| onTap: () { | ||
| if (e == 'GoRouter::pop') { | ||
| // WHEN THE USER PRESSES THIS BUTTON, THE URL | ||
| // DOESN'T CHANGE, BUT THE SCREEN DOES | ||
| counterStream | ||
| .increment(); // <- when removing this line the issue is gone | ||
| GoRouter.of(context).pop(); | ||
| } else { | ||
| Navigator.of(context).pop(); | ||
| } | ||
| }, | ||
| ); | ||
| }).toList(), | ||
| ), | ||
| ), | ||
| ), | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| /// A generic page that can be used to display a page in the app. | ||
| class GenericPage extends StatefulWidget { | ||
| /// Creates a generic page. | ||
| const GenericPage({ | ||
| this.child, | ||
| Key? key, | ||
| this.showPushButton = false, | ||
| this.showBackButton = false, | ||
| this.path, | ||
| }) : super(key: key ?? const ValueKey<String>('ShellWidget')); | ||
|
|
||
| /// The child widget to be displayed in the page. | ||
| final Widget? child; | ||
|
|
||
| /// Whether to show the push button. | ||
| final bool showPushButton; | ||
|
|
||
| /// Whether to show the back button. | ||
| final bool showBackButton; | ||
|
|
||
| /// The path of the page. | ||
| final String? path; | ||
|
|
||
| @override | ||
| State<GenericPage> createState() => _GenericPageState(); | ||
| } | ||
|
|
||
| class _GenericPageState extends State<GenericPage> { | ||
| late StreamSubscription<int> _stateSubscription; | ||
| int _currentState = 0; | ||
| final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>(); | ||
| @override | ||
| void initState() { | ||
| super.initState(); | ||
| _stateSubscription = counterStream.stateStream.listen((int state) { | ||
| setState(() { | ||
| _currentState = state; | ||
| }); | ||
| }); | ||
| } | ||
|
|
||
| @override | ||
| void dispose() { | ||
| _stateSubscription.cancel(); | ||
| super.dispose(); | ||
| } | ||
|
|
||
| @override | ||
| Widget build(BuildContext context) { | ||
| return Scaffold( | ||
| key: _scaffoldKey, | ||
| appBar: widget.child != null | ||
| ? AppBar( | ||
| title: Text('Count: $_currentState'), | ||
| actions: <Widget>[ | ||
| TextButton( | ||
| onPressed: () { | ||
| showDialog<void>( | ||
| context: context, | ||
| builder: (BuildContext context) { | ||
| return const DialogTest(); | ||
| }, | ||
| ); | ||
| }, | ||
| child: const Text('dialog1'), | ||
| ), | ||
| TextButton( | ||
| onPressed: () { | ||
| showModalBottomSheet<void>( | ||
| context: context, | ||
| builder: (BuildContext context) { | ||
| return const DialogTest(); | ||
| }, | ||
| ); | ||
| }, | ||
| child: const Text('dialog2'), | ||
| ), | ||
| TextButton( | ||
| onPressed: () { | ||
| _scaffoldKey.currentState?.openEndDrawer(); | ||
| }, | ||
| child: const Text('EndDrawer'), | ||
| ), | ||
| ], | ||
| ) | ||
| : null, | ||
| endDrawer: const Drawer( | ||
| width: 200, | ||
| child: DialogTest(), | ||
| ), | ||
| body: _buildWidget(context), | ||
| ); | ||
| } | ||
|
|
||
| Widget _buildWidget(BuildContext context) { | ||
| if (widget.child != null) { | ||
| return widget.child!; | ||
| } | ||
|
|
||
| if (widget.showBackButton) { | ||
| return TextButton( | ||
| onPressed: () { | ||
| // WHEN THE USER PRESSES THIS BUTTON, THE URL | ||
| // DOESN'T CHANGE, BUT THE SCREEN DOES | ||
| counterStream | ||
| .increment(); // <- when removing this line the issue is gone | ||
| GoRouter.of(context).pop(); | ||
| }, | ||
| child: const Text('<- Go Back'), | ||
| ); | ||
| } | ||
|
|
||
| if (widget.showPushButton) { | ||
| return TextButton( | ||
| onPressed: () { | ||
| GoRouter.of(context).goNamed(widget.path!); | ||
| }, | ||
| child: const Text('Push ->'), | ||
| ); | ||
| } | ||
|
|
||
| return Text('Current state: $_currentState'); | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| // Copyright 2013 The Flutter Authors. All rights reserved. | ||
| // Use of this source code is governed by a BSD-style license that can be | ||
| // found in the LICENSE file. | ||
|
|
||
| import 'package:flutter_test/flutter_test.dart'; | ||
| import 'package:go_router_examples/stream_listener_router.dart'; | ||
|
|
||
| void main() { | ||
| // For issue https://github.com/flutter/flutter/issues/150312. | ||
| testWidgets('GoRouter.of(context).pop() works', (WidgetTester tester) async { | ||
| await tester.pumpWidget(const MyApp()); | ||
|
|
||
| // Navigate to the second page | ||
| expect(find.text('Push ->'), findsOneWidget); | ||
| await tester.tap(find.text('Push ->')); | ||
| await tester.pumpAndSettle(); | ||
|
|
||
| // Navigate to the third page | ||
| expect(find.text('Push ->'), findsOneWidget); | ||
| await tester.tap(find.text('Push ->')); | ||
| await tester.pumpAndSettle(); | ||
|
|
||
| // Verify we are on the second page | ||
| expect(find.text('<- Go Back'), findsOneWidget); | ||
| await tester.tap(find.text('<- Go Back')); | ||
| await tester.pumpAndSettle(); | ||
|
|
||
| // Expect the Count is 1. | ||
| expect(find.text('Count: 1'), findsOneWidget); | ||
|
|
||
| // Check if we are pop back to the second page | ||
| // and push to the third page again. | ||
| expect(find.text('Push ->'), findsOneWidget); | ||
| await tester.tap(find.text('Push ->')); | ||
| await tester.pumpAndSettle(); | ||
|
|
||
| // Now we try pop again. | ||
| expect(find.text('<- Go Back'), findsOneWidget); | ||
| await tester.tap(find.text('<- Go Back')); | ||
| await tester.pumpAndSettle(); | ||
|
|
||
| // Check count increment. | ||
| expect(find.text('Count: 2'), findsOneWidget); | ||
| }); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.