Alert Dialog stays open through back button push - android

I'm having a little error I can't seem to figure out, and I believe it's due to my Alert Dialog implementation. I'm using the flutter Provider package, and opening my Dialog like this:
_openSearchHistory(
BuildContext context, TextEditingController searchController) {
final searchModel = Provider.of<SearchModel>(context, listen: false);
showDialog(
context: context,
builder: (_) => ChangeNotifierProvider<SearchModel>.value(
value: searchModel,
child: DialogSearchHistory(
searchHistory: searchModel.searchHistory,
searchController: searchController,
),
));
}
The problem is when on android and the user presses the back button, the Dialog does not close, but the page behind the Dialog pops back a page. I have a close button on the Dialog that successfully closes the dialog, but I want users to be able to use the back button for a better experience. The Dialog does close if the user clicks outside of it as well. I've tried wrapping the Dialog with WillPopScope, but it does not get called on a back button press.
Could anyone shed some light on what I'm doing wrong here?
I have tried wrapping the Widget in the calling method with WillPopScope
showDialog(
context: context,
builder: (_) => ChangeNotifierProvider<SearchModel>.value(
value: searchModel,
child: WillPopScope(
onWillPop: () {
Navigator.of(context).pop();
return Future.value(false);
},
child: DialogSearchHistory(
searchHistory: searchModel.searchHistory,
searchController: searchController,
),
),
));
As well as wrapping the Alert Dialog itself in the build function
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () {
Navigator.of(context).pop();
return Future.value(false);
},
child: AlertDialog(
backgroundColor: kCardColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(20.0)),
),...
Neither have worked. The page behind the Dialog pops back, but the Dialog stays put. onWillPop is never hit by the debugger

Late to the party but setting useRootNavigator to false inside showDialog worked for me.
showDialog(
context: context,
useRootNavigator: false,
builder: (context) {
return AlertDialog();
},
);

If the application has multiple Navigator objects, it may be necessary to call Navigator.of(context, rootNavigator: true).pop(result) to close the dialog rather than just Navigator.pop(context, result).
I hope this will close the alert dialog If you have multiple navigator objects. Give it a try
onWillPop: (){
Navigator.of(context, rootNavigator: true).pop();
return Future.value(false);
},
OR
Try Navigator.pop(context);, this will call internally Navigator.of(context).pop() method.
Official Implementation of Navigator.pop() method.
static void pop<T extends Object>(BuildContext context, [ T result ]) {
Navigator.of(context).pop<T>(result);
}
Doc - https://api.flutter.dev/flutter/widgets/Navigator/pop.html

Hi there i face similar issue and the below method worked for me as Vinoth vino tell wrap your page in WillPopScope widget and try to add pop function there like
body: WillPopScope(
onWillPop: (){
Navigator.of(context).pop();
return Future.value(false);
},
child:...your code

Related

How to run InitState() upon back button in flutter?

I have a two pages, in one page, i open Hive box but when I navigate to second page, the dispose() method runs and closes the Hive box. but the problem is, when i click on 'Back' button, the initState doesnt rerun on the first page, so I couldn't open the box again through initState.
here is the code on First page,
#override
initState() {
super.initState();
Hive.openBox<boxModel>('customTable');
}
#override
void dispose() {
Hive.close();
super.dispose();
}
Here is the back in appbar in second page,
AppBar(
leadingWidth: 100,
leading: IconButton(
onPressed: () => Navigator.of(context).pop(),
icon: Icon(
Icons.arrow_back,
color: AppTheme.colors.greyFontColor,
),
),
backgroundColor: AppTheme.colors.appBarColor,
elevation: 0,
iconTheme: IconThemeData(color: AppTheme.colors.greyFontColor),)
so is there a way to re run to the initState upon the back button pressed on second page.
Thanks for any help..
try (context as Element).reassemble(); try this snippet refresh and build widgets again in current screen flutter.
Navigator.pushReplacement(context, MaterialPageRoute(builder: (context) =>PreviousPage() ,)); for previous using navigator.
I don't think that dispose runs when you push a new route, at least not as long as there is a back button to go back, but anyways if you just want to open the box again you can add this to your Navigator.push
Navigator.of(context)
.push(MaterialPageRoute(
builder: (context) => SecondPage(),
)).then((_) {
Hive.openBox<boxModel>('customTable');
});

How to prevent android device back button from going back to splash screen in flutter?

I have made a logo screen for my app which redirects the user to the appropriate location (home screen if the user is logged in and the welcome/splash screen if he is not). After I reach the home screen I can go back to the previous screen by pressing the back button on the android phone. I want to prevent the user from going back to the logo screen.
Things I have tried:
WillPopScope() with onWillPop returning false
My logo screen code:
class LogoScreen extends StatefulWidget {
#override
_LogoScreenState createState() => _LogoScreenState();
}
class _LogoScreenState extends State<LogoScreen> {
var preferences;
#override
void initState() {
super.initState();
startTimer();
}
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async => false,
child: Scaffold(
backgroundColor: Colors.white,
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
"assets/images/g1177.png",
scale: 10.0,
),
],
),
),
),
);
}
startTimer() {
var _duration = Duration(milliseconds: 2000);
return Timer(_duration, navigate);
}
navigate() async {
SharedPreferences preferences = await SharedPreferences.getInstance();
if (preferences.getBool("is_logged_in") == true) {
Navigator.push(context, MaterialPageRoute(builder: (context) {
return HomeScreen();
}));
} else {
Navigator.push(context, MaterialPageRoute(builder: (context) {
return SplashScreen();
}));
}
}
}
Why this approach doesn't work on my device?
Use .pushReplacement(...) instead of .push(...)
pushReplacement Replace the current route of the navigator that most tightly encloses
the given context by pushing the given route and then disposing the
previous route once the new route has finished animating in.
I figured it out. If I apply WillPopScope() on the Logo Screen then when I am on the logo screen, if I press the back button then I can't go further back from that screen. Hence, if I use WillPopScope on the home screen then I can't use the back button on that screen. Here is my Home Screen build method:
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async {
return false;
},
child: Scaffold(
//add your UI here like your appBar, body, bottomNavBar etc..
) //Scaffold
); //WillPopScope

Display full screen loading widget in flutter

I want to show full-screen loading view in flutter while API calling. But when I add loading widget in scaffold body it appears after appbar and bottom navigator.
I am spending lots of time for display loading view in full-screen. Also, I want to prevent back action while API calling.
Well, since you're using Scaffold, then make use of its showDialog() method.
It has a property called barrierDismissible that if you set as false, the user won't be able to close or interact with the screen outside of it.
void _openLoadingDialog(BuildContext context) {
showDialog(
barrierDismissible: false,
context: context,
builder: (BuildContext context) {
return AlertDialog(
content: CircularProgressIndicator(),
);
},
);
}
Once you're done with the API loading, call Navigator.pop(context); to dismiss the dialog.
To prevent the user from clicking the back button on the Dialog, dismissing it, envelop your Scaffold inside a WillPopScope widget and implement the onWillPop function.
#override
Widget build(BuildContext context) {
return WillPopScope(
child: Scaffold(
body: Container(),
),
onWillPop: _onBackButton
);
}
Future<bool> _onBackButton() {
// Implement your logic
return Future.value(false);
}
If you return false on it, the user won't be able to press the back button. So use any logic you desire, e.g 'If I'm loading return false, otherwise return true'.
Full-screen loader: Best solution to display a full screen loader is to use package
https://pub.dev/packages/screen_loader.
Prevent back action while loading: This package this package provides a variable isLoading, use it to prevent navigating back. eg:
// --------------- some_screen.dart ---------------
import 'package:flutter/material.dart';
import 'package:screen_loader/screen_loader.dart';
class SomeScreen extends StatefulWidget {
#override
_SomeScreenState createState() => _SomeScreenState();
}
class _SomeScreenState extends State<SomeScreen> with ScreenLoader<SomeScreen> {
getData() {
this.performFuture(NetworkService.callApi);
}
#override
Widget screen(BuildContext context) {
return WillPopScope(
child: Scaffold(
// your beautiful design..
),
onWillPop: () async {
return !isLoading;
},
);
}
}
// --------------- app_api.dart ---------------
class NetworkService {
static Future callApi() async {
}
}
NOTE: You'll need to see the definition of performFuture to see how it works for different scenarios.
You can use this dialog for full screen loading progress_dialog
Did you not think of removing the Scaffold for the loading screen?

close app on device back button in flutter

The question may be duplicate but I'm unable to found the solution. My app scenario is the same as the almost app. My first screen is Splash screen and holds for two seconds and here login session is checked and upon this condition screen changes like this and below code is run in initState()
_checkPreference() async {
PreferencesConnector myprefs= PreferencesConnector();
id=await myprefs.readString('merchantid');
if(id==null||id==''){
Future.delayed(
const Duration(seconds: 2),
() => Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => Login(),settings: RouteSettings(name: '/login')),
));
}else{
Future.delayed(
const Duration(seconds: 2),
() => Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => DashBoard()),
));
}
}
If the session returns false then it goes to login screen, In Login screen my scaffold inside in WillPopScope widget and that class is stateful class
return WillPopScope(
onWillPop: () {
if (Navigator.canPop(context)) {
//Navigator.pop(context);
Navigator.of(context).pop();
} else {
SystemNavigator.pop();
}
},
child:Scaffold(
body: Stack(
children: <Widget>[
and if LoginApi returns true then it move to dashboard like this
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => DashBoard(),
settings: RouteSettings(name: '/dashboard')),
);
here everything is working fine when the user is already logged-in and we reach to dashboard after the splash and but there is a logOut button on my dashboard when user press logout then there is a dialog appear which asks for logout- if i press yes from dialog button works like this
onPressed:(){
clearSession();
// Navigator.of(context).popUntil(ModalRoute.());
// Navigator.of(context).popUntil('/login', (Route<dynamic> route) => false);
// Navigator.popUntil(context, ModalRoute.withName("/login"));
// Navigator.of(context).pushNamedAndRemoveUntil('/login', (Route<dynamic> route) => false);
Navigator.pushReplacement(context, MaterialPageRoute(builder: (context) => Login()),);
},
after pressing logout it reaches to login screen where I'm unable to close the app when user press back from the device back in login screen but it redirects to the dashboard and then I pressed back then app closed.
What you need to do is first clear all path before going to login() screen.
try this:
onPressed:(){
clearSession();
//Navigator.popUntil(context, ModalRoute.withName('/'));
Navigator.pop(context,true);// It worked for me instead of above line
Navigator.pushReplacement(context, MaterialPageRoute(builder: (context) => Login()),);
},
call this on your logout button
void _logout() {
Navigator.popUntil(context, ModalRoute.withName('/login'));
}
here is the link of official docs

How to go back from web view in flutter?

After clicking the button it should show the web view. I did this. But when press the back button it is closing the app. It does not go back to previous state. If I press back button it should go back to previous page or state. I am getting web view from a function.
Here is my code:
_contentSearch(BuildContext context){
url = "https://www.google.com";
return WebviewScaffold(
url: url,
appBar: AppBar(
title: Text("Google"),
),
withJavascript: true,
withLocalStorage: true,
withZoom: false,
enableAppScheme: true,
)
},
);
}
And I am calling this method like this:
FlatButton(
padding: EdgeInsets.all(0.0),
onPressed: (){
Navigator.pushReplacement(
context,
new MaterialPageRoute(builder: (BuildContext context) => _contentSearch(context)
));
},
child: new Text("Button"),
),
It's not going back. It's always closing the app.
Its because you are using pushReplacement(Replace the current route of the navigator) instead of push
Navigator.push(
context,
MaterialPageRoute(builder: (context) => _contentSearch(context),
);

Categories

Resources