-
Notifications
You must be signed in to change notification settings - Fork 27.7k
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
Document that WillPopScope prevents swipe to go back on MaterialPageRoute #14203
Comments
This is intended behavior. Is the documentation lacking? Can you elaborate on why you wouldn't want this? |
The page that I'm trying to watch is a full page, not just a modal like in #8269. I see in the documentation that it sounds like this widget is only intended to wrap a modal. The goal that I'm trying to achieve is to add a listener for when the page pops, so that I can save the scroll position to the my app's state store. Is there a better way to go about this? |
WillPopScope is about preventing people from popping. If you just want to store something when the page pops, I recommend storing it all the time (so that you handle unexpected events like the app crashing). If that doesn't work for you, you could use a custom MaterialPageRoute subclass that exposes the relevant route lifecycle methods like didPush/didPop for you (you could even make that work with a widget like WillPopScope, using a similar implementation strategy). Or you could use a NavigatorObserver. |
We should make sure we explain WillPopScope in its docs, and either explain how to do the effect you want, or provide some dedicated widget to do so. |
I got the idea to use I think that it would be beneficial to have an easy way to detect a page pop for similar scenarios. For example, if the Android settings app were made with Flutter, you'd want to be able to turn off Bluetooth searching when the user leaves the Bluetooth page. |
@Hixie mulligan just noticed this issue. We use In the cases where the user has no changes to return, they can press the back button to go to the previous screen; however, they can't use the swipe back gesture. I think from a user's perspective, I wouldn't understand why I can't swipe back. I'm assuming the What are your thoughts? |
Being async isn't necessarily a blocker (so long as it's fast enough to fit within a single frame). @xster was looking at this recently I believe. |
@dudeofawesome: I think the WillPopScope is the wrong tool for the scenario you're describing. If you want to conduct cleanup task either at the route level or at the widget level to release resources or stop execution that could have a longer lifecycle than the UI, I'd use the @alanrussian: I'd argue WillPopScope is likely the wrong UX pattern to use in your case too. Since the back swipe is from iOS, we can take iOS patterns as prior art reference. When you're in a 'route' on iOS where it's possible to enter data that can be lost by navigating away, that view controller should always be presented rather than pushed. In Flutter-land, that should always translate to a You can, for instance, reference the native iOS contacts app or calendar app and try to create a new entry. It's always a bottom up transition and a cancel button. In other words, I think showing a [< Back] button is 'wrong' UX in the first place even without the gesture. In iOS, you should never press the [< Back] button which then triggers an action sheet that asks you do you want to discard data. The [< Back] button should always unconditionally succeed via tap or back swipe. |
Thanks for the reply, @xster. I'm not super familiar with iOS design patterns these days so cc/ @johnfesa and @jklint-g. We have one use case today where we use In this case, we're not pushing a new route. In order to selectively use
I'd argue that if the pattern we have is common, this should be easier to do. |
The last usage you just described is correct right? In edit mode with the X button, you get a conditional close on the X tap and you can never swipe and native iOS would behave the same way (for instance when going into edit mode for a contact which turns the [< Back] button into Cancel). Are you asking about a different question / feature request in this case than back swipe gestures? A A semi-side-discussion, I wouldn't overly depend on all the data being UI state data in this case either (re: reshaping trees with GlobalKeys). Since the user might cancel the edit etc, you'll want your non-Flutter Dart application logic to harmonize between the 2 modes anyway (at which point it's cheap to recreate the UI on 2 different routes based on your application logic source of truth). |
@tvolkert Could we see about getting a volunteer to do a doc update for this as part of the doc fixit this week? Thanks! |
It should get picked up in a sweep of issues tagged as api docs. |
How to achieve back swipe when using |
We use However, at the end of the survey, we want the user to be able to the home screen to take another one if they wish so. We're pushing routes in agreement to the abovementioned behavior so a Any suggestions? |
I think many people just want to be able to set a navigator result so we don't have to set create our own backbutton, or disable swipe to go back. Having the ability to just do |
Maybe instead of just prevent the gesture, a way to detect and handle the swipe back inside the WillPopScope give us more flexibility to accomplish more use cases |
you can set the route's data without using pop, which would avoid the issue |
@dark-chocolate I found the way to do this without using WillPopScope, you can use the addScopedWillPopCallback method. There's some more info here: https://api.flutter.dev/flutter/widgets/ModalRoute/addScopedWillPopCallback.html The first section shows how to use the WillPopScope widget, but after that it shows how to register the ModalRoute callback manually. |
To achieve this behavior you can override class CustomMaterialPageRoute extends MaterialPageRoute {
@protected
bool get hasScopedWillPopCallback {
return false;
}
CustomMaterialPageRoute({
@required WidgetBuilder builder,
RouteSettings settings,
bool maintainState = true,
bool fullscreenDialog = false,
}) : super(
builder: builder,
settings: settings,
maintainState: maintainState,
fullscreenDialog: fullscreenDialog,
);
} And then replace |
This comment was marked as spam.
This comment was marked as spam.
This comment was marked as spam.
This comment was marked as spam.
1 similar comment
This comment was marked as spam.
This comment was marked as spam.
I am adding one solution which is a kind of workaround not an ideal solution for the problem. Use the below widget 'MyWillPopScope' on behalf of 'WillPopScope'...
|
Good. Thank you DhavalRKansara
When used on a real device, this code gives a pretty natural result. |
In my case, I have a TextField in the page, and want to save the content to disk when the user leaves the page, and will restore the data when entering the page. Since "save to disk" (and other operations) is asynchronous, I cannot simply use |
I use it works for back swipe ,but when I use it in webview page, the webview content will can not scroll |
So basically the Flutter team has no idea how to fix this... lol. This is a serious problem. |
I created a simple widget that mimics the standard android behavior - when you swipe from the side, an arrow will appear, and when you end dragging, the arrow disappears and WillPopScope will be called. Code: https://gist.github.com/fzyzcjy/4d9238c5be3f49ab45de9a00436b4743 (Used my own internal code, so you may need a bit of tweaking, e.g. remove the |
Sharing my workaround using a callback, without using WillPopScope, if you need to get a result back from the pushed page with all back behaviors working. The pushed page takes a callback, which is then called for any update happening. It's not efficient as it can be called multiple times (potentially triggering rebuild of the parent page, depending on you have to do setState or not), but it gets the job done. class PushedPage extends StatelessWidget {
final void Function(int updated)? onUpdate;
const PushedPage({super.key, this.onChange});
...
onPress: () { onUpdate?.call(42); },
...
}
///
class HomePage extends StatefulWidget {
...
}
class _HomePageState extends State<HomePage> {
...
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => PostPage(
onUpdate: (updated) {
setState(() { _pushedPageResult = updated; });
},
),
).then((_) { /* use _pushedPageResult ... */ });
} |
This comment was marked as duplicate.
This comment was marked as duplicate.
🔥 I encountered the same issue and tried using a
More : https://github.com/kofkuiper/swipe-iOS/blob/main/lib/main.dart |
PopScope.canPop being false prevents back gesture on iOS See flutter/flutter#14203
Just sharing my simple widget to enable ios gesture: using will pop scope: using pop scope: |
Can we please get an update from the Flutter team on this issue? I am not super familiar with iOS UI/UX, but can someone elaborate on why using PopScope/WillPopScope as a mechanism to intercept the back gesture and display a dialog when the user has pending form changes is not recommended for iOS? On Android this is perfectly acceptable UI/UX, and is used extensively. If the Flutter team is unable (or unwilling) to fix this open issue, then what is the iOS alternative to the workflow above? I read through this entire thread, and am still not clear on this. (sorry if I missed it) |
After reading this I did a tricky fast solution: Widget WillPopOrChild( // could be moved to a class tho
{required bool shouldConfirmLeave,
required Widget child,
required Future<bool> Function()? onWillPop}) {
if (shouldConfirmLeave) {
return WillPopScope(onWillPop: onWillPop, child: child);
}
return child;
} I have a flag in state when there's edition and changes, so if that's true I will pass that to the function and it will show a confirmation and prevent swiping back, if not, it will just swipe back, I think combined with some form of telling the user that still have pending changes will do the trick, hope this helps someone |
@darshankawar any comment for this? it's here since years |
any update on this issue.? or any feasible solution. |
Steps to Reproduce
MaterialPageRoute
to your app's page stackScaffold
in aWillPopScope
widgetThis also occurs if you replace the
MaterialPageRoute
with aCupertinoPageRoute
.I think this might be an unintended side-affect of fixing #8269.
For a live example of this, see my repo here.
Flutter Doctor
The text was updated successfully, but these errors were encountered: