I want to give parameter to the on will pop method to decide either user want to quit or not, I used methods below..
Future<bool> showAlertDialog(BuildContext context){
bool response;
AlertDialog alert = AlertDialog(
elevation: 24,
content: Text("Çıkmak istediğinizden emin misiniz?"),
actions: [
TextButton(onPressed:(){
response=true;
return response;
},
child: Text("EVET")),
TextButton(onPressed:(){
response=false;
return response;
},
child: Text("HAYIR")),
],
);
showDialog(
context: context,
builder: (_)=>alert);
}
When the user press to the android phones back button, alert box will seen which ı shared above, according to parameter which come from this method, the app will close or not, To do that I used on will show method which seems below
onWillPop: ()async{
var response = await showAlertDialog(context);
return response;
},
But when user hit "yes" or "no" nothings happen, What I do wrong, olease help me
You should return a boolean from showDialog() function
Use Navigator.of(context).pop(true); to return true & Navigator.of(context).pop(FALSE); to return false on button press.
Future<bool> showAlertDialog(BuildContext context){
AlertDialog alert = AlertDialog(
elevation: 24,
content: Text("Çıkmak istediğinizden emin misiniz?"),
actions: [
TextButton(onPressed:(){
Navigator.of(context).pop(true);
},
child: Text("EVET")),
TextButton(onPressed:(){
Navigator.of(context).pop(false);
},
child: Text("HAYIR")),
],
);
return showDialog(
context: context,
builder: (_)=>alert);
}
.
You can use a callback on the yes/no option's click action.
Here is the code..
typedef YesOrNoTapCallback = Function(bool);
Some changes in your alert dialog
Future showAlertDialog(BuildContext context, YesOrNoTapCallback isYesTapped) async {
AlertDialog alert = AlertDialog(
elevation: 24,
content: Text("Çıkmak istediğinizden emin misiniz?"),
actions: [
TextButton(onPressed:(){
isYesTapped(true);
},
child: Text("EVET")),
TextButton(onPressed:(){
isYesTapped(false);
},
child: Text("HAYIR")),
],
);
await showDialog(
context: context,
builder: (_)=>alert);
}
And this is how you can use it in onWillPop
onWillPop: () async {
bool isPop = false;
await showAlertDialog(context, (alertResponse) {
Navigator.of(context).pop(); // This will dismiss the alert dialog
isPop = alertResponse;
});
return isPop;
},
Related
I am currently attempting to make a user search list within an alert dialog, which will query users from the project's database based on the user's search input. I am doing this in Android Studio, using Flutter's native language (Dart) and Firebase Cloud Firestore. I have the search bar itself working, but for some reason, whenever I try to actually get the results from the database, my code will access the stream for the Streambuilder being used, but will never touch the actual builder, skipping it entirely. What exactly am I doing wrong here?
The function responsible for creating the alert dialog:
Future createAlertDialog(BuildContext context){
String userToSearch = '';
bool showUsers = false;
return showDialog(context: context, builder: (context){
return AlertDialog(
title: const Text("Search for a user:"),
content: StatefulBuilder(
builder: (context, setState) => Container(
child: CupertinoSearchTextField(
onChanged: (value) => {
setState(() {
showUsers = true;
}),
showUsers
? Expanded(
child: StreamBuilder(
stream: FireStoreMethods().searchUsers(value),
builder: (context, snapshot) {
if (snapshot.connectionState ==
ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
}
if (snapshot.connectionState ==
ConnectionState.none) {
return const Center(child: Text("Internet error"));
}
if (snapshot.hasError) {
return const Center(
child: Text("Something went wrong."),
);
}
return ListView.builder(
shrinkWrap: true,
itemCount: snapshot.data!.docs.length,
itemBuilder: (context, index) {
return ListTile(
onTap: () => Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => ProfileScreen(
uid: snapshot.data!.docs[index]['uid'],
),
),
),
leading: CircleAvatar(
backgroundImage: NetworkImage(
snapshot.data!.docs[index]['photoUrl'],
),
radius: 16,
),
title: Text(
snapshot.data!.docs[index]['username'],
),
);
},
);
},
),
)
: const Expanded(child: Text("error"))
}
),
),
)
);
});
}
Function responsible for querying the database:
Stream searchUsers(String userInput){
String? currentUserID = FirebaseAuth.instance.currentUser?.uid;
//String? valueFromFirebase = '';
Stream s = FirebaseFirestore.instance.collection('users').where('username', isGreaterThanOrEqualTo: userInput).orderBy('username', descending: false).snapshots();
return s;
}
To be clear, I expected this code to create a list of users from the database, under the search bar in the alert dialog, containing the users that match the current input. I tried debugging, changing the positioning of certain lines of code, and comparing and contrasting my code to code I found all over the internet. The actual result that I received was the ability to use the search bar and have the input saved properly, but literally nothing happens after pressing enter. No list is rendered, no error is thrown, and the program continues like nothing happened.
You need to place StreamBuilder inside widget tree to make it visible. Currently having inside onChanged which is just callback method for textFiled.
Future createAlertDialog(BuildContext context) {
String userToSearch = '';
return showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: const Text("Search for a user:"),
content: StatefulBuilder(
builder: (context, setState) => Column(
children: [
CupertinoSearchTextField(
onChanged: (value) {
setState(() {
userToSearch = value;
});
},
),
userToSearch.isNotEmpty
? Expanded(
child: StreamBuilder(
stream: FireStoreMethods().searchUsers(userToSearch),
...........
),
)
: Text("Empty")
],
),
),
);
});
I'm writing an alertDialog where the user can type a name.
The alertDialog has a "OK" button and a "Annulla" button. I want the "OK" button to be disabled while the textField is empty, and then enabled when the user types something.
I'm using a statefulBuilder as recommended by some answers here on StackOverflow, but clearly my implementation is not working.
// Function to display a dialog to insert a new item to the list
Future<void> _displayItemAddDialog(BuildContext context, provider) async {
String itemName;
// clear the textField and add the item to the list
Future<void> onOKPressed() {
_textFieldController.clear();
Navigator.pop(context);
provider.addItem(itemName);
}
showDialog(
context: context,
builder: (context) {
return StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
// used to check if to enable the OK button
bool okButtonEnabled = false;
return AlertDialog(
title: Text('Inserisci Nome Oggetto'),
content: TextField(
onChanged: (value) {
itemName = value;
print(value);
// if the TextField is not empty then enable the button
if (value != "") {
// not working :(
setState() => okButtonEnabled = true;
}
},
controller: _textFieldController,
decoration: InputDecoration(hintText: 'Nome'),
),
actions: <Widget>[
TextButton(
onPressed: () {
_textFieldController.clear();
Navigator.pop(context);
},
child: Text('Annulla'),
),
TextButton(
// if button enabled then I change the assigned function
onPressed: okButtonEnabled ? onOKPressed : null,
child: Text('OK')),
],
);
});
});
}
You should move your okButtonEnabled outside StatefulBuilder, so right above it.
showDialog(
context: context,
builder: (context) {
// Move okButtonEnabled here
bool okButtonEnabled = false;
return StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return AlertDialog(
title: Text('Inserisci Nome Oggetto'),
content: TextField(
onChanged: (value) {
itemName = value;
print(value);
// if the TextField is not empty then enable the button
if (value != "") {
setState(() => okButtonEnabled = true);
}
},
controller: _textFieldController,
decoration: InputDecoration(hintText: 'Nome'),
),
actions: <Widget>[
TextButton(
onPressed: () {
_textFieldController.clear();
Navigator.pop(context);
},
child: Text('Annulla'),
),
TextButton(
// if button enabled then I change the assigned function
onPressed: okButtonEnabled ? onOKPressed : null,
child: Text('OK')),
],
);
},
);
},
);
I have created one method outside the scaffold in the build method but not able to setState() from there.
I have written code like this :
#override
Widget build(BuildContext context) {
//Method that returns alert dialog
method(){
setState(() {
selectedType = newValue;
enableBrand = true;
});
}
//Main scaffold from where i'm calling the alert dialog method
return scaffold()
}
But this doesn't work, the state is not getting updated, so can anyone suggest what is the issue here?
I want to show an AlertDialog on click of a button so i have created a method for that in the build method and trying to setState() from there but it is not working.
Thanks in advance.
void _showDialog() {
showDialog(
context: context,
builder: (context) {
return StatefulBuilder( // StatefulBuilder
builder: (context, setState) {
return AlertDialog(
actions: <Widget>[
],
);
},
);
},
);
}
First of all, is your class extending StatefulWidget?
In the onPressed of your button you could do something like this example.
An AlertDialog can give back a value when popped. I just took the example Alertdialog for now and returned a String.
Then you can set your state with the new value.
onPressed: () async {
var newValue = await showDialog(
context: context,
builder: (c) => AlertDialog(
title: Text('AlertDialog Title'),
content: SingleChildScrollView(
child: ListBody(
children: <Widget>[
Text('This is a demo alert dialog.'),
Text(
'Would you like to approve of this message?'),
],
),
),
actions: <Widget>[
FlatButton(
child: Text('Approve'),
onPressed: () {
Navigator.of(context).pop("YOUR_NEW_VALUE");
},
),
],
));
//newValue now has the value "YOUR_NEW_VALUE"
setState((){
selectedType = newValue;
enableBrand = true;
});
}
I have an app for showing world times. I have a page for changing different locations around the world. It's a ListView.
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: colorOne,
appBar: AppBar(
backgroundColor: Colors.black,
title: Text("Change location"),
centerTitle: true,
elevation: 0.0,
),
body: ListView.builder(
physics: const AlwaysScrollableScrollPhysics(),
padding: EdgeInsets.fromLTRB(5, 10, 5, 0),
itemCount: locations.length,
itemBuilder: (context, index) {
return Card(
child: ListTile(
onTap: () {
updateTime(index);
... rest code
As you can see, when I tap on ListTIle, it calls updateTime function
updateTime function:
void updateTime(index) async {
WorldTime instance = locations[index];
await instance.getTime();
Navigator.pop(context, {
"location": instance.location,
"flag": instance.flag,
"time": instance.time,
"date": instance.date,
"isDayTime": instance.isDayTime,
});
// obtain shared preferences
final savingLastLocation = await SharedPreferences.getInstance();
// set value
savingLastLocation.setString("location", instance.location);
savingLastLocation.setString("url", instance.url);
savingLastLocation.setString("flag", instance.flag);
}
If user starts spamming on tiles while awaiting for that function, app will either show full blank grey screen or drop red screen of death saying "boolean expression must be null".
How can I add some kind of loading screen/widget or prevent calling function again if it's already called once?
You can wrap your screen with IgnorePointer, which ignores any click.
Create bool variable.
bool ignore = false;
bool methodcalled = false; // new added line variable
Now wrap your scaffold with IgnorePointer.
return IgnorePointer(
ignoring: ignore,
child: Scaffold(
now, set ignore variable to true when user tap on any item.
onTap: () {
setState(() {
ignore = true;
});
updateTime(index).then((_){
setState(() {
ignore = false;
});
});
.... rest code
Add return in your method.
return 1
void updateTime(index) async {
if(!methodcalled){
methodcalled = !methodcalled;
}else{
return 0;
}
WorldTime instance = locations[index];
await instance.getTime();
Navigator.pop(context, {
"location": instance.location,
"flag": instance.flag,
"time": instance.time,
"date": instance.date,
"isDayTime": instance.isDayTime,
});
// obtain shared preferences
final savingLastLocation = await SharedPreferences.getInstance();
// set value
savingLastLocation.setString("location", instance.location);
savingLastLocation.setString("url", instance.url);
savingLastLocation.setString("flag", instance.flag);
methodcalled = !methodcalled; // added line
return 1; // added line
}
onPressed set like this
onPressed: () async {
dynamic result =
await Navigator.pushNamed(context, '/location');
if (result != null) {
setState(() {
data = {
'location': result['location'],
'flag': result['flag'],
'time': result['time'],
'isDateTime': result['isDateTime']
};
});
}
},
i want to for loop a button i'm using this code but it show an error , i'm stuck here for 1 day thank you
I want to use for loop because this data is dynamic.
showDialog(
barrierDismissible: true,
context: context,
builder: (BuildContext context) {
// return object of type Dialog
return CupertinoAlertDialog(
title: Text('Add Location'),
actions: <Widget>[
for (var q = 1;q<=2;q++){
FlatButton(
child: new Text("Location A"),
onPressed: () {
Navigator.of(context).pop();
locationA = 'Location A';
},
),
}
],
);
},
);```
I have created a simple method that hopefully fits your needs. The method returns a list that uses a loop to add items to the list. In the end, it returns the populated list.
showDialog(
barrierDismissible: true,
context: context,
builder: (BuildContext context) {
// return object of type Dialog
return CupertinoAlertDialog(
title: Text('Add Location'),
actions: _getList(), // try with or without the ()'s
);
},
);
// the method
List<Widget> _getList() {
List<Widget> temp = [];
for (var q = 1; q<=2; q++) {
temp.add(
FlatButton(
child: new Text("Location A"),
onPressed: () {
Navigator.of(context).pop();
locationA = 'Location A';
},
);
);
}
return temp;
}