-
Notifications
You must be signed in to change notification settings - Fork 29.7k
Add generic type for result in PopScope #139164
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
Conversation
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.
I added this since we added to popScope, but I am not sure if there is a use case for result in NavigatorPopHandler.
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.
I decide to revert this change, I cant think of a reasonable way this can be used.
|
This will be a breaking change. will prepare the doc once this is approved |
|
will reopen once I am back from vacation |
|
internal test failures are expected, the code needs to be migrated |
justinmc
left a comment
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.
Thanks again for jumping in to help with this so quick after returning from leave!
This looks good, I probably should have done this in the first place. I'm struggling to picture exactly how the types should be used though. Maybe it would be worth adding an example (pop_scope.1.dart).
Consider the pop_scope.0.dart example. If I modify it so that the second page contains a Form that is returned as a result, the type parameters kind of confuse me:
pop_scope.0.dart modified to include a result value
// Copyright 2014 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.
// This sample demonstrates showing a confirmation dialog before navigating
// away from a page.
import 'package:flutter/material.dart';
void main() => runApp(const NavigatorPopHandlerApp());
class NavigatorPopHandlerApp extends StatelessWidget {
const NavigatorPopHandlerApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
initialRoute: '/home',
onGenerateRoute: (RouteSettings settings) {
return switch (settings.name) {
'/two' => MaterialPageRoute<FormData>(
builder: (BuildContext context) => const _PageTwo(),
),
_ => MaterialPageRoute(
builder: (BuildContext context) => const _HomePage(),
),
};
},
);
}
}
class _HomePage extends StatefulWidget {
const _HomePage();
@override
State<_HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<_HomePage> {
FormData? _formData;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text('Page One'),
if (_formData != null)
Text('Hello ${_formData!.name}, whose favorite food is ${_formData!.favoriteFood}.'),
TextButton(
onPressed: () async {
final FormData formData =
await Navigator.of(context).pushNamed<FormData?>('/two')
?? const FormData();
if (formData != _formData) {
setState(() {
_formData = formData;
});
}
},
child: const Text('Next page'),
),
],
),
),
);
}
}
class _PageTwo extends StatefulWidget {
const _PageTwo();
@override
State<_PageTwo> createState() => _PageTwoState();
}
class _PageTwoState extends State<_PageTwo> {
FormData _formData = const FormData();
/// Shows a dialog and resolves to true when the user has indicated that they
/// want to pop.
///
/// A return value of null indicates a desire not to pop, such as when the
/// user has dismissed the modal without tapping a button.
Future<bool?> _showBackDialog() {
return showDialog<bool>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Are you sure?'),
content: const Text(
'Are you sure you want to leave this page?',
),
actions: <Widget>[
TextButton(
style: TextButton.styleFrom(
textStyle: Theme.of(context).textTheme.labelLarge,
),
child: const Text('Nevermind'),
onPressed: () {
Navigator.pop(context, false);
},
),
TextButton(
style: TextButton.styleFrom(
textStyle: Theme.of(context).textTheme.labelLarge,
),
child: const Text('Leave'),
onPressed: () {
Navigator.pop(context, true);
},
),
],
);
},
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text('Page Two'),
Form(
child: Column(
children: <Widget>[
TextFormField(
decoration: const InputDecoration(
hintText: 'Enter your name.',
),
onChanged: (String value) {
_formData = _formData.copyWith(
name: value,
);
},
),
TextFormField(
decoration: const InputDecoration(
hintText: 'Enter your favorite food.',
),
onChanged: (String value) {
_formData = _formData.copyWith(
favoriteFood: value,
);
},
),
],
),
),
// If this type is `Object?`, then it crashes because it doesn't
// match the type of the MaterialPageRoute.
PopScope<FormData>(
canPop: false,
onPopInvoked: (bool didPop, dynamic result) async {
if (didPop) {
return;
}
final bool shouldPop = await _showBackDialog() ?? false;
if (context.mounted && shouldPop) {
Navigator.pop(context, _formData);
}
},
child: TextButton(
onPressed: () async {
final bool shouldPop = await _showBackDialog() ?? false;
if (context.mounted && shouldPop) {
Navigator.pop(context, _formData);
}
},
child: const Text('Go back'),
),
),
],
),
),
);
}
}
class FormData {
const FormData({
this.name = '',
this.favoriteFood = '',
});
final String name;
final String favoriteFood;
FormData copyWith({String? name, String? favoriteFood}) {
return FormData(
name: name ?? this.name,
favoriteFood: favoriteFood ?? this.favoriteFood,
);
}
@override
bool operator ==(Object other) {
if (identical(this, other)) {
return true;
}
if (other.runtimeType != runtimeType) {
return false;
}
return other is FormData
&& other.name == name
&& other.favoriteFood == favoriteFood;
}
@override
int get hashCode => Object.hash(name, favoriteFood);
}My confusion comes from the fact that the result type of the dialog (bool) is different from the result type of the route (FormData). Is that example a good demonstration of this PR or am I using it wrong?
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.
Avoid underscore here and below. I guess this is less important in an integration test but still might help to write out the parameter.
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.
Is it possible to make this void? Not sure if it's better but just curious.
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.
this should match the Route generic type in line 1233. I am not sure if both can be void or not, I will have to investigate a bit more
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.
Pointing out some underscores in this test here and below as well.
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.
I wonder if people will want NavigatorPopHandler itself to have a type parameter, which is passed to PopScope here? I guess it's probably best to save that for another PR if/when it's needed.
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.
That was what i did before, but I can't think of a reason you need to access result in NavigatorPopHandler
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.
If I understand correctly, NavigatorPopHandler is for handling system back gesture, which won't have any result
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.
You're right, it shouldn't ever be necessary to set that as far as I can tell.
|
@justinmc Details// Copyright 2014 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.
// This sample demonstrates showing a confirmation dialog before navigating
// away from a page.
import 'package:flutter/material.dart';
void main() => runApp(const NavigatorPopHandlerApp());
class NavigatorPopHandlerApp extends StatelessWidget {
const NavigatorPopHandlerApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
initialRoute: '/home',
onGenerateRoute: (RouteSettings settings) {
return switch (settings.name) {
'/two' => MaterialPageRoute<FormData>(
builder: (BuildContext context) => const _PageTwo(),
),
_ => MaterialPageRoute(
builder: (BuildContext context) => const _HomePage(),
),
};
},
);
}
}
class _HomePage extends StatefulWidget {
const _HomePage();
@override
State<_HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<_HomePage> {
FormData? _formData;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text('Page One'),
if (_formData != null)
Text('Hello ${_formData!.name}, whose favorite food is ${_formData!.favoriteFood}.'),
TextButton(
onPressed: () async {
final FormData formData =
await Navigator.of(context).pushNamed<FormData?>('/two')
?? const FormData();
if (formData != _formData) {
setState(() {
_formData = formData;
});
}
},
child: const Text('Next page'),
),
],
),
),
);
}
}
// This is a PopScope wrapper over _PageTwoBody
class _PageTwo extends StatelessWidget {
const _PageTwo();
Future<bool?> _showBackDialog(BuildContext context) {
return showDialog<bool>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Are you sure?'),
content: const Text(
'Are you sure you want to leave this page?',
),
actions: <Widget>[
TextButton(
style: TextButton.styleFrom(
textStyle: Theme.of(context).textTheme.labelLarge,
),
child: const Text('Nevermind'),
onPressed: () {
Navigator.pop(context, false);
},
),
TextButton(
style: TextButton.styleFrom(
textStyle: Theme.of(context).textTheme.labelLarge,
),
child: const Text('Leave'),
onPressed: () {
Navigator.pop(context, true);
},
),
],
);
},
);
}
@override
Widget build(BuildContext context) {
return PopScope<FormData>(
canPop: false,
// We need result here because we don't know what `_PageTwoBody` pop the
// page with
onPopInvoked: (bool didPop, FormData? result) async {
if (didPop) {
return;
}
final bool shouldPop = await _showBackDialog(context) ?? false;
if (context.mounted && shouldPop) {
Navigator.pop(context, result);
}
},
child: const _PageTwoBody(),
);
}
}
class _PageTwoBody extends StatefulWidget {
const _PageTwoBody();
@override
State<_PageTwoBody> createState() => _PageTwoBodyState();
}
class _PageTwoBodyState extends State<_PageTwoBody> {
FormData _formData = const FormData();
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text('Page Two'),
Form(
child: Column(
children: <Widget>[
TextFormField(
decoration: const InputDecoration(
hintText: 'Enter your name.',
),
onChanged: (String value) {
_formData = _formData.copyWith(
name: value,
);
},
),
TextFormField(
decoration: const InputDecoration(
hintText: 'Enter your favorite food.',
),
onChanged: (String value) {
_formData = _formData.copyWith(
favoriteFood: value,
);
},
),
],
),
),
// If this type is `Object?`, then it crashes because it doesn't
// match the type of the MaterialPageRoute.
TextButton(
onPressed: () async {
Navigator.maybePop(context, _formData);
},
child: const Text('Go back'),
),
],
),
),
);
}
}
class FormData {
const FormData({
this.name = '',
this.favoriteFood = '',
});
final String name;
final String favoriteFood;
FormData copyWith({String? name, String? favoriteFood}) {
return FormData(
name: name ?? this.name,
favoriteFood: favoriteFood ?? this.favoriteFood,
);
}
@override
bool operator ==(Object other) {
if (identical(this, other)) {
return true;
}
if (other.runtimeType != runtimeType) {
return false;
}
return other is FormData
&& other.name == name
&& other.favoriteFood == favoriteFood;
}
@override
int get hashCode => Object.hash(name, favoriteFood);
}The crash when you use |
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.
LGTM with nits.
Thanks for the example about the types, that makes it very clear. I added a comment below about explaining this in the docs.
I think there are some infra failures FYI.
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.
Nit: Too much whitespace here.
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.
Nit: Can you document what the T parameter means? Maybe something like this, based on MaterialPageRoute's docs:
The type parameter
Trepresents the return type of the route in which this widget appears. This result can be supplied as the route is popped from the stack with [Navigator.pop] by providing the optionalresultargument. It will be passed into [onPopInvoked] as theresultargument with the specified type.
|
some update, I am migrating internal code. |
|
A reason for requesting a revert of flutter/flutter/139164 could |
|
Reason for revert: hard breaking change |
This reverts commit 7ee05a0.
Reverts: #139164 Initiated by: chunhtai Reason for reverting: hard breaking change Original PR Author: chunhtai Reviewed By: {justinmc} This change reverts the following previous change: Adds a generic type and pop result to popscope and its friend. The use cases are to be able to capture the result when the pop is called. migration guide: flutter/website#9872
flutter/flutter@fb110b9...98685a0 2024-04-19 [email protected] Replace CocoaPods deprecated `exists?` with `exist?` (flutter/flutter#147056) 2024-04-19 [email protected] Roll Flutter Engine from 4ed7a2d6aed9 to 7fafbbf17c02 (2 revisions) (flutter/flutter#147046) 2024-04-19 [email protected] Roll Flutter Engine from 25d773ea90c4 to 4ed7a2d6aed9 (3 revisions) (flutter/flutter#147038) 2024-04-19 [email protected] Update link branches to `main` (continued) (flutter/flutter#146985) 2024-04-19 [email protected] Roll Flutter Engine from b6234dd1984e to 25d773ea90c4 (1 revision) (flutter/flutter#147035) 2024-04-19 [email protected] Roll Flutter Engine from ff471881e90a to b6234dd1984e (2 revisions) (flutter/flutter#147029) 2024-04-19 [email protected] Roll Flutter Engine from 442d14a7e840 to ff471881e90a (1 revision) (flutter/flutter#147025) 2024-04-19 [email protected] Roll Flutter Engine from 6e4a15d31769 to 442d14a7e840 (2 revisions) (flutter/flutter#147023) 2024-04-19 [email protected] Opt out users from GA3 if opted out of GA4 (flutter/flutter#146453) 2024-04-19 [email protected] Roll Flutter Engine from 61bf47d129bb to 6e4a15d31769 (1 revision) (flutter/flutter#147022) 2024-04-18 [email protected] Roll Flutter Engine from 13a6ce419664 to 61bf47d129bb (4 revisions) (flutter/flutter#147017) 2024-04-18 [email protected] Add a breadcrumb for the pub autoroller (flutter/flutter#146786) 2024-04-18 98614782+auto-submit[bot]@users.noreply.github.com Reverts "Add generic type for result in PopScope (#139164)" (flutter/flutter#147015) 2024-04-18 [email protected] Redundant message fix (flutter/flutter#143978) 2024-04-18 [email protected] Clean up flutterRoot (flutter/flutter#147010) 2024-04-18 49699333+dependabot[bot]@users.noreply.github.com Bump actions/upload-artifact from 4.3.1 to 4.3.2 (flutter/flutter#147011) 2024-04-18 [email protected] Add Swift Package Manager as new opt-in feature for iOS and macOS (flutter/flutter#146256) 2024-04-18 [email protected] Refactor framework coverage tests (flutter/flutter#146210) 2024-04-18 [email protected] Roll Flutter Engine from 46ff024bff10 to 13a6ce419664 (3 revisions) (flutter/flutter#147006) 2024-04-18 [email protected] Changing the renderer on the web target should change its build key. (flutter/flutter#147003) 2024-04-18 [email protected] Roll Flutter Engine from 6abfa565a9f9 to 46ff024bff10 (1 revision) (flutter/flutter#147005) 2024-04-18 [email protected] Add generic type for result in PopScope (flutter/flutter#139164) 2024-04-18 [email protected] Roll Flutter Engine from aa6f7411c219 to 6abfa565a9f9 (1 revision) (flutter/flutter#147002) 2024-04-18 [email protected] [tools] Make SnapshotType.platform non-nullable (flutter/flutter#146958) 2024-04-18 [email protected] Roll Flutter Engine from b8e802515b5a to aa6f7411c219 (1 revision) (flutter/flutter#146996) 2024-04-18 [email protected] Roll Flutter Engine from 2c3e9c8bfce6 to b8e802515b5a (2 revisions) (flutter/flutter#146993) 2024-04-18 [email protected] Roll Packages from d39830e to 0e3809d (9 revisions) (flutter/flutter#146992) 2024-04-18 [email protected] Fix memory leaks in navigation rail (flutter/flutter#146988) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages Please CC [email protected],[email protected],[email protected] on the revert to ensure that a human is aware of the problem. To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
Adds a generic type and pop result to popscope and its friend. The use cases are to be able to capture the result when the pop is called. migration guide: flutter/website#9872
…lutter#147015) Reverts: flutter#139164 Initiated by: chunhtai Reason for reverting: hard breaking change Original PR Author: chunhtai Reviewed By: {justinmc} This change reverts the following previous change: Adds a generic type and pop result to popscope and its friend. The use cases are to be able to capture the result when the pop is called. migration guide: flutter/website#9872
)" (flutter#147015)" This reverts commit bb4c9b3.
waiting for flutter/flutter#139164 ## Presubmit checklist - [ ] This PR doesn’t contain automatically generated corrections (Grammarly or similar). - [ ] This PR follows the [Google Developer Documentation Style Guidelines](https://developers.google.com/style) — for example, it doesn’t use _i.e._ or _e.g._, and it avoids _I_ and _we_ (first person). - [ ] This PR uses [semantic line breaks](https://github.com/dart-lang/site-shared/blob/main/doc/writing-for-dart-and-flutter-websites.md#semantic-line-breaks) of 80 characters or fewer. --------- Co-authored-by: Shams Zakhour (ignore Sfshaza) <[email protected]>
waiting for flutter/flutter#139164 ## Presubmit checklist - [ ] This PR doesn’t contain automatically generated corrections (Grammarly or similar). - [ ] This PR follows the [Google Developer Documentation Style Guidelines](https://developers.google.com/style) — for example, it doesn’t use _i.e._ or _e.g._, and it avoids _I_ and _we_ (first person). - [ ] This PR uses [semantic line breaks](https://github.com/dart-lang/site-shared/blob/main/doc/writing-for-dart-and-flutter-websites.md#semantic-line-breaks) of 80 characters or fewer. --------- Co-authored-by: Shams Zakhour (ignore Sfshaza) <[email protected]>
flutter/flutter@fb110b9...98685a0 2024-04-19 [email protected] Replace CocoaPods deprecated `exists?` with `exist?` (flutter/flutter#147056) 2024-04-19 [email protected] Roll Flutter Engine from 4ed7a2d6aed9 to 7fafbbf17c02 (2 revisions) (flutter/flutter#147046) 2024-04-19 [email protected] Roll Flutter Engine from 25d773ea90c4 to 4ed7a2d6aed9 (3 revisions) (flutter/flutter#147038) 2024-04-19 [email protected] Update link branches to `main` (continued) (flutter/flutter#146985) 2024-04-19 [email protected] Roll Flutter Engine from b6234dd1984e to 25d773ea90c4 (1 revision) (flutter/flutter#147035) 2024-04-19 [email protected] Roll Flutter Engine from ff471881e90a to b6234dd1984e (2 revisions) (flutter/flutter#147029) 2024-04-19 [email protected] Roll Flutter Engine from 442d14a7e840 to ff471881e90a (1 revision) (flutter/flutter#147025) 2024-04-19 [email protected] Roll Flutter Engine from 6e4a15d31769 to 442d14a7e840 (2 revisions) (flutter/flutter#147023) 2024-04-19 [email protected] Opt out users from GA3 if opted out of GA4 (flutter/flutter#146453) 2024-04-19 [email protected] Roll Flutter Engine from 61bf47d129bb to 6e4a15d31769 (1 revision) (flutter/flutter#147022) 2024-04-18 [email protected] Roll Flutter Engine from 13a6ce419664 to 61bf47d129bb (4 revisions) (flutter/flutter#147017) 2024-04-18 [email protected] Add a breadcrumb for the pub autoroller (flutter/flutter#146786) 2024-04-18 98614782+auto-submit[bot]@users.noreply.github.com Reverts "Add generic type for result in PopScope (#139164)" (flutter/flutter#147015) 2024-04-18 [email protected] Redundant message fix (flutter/flutter#143978) 2024-04-18 [email protected] Clean up flutterRoot (flutter/flutter#147010) 2024-04-18 49699333+dependabot[bot]@users.noreply.github.com Bump actions/upload-artifact from 4.3.1 to 4.3.2 (flutter/flutter#147011) 2024-04-18 [email protected] Add Swift Package Manager as new opt-in feature for iOS and macOS (flutter/flutter#146256) 2024-04-18 [email protected] Refactor framework coverage tests (flutter/flutter#146210) 2024-04-18 [email protected] Roll Flutter Engine from 46ff024bff10 to 13a6ce419664 (3 revisions) (flutter/flutter#147006) 2024-04-18 [email protected] Changing the renderer on the web target should change its build key. (flutter/flutter#147003) 2024-04-18 [email protected] Roll Flutter Engine from 6abfa565a9f9 to 46ff024bff10 (1 revision) (flutter/flutter#147005) 2024-04-18 [email protected] Add generic type for result in PopScope (flutter/flutter#139164) 2024-04-18 [email protected] Roll Flutter Engine from aa6f7411c219 to 6abfa565a9f9 (1 revision) (flutter/flutter#147002) 2024-04-18 [email protected] [tools] Make SnapshotType.platform non-nullable (flutter/flutter#146958) 2024-04-18 [email protected] Roll Flutter Engine from b8e802515b5a to aa6f7411c219 (1 revision) (flutter/flutter#146996) 2024-04-18 [email protected] Roll Flutter Engine from 2c3e9c8bfce6 to b8e802515b5a (2 revisions) (flutter/flutter#146993) 2024-04-18 [email protected] Roll Packages from d39830e to 0e3809d (9 revisions) (flutter/flutter#146992) 2024-04-18 [email protected] Fix memory leaks in navigation rail (flutter/flutter#146988) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages Please CC [email protected],[email protected],[email protected] on the revert to ensure that a human is aware of the problem. To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
Adds a generic type and pop result to popscope and its friend.
The use cases are to be able to capture the result when the pop is called.
migration guide: flutter/website#9872
Pre-launch Checklist
///).If you need help, consider asking for advice on the #hackers-new channel on Discord.