Query in button error on the first click flutter - flutter

I have problem in button to search user by phone number. When I press button for the first time, query can't retrieve the data, like this : Debug Console 1. But if I press button again for the second time or more, query can retrieve the data, like this : Debug Console 2.
This is my button code :
Widget tmblKonfirm() {
return Center(
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
),
child: FlatButton(
onPressed: () async{
await pr.show();
print('NoHP : ' + nohp);
final dataUser = FirebaseDatabase.instance
.reference()
.child("users")
.orderByChild("pengguna_nomor")
.equalTo(nohp);
print('Data User : ' + dataUser.toString());
dataUser.once().then((DataSnapshot snapshot) {
Map<dynamic, dynamic> data = snapshot.value;
setState(() {
testUser = snapshot.value.toString();
});
data.forEach((key, values) {
lists.add(values);
});
});
print('User 1 : ' + lists.toString());
pr.hide();
if (_formKeyKonfirm.currentState.validate()) {
print('User 2 : ' + testUser.toString());
if (testUser != 'null'){
// setState(() {
sttsUser = true;
// });
// showWidgetTopup();
}
else {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
content: Text(
'Nomor HP tidak terdaftar'),
actions: <Widget>[
FlatButton(
onPressed: () => Navigator.of(context).pop(),
child: Text('OK'),
)
],
);
},
);
}
}
},
child: Text('Konfirmasi',
style: TextStyle(color: Colors.black, fontSize: 25)),
color: Colors.lightBlue,
),
));
}

dataUser is a Query so you always have to fetch the data snapshot (once() or onValue)
Instead of dataUser.once().then use await dataUser.once(), otherwise the snapshot callback can be actually called after the onPressed function finishes

Related

Flutter How to use DropDowns selected item on http.post

I have a drop down button with a list of items in it that are fetched from the api, i need to send to the server the id of the selected item. So right when the user chooses the item and presses the Start Working button i need to send the chosen items id to the server, the problem that im having is working out with sending the chosen items id.
Future<void> postLocationId(String id) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String? authorization = prefs.getString('authorization');
var url = 'some url';
try {
final response = await http.post(
Uri.parse(url),
headers: <String, String>{
'authorization': authorization ?? basicAuth.toString(),
"Content-Type": "application/json"
},
body: jsonEncode(id)
);
print(response.statusCode);
print(id);
} catch (er) {}
}
String? chooseLocation;
late Future<Response> futureData;
late Future<Response> futureDataForAccount;
bool _flag = true;
List<WorkingLocationData>? workingData;
List<AccountData>? accountData;
Scaffold(
body: FutureBuilder<List<Response>>(
future: Future.wait([futureData, futureDataForAccount]),
builder: (context, snapshot) {
if (snapshot.hasData) {
List<WorkingLocationData> data1 =
parsedData(snapshot.data![0].body);
workingData = data1;
AccountData data3 =
AccountData.fromJson(json.decode(snapshot.data![1].body));
child: DropdownButton<String>(
value: chooseLocation,
hint: const Text('Select a location'),
isExpanded: true,
items: workingData!.map((some) {
return DropdownMenuItem(
child: Text(some.name + ' (${some.location})'),
value: some.id,
);
}).toList(),
onChanged: (String? displayedValue) {
setState(
() {
chooseLocation = displayedValue!;
},
);
},
),
),
SizedBox(height: height * 0.150),
Column(
children: [
ElevatedButton(
onPressed: () async {
final WorkingLocationData locatonData = workingData!.firstWhere((some) => some.location == chooseLocation);
await postLocationId(locatonData.id);
print(locatonData.id);
setState(() {
_flag = !_flag;
});
},
child: Text(_flag ? 'Start Work' : 'Stop Work'),
style: ElevatedButton.styleFrom(
primary: _flag ? Colors.teal : Colors.red,
),
),
],
You need to add the parameter you want: postLocationId(String id).
Without changing the structure too much you can search through workingData for the item with the matching location and pass it on press like:
ElevatedButton(
onPressed: () async {
final WorkingLocationData locatonData = workingData!.firstWhere((some) => some.location == chooseLocation);
postLocationId(locatonData.id);
setState(() {
_flag = !_flag;
});
},
child: Text(_flag ? 'Start Work' : 'Stop Work'),
style: ElevatedButton.styleFrom(
primary: _flag ? Colors.teal : Colors.red,
),
),
If you're interested in a larger structural change I use https://pub.dev/packages/flutter_typeahead for this kind of thing

Flutter) Dialog TextButton's function is executed automatically without tab the button

Hi I'm newbie at coding and Flutter is my first language.
I want to delete Firestore data(Chat List) which is in the ListView from the StreamBuilder when user long tab the 'OK' button in AlertDialog.
(Process Summary:
longPress one item of the ListViewBuilder
show check AlertDialog
tab 'OK'
OK button's Function execute)
But when i longPress the item, then the deleteFunction(OK button's function) is automatically executed before i see the dialog.
How can I make it?
It is really difficult!
Here is my code
StreamBuilder(
stream: chatRoomDB,
builder: (context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.connectionState == ConnectionState.active &&
snapshot.data != null) {
return ListView(
children: snapshot.data!.docs
.map<Widget>((DocumentSnapshot ds) {
Map<String, dynamic> data =
ds.data()! as Map<String, dynamic>;
receiverProfileImageURL = data['ProfileImageURL'];
receiverUID = data['UserID'];
receiverNickName = data['NickName'];
msgContent = data['MsgContent'];
chatID = data['ChatID'];
return Container(
margin: EdgeInsets.all(3),
child: ListTile(
shape: Border(
bottom:
BorderSide(color: Colors.grey, width: 1)),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ChattingRoom(
chatID: data['ChatID'],
receiverUID: data['UserID'],
receiverImageURL:
data['ProfileImageURL'],
receiverNickName: data['NickName'],
)));
},
onLongPress: () => checkDialog2(
context: context,
msg: 'Delete?',
okButtonString: 'OK',
okFunction: chatDBDeleteFunction(
data['UserID'], data['ChatID']),
noButtonString: 'No'),
leading: Container(
child: CircleAvatar(
foregroundImage: NetworkImage(
'$receiverProfileImageURL',
),
backgroundImage:
NetworkImage('$errorImageURL'),
)),
title: Text('$receiverNickName'),
),
);
}).toList());
}
return Container();
})
checkDialog2({
required context,
required String? msg,
required String? okButtonString,
required Future<dynamic> okFunction,
required String? noButtonString,
}) {
print(1);
okFunctions(okFunc) {
print(3);
okFunction;
Navigator.pop(context);
print(4);
}
print(5);
showDialog(
context: context,
builder: (context) {
print(2);
return AlertDialog(content: Text('$msg'), actions: <Widget>[
TextButton(
onPressed: () => okFunctions(okFunction),
child: Text(
'$okButtonString',
textAlign: TextAlign.end,
),
),
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: Text(
'$noButtonString',
textAlign: TextAlign.end,
),
),
]);
});
}
Future chatDBDeleteFunction(receiverUID, chatID) async {
await FirebaseFirestore.instance
.collection('ChatMessage')
.doc('${FirebaseAuth.instance.currentUser?.uid}')
.collection(chatID)
.get()
.then((QuerySnapshot qs) => {
qs.docs.forEach((DocumentSnapshot snapshot) {
snapshot.reference.delete();
})
});
Log Order is,
print 1->5>2 and automatically deleted + showDialog.
At that time(list item already disappeared), if i tab the 'OK' button, then print '3' and error(because there is nothing to delete)
(ps. although i change the okfunction to the void Function, it starts automatically. same)
Thanks for your help
I would prefer passing the UserId and ChatId and then call the chatDBDeleteFunction(receiverUID, chatID) Function and pass the values from values
TextButton(
onPressed: () {
Class.chatDBDeleteFunction(receiverUID, chatID)
}
)
The chatDBDeleteFunction(receiverUID, chatID) function can be static

Update an AlertDialog with success or fail message after network communication

Summarize the Problem:
My application sends information to a server and the server responds with success or failure. I am having trouble updating an AlertDialog with the result of network communication. I am sending multiple items to the server when the user saves their settings and I need to track if all the settings were successfully sent. So when all the settings were successfully sent, I can update the AlertDialog with success. The issue I am seeing with current implementation is it takes me two times to activate the TextButton before I see the correct message. AlertDialog should show the correct message after the first TextButton press labeled as "save". One of the cases I need to solve is if the server is down and the app's connection request times out. Then I need to use something like a CircularProgressIndicator so the user can wait while network communication is being done.
The variable successPrompt is what contains the message with the result of the network transaction. This needs to be updated to the correct message by the time the AlertDialog pops up.
2: What I've tried:
I've tried using FutureBuilder to create the AlertDialog but I got the same result. I need a way to bring up the AlertDialog when I know the result of the network transaction. What happens is the AlertDialog will be brought up but the application is still trying to connect to the server in the background. I want to bring up the widget once this step is done and the socket is closed.
3: Here's the relevant code. Please don't mind the debug prints and commented out code.
import 'package:flutter/material.dart';
import 'dart:io';
import 'globals.dart';
import 'dart:convert' show utf8;
import 'package:local_auth/local_auth.dart';
class SystemsSettingsPage extends StatefulWidget {
final int index;
SystemsSettingsPage({ required this.index});
#override
_SystemsSettingsPage createState() => _SystemsSettingsPage();
}
class _SystemsSettingsPage extends State<SystemsSettingsPage> {
bool tileValTemp = false;
bool tileValDetect = false;
bool tileValCamOff = false;
bool tileValSystem = false;
bool connected = false;
int successCount = 0;
String successPrompt = "";
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
appBar: AppBar(
backgroundColor: Colors.blueAccent,
title: Text("Base Station Settings"),
),
body: Column(
children: <Widget> [
SwitchListTile(value: tileValDetect,
onChanged: (bool val){ setState(() {
tileValDetect = val;
});},
title: Text('Detection notifications', style: TextStyle(color: Colors.white))
),
SwitchListTile(value: tileValTemp,
onChanged: (bool val){ setState(() {
tileValTemp = val;
});},
title: Text('Temperature threshold out of range', style: TextStyle(color: Colors.white))
),
TextButton(
child: const Text("save", style: TextStyle(fontSize: 20.0)),
style: ButtonStyle(foregroundColor: MaterialStateProperty.all<Color>(Colors.white),
padding: MaterialStateProperty.all<EdgeInsets>(EdgeInsets.all(10.0)),
backgroundColor: MaterialStateProperty.all<Color>(Colors.blueAccent)),
onPressed: () {
//successPrompt = "Loading.. Wait 5 seconds to update.";
successCount = 0;
Socket.connect(baseStationAddresses[0], baseStationPort,timeout: Duration(seconds: 5)).then(
(socket) {
print('Connected to: '
'${socket.remoteAddress.address}:${socket
.remotePort}');
String command = "SETSYSTEM," + baseStationNames[0] + ",detectMotion," + "$tileValDetect";
socket.write(command);
socket.listen((data) {
String socketData = utf8.decode(data);
if(socketData == "REQUEST_CONFIRMED") {
successCount += 1;
}
},
onDone: () {
socket.destroy();
},
);
},
).catchError((onError) {
print("here 1");
successPrompt = "There was a problem. Please retry.";
});
Socket.connect(baseStationAddresses[0], baseStationPort,timeout: Duration(seconds: 5)).then(
(socket) {
print('Connected to: '
'${socket.remoteAddress.address}:${socket
.remotePort}');
String command = "SETSYSTEM," + baseStationNames[0] + ",tempThreshold," + "$tileValTemp";
socket.write(command);
socket.listen((data) {
String socketData = utf8.decode(data);
if(socketData == "REQUEST_CONFIRMED") {
successCount += 1;
}
},
onDone: () {
print("SuccessCount $successCount");
if(successCount == 2)
{
print("here 2");
successPrompt = "Setting successfully saved.";
}
else
{
print("here 3");
successPrompt = "Couldn't save, please retry.";
}
socket.destroy();
},
);
}
).catchError((onError) {
print("here 4");
successPrompt = "There was a problem. Please retry.";
});
showDialog(context: context, builder: (context) =>
AlertDialog(
title: Text("Save results"),
content: Text(successPrompt),
actions: <Widget>[
TextButton(onPressed: () => Navigator.pop(context),
child: const Text("OK"),
)
]
)
);
/*
FutureBuilder<String>(
future: getSaveStatus(),
builder: (context, snapshot) {
String nonNullableString = snapshot.data ?? 'Error';
if(snapshot.hasData) {
return AlertDialog(
title: Text("Save results"),
content: Text(nonNullableString),
actions: <Widget>[
TextButton(onPressed: () => Navigator.pop(context),
child: const Text("OK"),
)
]
);
}
return Center(child: CircularProgressIndicator());
},
);*/
}
),
Center(
child:ClipRRect(
borderRadius: BorderRadius.circular(4),
child: Stack(
children: <Widget>[
Positioned.fill(
child: Container(
decoration: const BoxDecoration(
color: Colors.red,
),
),
),
TextButton(
style: TextButton.styleFrom(
padding: const EdgeInsets.all(16.0),
primary: Colors.white,
textStyle: const TextStyle(fontSize: 20),
),
onPressed: () {},
child: const Text('Remove System'),
),
],
),
),
)
],
)
);
}
Future<String> getSaveStatus() async {
return await new Future(() => successPrompt);
}
}
Any suggestion would be helpful.
Wrap the content of the dialog inside of a StatefulBuilder until that your AlertDialog behave as stateless widget Refer:
await showDialog<void>(
context: context,
builder: (BuildContext context) {
int selectedRadio = 0;
return AlertDialog(
content: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Text(successPrompt);
},
),
);
},
);

showDialog always returns null

I try to build cusom confirmation dialog. I tried many different options, but none of them worked for me as it should
Now I have stopped at this one:
Future<bool?> showConfirmationDialog(BuildContext context, String action, {String title = 'Confirmation'}) {
bool result = false;
var confirmDialog = AlertDialog(
title: Text(title),
content: SizedBox(
height: 50,
child: Text(
action,
),
),
actions: [
TextButton(
child: const Text("OK"),
onPressed: () {
Navigator.pop(context, true);
},
),
TextButton(
child: const Text("Cancel"),
onPressed: () {
Navigator.of(context).pop(false);
},
)
],
);
return showDialog<bool>(
context: context,
builder: (BuildContext context) {
return confirmDialog;
},
);
}
but I always get null in result:
confirmDismiss: (DismissDirection direction) async {
var result = await showConfirmationDialog(context, 'Are you sure?');
// here result is null always
if (result == true){
print('yes');
return true;
}
else if(result == false){
print('no');
return false;
}
}
How to make a confirmation dialog box in flutter correctly?

Flutter : how to conditionally repeat a showDialog inside a ListView

I am using the flutter_reactive_ble_example to connect to my Bluetooth module by modifying the file device_list.dart.
and I wonder how do I re prompt the user if password is wrong.
I'm fairly new to flutter, please do ask more details if required.
here is the code snippet that I currently have:
Flexible(
child: ListView(
children: widget.scannerState.discoveredDevices
.map(
(device) => ListTile(
title: Text(tile.name),
subtitle: Text("${tile.name}\n: ${tile.sub}"),
leading: const ConnectIcon(),
onTap: () async {
//stop the scan
widget.stopScan();
//connect to the device
await widget.deviceConn.connect(device.id);
//prompt user for password
final inputData = await showDialog(
context: context,
barrierDismissible:
false, // prevent user from closing the dialog by pressing outside the dialog
builder: (_) {
String userData = "";
return AlertDialog(
title: new Text("Enter Password"),
content: new TextField(
onChanged: (value) {
userData = value;
},
),
actions: <Widget>[
ElevatedButton(
child: Text('Ok'),
onPressed: () async {
//on press subscribe and send the password
response = await ble.subscribeToCharacteristic(characteristic);
//if data failure check, how do I reshow this showDialog??
response.listen((event) {
if(event == 1){
//if return 1, password correct
Navigator.of(context).pop(userData);
}else{
//if not reshow Dialog
//howw?
}
}
//send password
ble.writeCharacteristicWithoutResponse(characteristic, value: userData);
},
)
],
);
},
);
Navigator.of(context).pop(
inputData); // pass data back to the previous page
},
),
)
.toList(),
),
),
You can use a recursion I think, here an example
Future _showPasswordDialog(){
return showDialog(
context: context,
barrierDismissible:
false, // prevent user from closing the dialog by pressing outside the dialog
builder: (_) {
String userData = "";
return AlertDialog(
title: new Text("Enter Password"),
content: new TextField(
onChanged: (value) {
userData = value;
},
),
actions: <Widget>[
ElevatedButton(
child: Text('Ok'),
onPressed: () async {
//on press subscribe and send the password
response = await ble.subscribeToCharacteristic(characteristic);
//if data failure check, how do I reshow this showDialog??
response.listen((event) {
if(event == 1){
//if return 1, password correct
Navigator.of(context).pop(userData);
}else{
//if not reshow Dialog
//howw?
Navigator.of(context).pop();
_showPasswordDialog();
}
}
//send password
ble.writeCharacteristicWithoutResponse(characteristic, value: userData);
},
)
],
);
},
);
}
separate the the alert prompting as another function, and return user details if login success else return null.
Future<String> promptAlert(BuildContext context){
return showDialog(
context: context,
barrierDismissible:
false, // prevent user from closing the dialog by pressing outside the dialog
builder: (_) {
String userData = "";
return AlertDialog(
title: new Text("Enter Password"),
content: new TextField(
onChanged: (value) {
userData = value;
},
),
actions: <Widget>[
ElevatedButton(
child: Text('Ok'),
onPressed: () async {
//on press subscribe and send the password
response = await ble.subscribeToCharacteristic(characteristic);
//if data failure check, how do I reshow this showDialog??
response.listen((event) {
if(event == 1){
//if return 1, password correct
Navigator.of(context).pop(userData);
}else{
Navigator.of(context).pop();
}
}
//send password
ble.writeCharacteristicWithoutResponse(characteristic, value: userData);
},
)
],
);
},
);
}
and check for the returned value is not null on the ListItem onTap
bool isLogin = (await promptAlert(context)) !=null;
while(isLogin ){
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
duration: Duration(seconds: 2),
content: Text('Login Failed Try again')));
String user= await Future.delayed(
Duration(seconds: 2), () => promptAlert(context));
isLogin = user !=null;
}
If you want to show a snackbar and delayed alert,
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
duration: Duration(seconds: 2),
content: Text('Login Failed Try again'),
));
Future.delayed(
Duration(seconds: 2), () => promptAlert(context));