My flutter android back button event is not triggering using willpopscope - flutter

Help! my will pop not even printing. Here is the code:
Future<bool> _willPopCallback() async {
print('Hi');
return false;
}
WillPopScope(
onWillPop: _willPopCallback,
child: Scaffold()

It was not working i tried my best so at the end i started using a package called back_button_interceptor. Its easy to use you can find here:
https://pub.dev/packages/back_button_interceptor/example

Related

How to go back in the previous page, upon tapping the phone's back button without showing any dialog?

// I already tried this but it wont work
return WillPopScope(
onWillPop: () { Navigator.pop(context);
},
child: Scaffold(),
error:Error: A non-null value must be returned since the return type 'Future' doesn't allow null.
'Future' is from 'dart:async'.
onWillPop: () { Navigator.pop(context);
If there is a screen in the stack then tapping the android back bottom will automatically go to the previous screen. this is the android default behavior. you don't have to wrap with WillPopScope.
If you want to show some dialog or do something by tapping the back button then wrap with WillPopScope.
onWillpop is an async function, it requires a function with a return type Future<bool>.
If you want to disable the default behavior of the android back button then you can use this.
onWillPop: () async {
// do something here
return false;
},
Otherwise,
onWillPop: () async {
// do something here
return true;
},
Returning true will not disable the default behavior of the android back button.

Trying to use showDialog()/show Pop up on app startup

What I want to achieve: I want to open a pop up explaining my app when it starts.
My approach: As far as I understand it from googling the issue, I should use the showDialog() method. In its most basic form:
showDialog(
context: context,
builder: (context) {
return Text('data');
});
I tried returning actual dialogs (e.g. AlertDialog) but it doesn't change the behavior so I'm just using Text() with a string as a placeholder for now.
The problem:
No matter where I place the showDialog function, it doesn't work as intended (also see scrennshots below):
Placing it in initState: I get an error message about inherited Widgets being called before the initState is done + an explanation about dependiencies I can barely follow.
Placing it in the build method: I get an error message that setState() or markNeedsBuild() gets called while the app is already buildung widgets.
Placing it in DidChangeAppLifeCycleState(): This is actually working and opening the pop when I pause the app and then resume it. It is not opening on app startup though.
Wrapping it in WidgetsBinding.instance!.addPostFrameCallback(): An idea I picked up here: How to show a popup on app start in Flutter. Doesn't change the outcome of the error messages, neither in initState nor in build.
Any ideas?
Screenshots:
From initState:
From build method:
From DidChangeAppLifecycleState (the "succesful" variant:
Will you please try below code in your init method? I hope this may work.
Future.delayed(Duration.zero, () async {
myFunction();
});
Using WidgetsBinding.instance.addPostFrameCallback inside initState perform its inner task after the 1st frame is complete.
addPostFrameCallback Schedule a callback for the end of this frame.
Next issue arise for not having material. You can directly return AlertDialog on builder or wrap with any material widget like Material, Scaffold..
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
showDialog(
context: context,
builder: (context) {
return const AlertDialog(
content: Text('data'),
);
},
);
});
}
If you are running version<3 null safety, use WidgetsBinding.instance?.addPostFrameCallback
One of the methods with WidgetsBinding.instance!.addPostFrameCallback() works fine .
If you show a normal show dialog with the press of a button too it will produce the same result.
Here, you need to wrap the text("data") in a dialog widget such as alertDialog or simpleDialog widget as needed and it will display the dialog within the current scaffold as -
WidgetsBinding.instance!.addPostFrameCallback((_) async {
return await showDialog(
context: context,
builder: (context) {
return AlertDialog(
content: Text("data"),
);
});
});
I tried adding this in the init state and the dialog pops up fine when I restart the app
Thanks a lot for your answers. I ficed the issue by rewriting with your suggestions; and it works. I tihnk the issue was that I did not have _ or anything else in my WidgetsBinding code. So I did:
WidgetsBinding.instance?.addPostFrameCallback(() {})
instead of
WidgetsBinding.instance?.addPostFrameCallback((_) {})

flutter - android back button does not call onWillPop of WillPopScope

I would like to exit my application. I have implemented a WillPopScope, but it looks like the onWillPop function is not being called at all. I tried many things like swap WillPopScope with Scaffold, changing the return value of the function, but it just looks like it is not working as expected.
My code:
Future<bool> _willPopCallback() async {
exit(0);
// await showDialog or Show add banners or whatever
// then
return true; // return true if the route to be popped
}
return Scaffold(
appBar: MyAppBar(
leading: DrawerAction(),
title: Text(AppLocalizations.of(context).textCapitalized('home_page')),
onSearch: (searchTerms) => this.search(searchTerms, context),
),
body: new WillPopScope(
onWillPop: _willPopCallback, // Empty Function.
child: //my screen widgets
I am not sure if this is a bug I should report to flutter or I am doing something wrong. Happy to provide more code on request.
I have tried:
exit(0);
Navigator.of(context).pop();
SystemChannels.platform.invokeMethod('SystemNavigator.pop');
Thanks in advance!
I managed to solve my problem and I think it is a very particular case, but it still might be helpful to someone.
TL;DR: Ensure that you dont have multiple Scaffolds in your widgets
I was using IndexedStack in my menu navigator, obviously wrapped with a Scaffold. The pages of the stack had Scaffold as well, and with this combination WillPopScope was not working neither in the navigator page neither in its stack pages. I solved by removing all the Scaffolds in the stack pages and having only one in the controller. In this way I managed to use WillPopScope correctly.
First of all do not ever use exit(0). It may be fine in Android environment, but apple won't allow the app on app store if it programmatically shuts down itself.
Here in the docs of onWillPop it clearly mentions that function should resolves to a boolean value.
Future<bool> _willPopCallback() async {
// await showDialog or Show add banners or whatever
// then
return Future.value(true);
}
This only works if your current page is the root of navigation stack.
Modify the code to return WillPopScope, and have Scaffold as a child.
return new WillPopScope(
onWillPop: _willPopCallback,
child: new Scaffold(
//then the rest of your code...
i know i am too late, but the problem still exists.
maybe i found the right solution.
make sure you are passing MaterialApp to the runApp method like this:
runApp(MaterialApp(home: MyFirstPage()));
this works for me for all my application's widgets. if you do not want to use it just wrap your widget in MaterialApp but do not forget that in every MaterialApp instance a new Navigator is created, so for me i just created one as above and in all my pages i just used scaffold and everything is ok.
I also stuck in the same problem but after a lot of searching, I found that this error is related to my parent container.
return WillPopScope(
onWillPop: () => _onWillPop(),
child: Scaffold(
appBar: AppBar(
backgroundColor: Colors.transparent,
...
],
),
Another possible reason: my implementation of _onWillPop() was throwing an exception and the code inside _onWillPop() was ignored. The exception did not appear in the log.
I resolved it by using a TRY/CATCH inside _onWillPop(), and handling all code paths.
I have been battling this and initially thought it had something to do with the nested Scaffold widgets as the OP had mentioned in their answer above. I tested this though and still had the same problem. The answer for me was that my root Scaffold was a child of a Navigator. It worked as soon as I removed the Scaffold as a child of the Navigator. Thankfully I didn't need a Navigator at the root level anyway as I was using an IndexedStack which has multiple Navigator widgets in it.
This is a late answer but I hope can helps someone.
The #Gicminos answer was right. If you have nested scaffold willPopScope not worked.
I wanna add some info in case you need.
I have a Scaffold containing bottomNavBar. Every Item in bottomNav is a Navigator which children are Scaffold (you notice that in this moment there are scaffolds innested).
This is my MainScaffold containing the bottom bar:
...
_navigatorKeys = {
TabItem.tabOne: tabOneKey,
TabItem.tabTwo: GlobalKey<NavigatorState>(),
};
...
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async {
//check if there are pages in stack so it can pop;
var navigatorState =(_navigatorKeys.values.toList([_selectedIndex]as GlobalKey<NavigatorState>).currentState;
if ( navigatorState !=null) {
if (!await navigatorState
.maybePop()) return true;
}
return false;
},
child: Scaffold(
body: SafeArea(
child: IndexedStack(
index: _selectedIndex,
children: _pages,
),
),
resizeToAvoidBottomInset: true,
bottomNavigationBar: Container(...)
...
}
If you wrap with a WillPopScope widget also your children like in the code below:
#override
Widget build(BuildContext context) {
var t = AppLocalizations.of(context)!;
return WillPopScope(
onWillPop: () async {
debugPrint("test");
return true;
},
child: Scaffold(...)
}
both onWillPop will be called (in main scaffold and children scaffold).
In the example the first one will pop only if can (there are page in navigator stack), the second one will be called immediatly after the first one and it will call the debugPrint function before returned
I my case onWillPop didn't call because I had a custom AppBar and tried to call Navigator.pop(context) instead of Navigator.maybePop(context).

How to send app in background when back button is pressed

I'm trying to avoid the default behaviour of Flutter, instead of closing app when the back button (of the smartophone of course) is pressed I will send the app in background, so when I try to reopen the app it returns on the last screen.
I also tried a solution that I have found here, but didn't work.
This is the code:
return WillPopScope(
onWillPop: () async =>
await SystemChannels.platform.invokeMethod('SystemNavigator.pop'),
child: Scaffold(
It also closes the app and don't send it in background.
The screen that contains the code showed above is the last route.
You can use MethodChannel for this, invoke a method from Flutter that will trigger a method in Java.
// in flutter use something like this
methodChannel.invokeMethod("homeButton");
And in Java, you can create a method like:
public void homeButton() {
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
More info on how to write platform specific code.
Flutter plugin for sending mobile applications to the background. Supports iOS and Android.
move_to_background: ^1.0.2
WillPopScope(
child: MaterialApp(...),
onWillPop: () async {
MoveToBackground.moveTaskToBack();
return false;
},
);

WillPopScope should I use return Future.value(true) after Navigator.pop

Is using return Future.value(false); after Navigator.pop(context) is the right way.
If I use
Navigator.pop(context, false) and after it
return Future.value(true);
The application will show black screen after pressing the back button, and no errors in the Logcat.
But if I use the same code without the Navigator.pop(context) or without the return Future.value(true); everything will be fine, using return Future.value(false); also works fine.
*Following a tutorial on Udemy that show return Future.value(true) is ok.
I found the solution.
You should use return Future.value(false);.
You navigated manually by using Navigator.pop(context), Future.value(true); trigger another pop which can't be done because you already exist the page and this crashes the app.
OnWillPop expect a return so By using the return Future.value(false); you tell the onWillPop that you handle the closing of the page here.
onWillPop: () {
Navigator.pop(context, 'ANY RETURN VALUE');
return new Future(() => false);
},