An alert dialog is not displayed at the widget load, why? - flutter

In my application I am reading a qr code with the flutter_barcode_scanner package, and when reading a valid code it is supposed to show me an alert dialog saying that it is correct, otherwise, well, no.
To implement this package, an example tells us that based on an action (such as pressing a button) start the scanning method, and save what is read in a variable. Thus:
Future<void> scanQr() async {
try {
final qrCode = await FlutterBarcodeScanner.scanBarcode(
'#ffffff', 'Cancel', true, ScanMode.QR);
if (!mounted) {
return;
}
setState(() {
this.qrCode = qrCode.isEmpty
? 'EMPTY'
: qrCode == '-1'
? 'INVALID'
: qrCode;
});
} on PlatformException {
qrCode = 'fail';
}
}
We can see what the set state occupies in order to update the value. This in a variable that, as you can see, is called qrCode. Which in the example puts it in a text widget and it works fine. when reading something, it is updated.
So, what I want to do is a simple validation, and based on said validation, show an alertdialog saying if what was read in the qr is right or wrong.
I have my validation in the same setstate, I ask if a certain part of the read string looks like what I want. Likewise, I have an int variable, which when updated in the set state, draws an alertdialog.
Future<void> scanQr() async {
try {
final qrCode = await FlutterBarcodeScanner.scanBarcode(
'#ffffff', 'Cancel', true, ScanMode.QR);
if (!mounted) {
return;
}
setState(() {
this.qrCode = qrCode.isEmpty
? 'EMPTY'
: qrCode == '-1'
? 'INVALID'
: qrCode;
});
if (qrCode.substring(0, 2) == 'somepattern') {
setState(() {
value = 1;
});
} else {
setState(() {
value = 2;
});
}
//saveContact(qrCode);
} on PlatformException {
qrCode = 'fail';
}
}
In the build method I have:
String qrCode = '';
int value = 0;
#override
Widget build(BuildContext context) {
//Future(() async {
(value == 1)
? alerta('Yeah', "This code are right!")
: (value == 2)
? alerta(
'Oh no!', 'An invalid qr code')
: null;
//});
return Scaffold(...
As you can see, I even try it with a future to give it time to draw, but it doesn't show any alertDialog. The text widget with the each is displayed. Even, i print the value, and its right, prints 2 and 1 respectively; but the alert dialog is never showed
Why doesn't it draw my alert dialog? What am I doing wrong?

Instead of writing the alert dialog logic inside build method, you should have it in scanQR method
Future<void> scanQr() async {
try {
final qrCode = await FlutterBarcodeScanner.scanBarcode(
'#ffffff', 'Cancel', true, ScanMode.QR);
if (!mounted) {
return;
}
setState(() {
this.qrCode = qrCode.isEmpty
? 'EMPTY'
: qrCode == '-1'
? 'INVALID'
: qrCode;
});
if (qrCode.substring(0, 2) == 'somepattern') {
setState(() {
value = 1;
alerta('Yeah', "This code are right!");
});
} else {
setState(() {
value = 2;
alerta(
'Oh no!', 'An invalid qr code');
});
}
//saveContact(qrCode);
} on PlatformException {
qrCode = 'fail';
}
}
Try this and let me know.

Related

flutter/dart : How to close camera after continuous few barcodes scanning

I have used flutter_barcode_scanner: ^2.0.0 package to scan barcodes it work fine but I scan continuously and after few barcodes i want to close the camera and return back to screen now I am stock so how the camera is closed after scanning few barcodes.
the following is a scenario I wont to implement
scanBarcode() {
FlutterBarcodeScanner.getBarcodeStreamReceiver(
"#ff6666",
"Cancel",
false,
ScanMode.DEFAULT,
)!
.listen(
(barcode) {
setState(() {
list.add(barcode);
});
if (list.length == 10) {
// I want to close camera here and show page again?
}
},
);
}
I had the same issue. The workaround is to loop on single scan. So as a result you can still continue to use flutter_barcode_scanner package :).
In my case i don't need to call setState or something like to update the UI but you can do it if you need.
Future<String?> _scanBarcodeNormal() async {
String? barcodeScanRes;
// Platform messages may fail, so we use a try/catch PlatformException.
try {
barcodeScanRes = await FlutterBarcodeScanner.scanBarcode('#ff6666', 'Cancel', true, ScanMode.BARCODE);
} on PlatformException {
barcodeScanRes = 'Failed to get platform version.';
}
return barcodeScanRes;
}
void _scanBarCodeListAndFetchData() async{
String? barcode = await _scanBarcodeNormal();
final List<String> barcodeList = [];
// continue scanning
while(barcode != null){
if(barcode == "-1"){ // cancel button -> -1
barcode = null;
}
// add the barcode
else {
// avoid duplicates
if(!barcodeList.contains(barcode)){
barcodeList.add(barcode);
}
// you can also call setState(() {....} here
if (barcodeList.length == 10) {
barcode = null; // out !
}
else{ // next scan
barcode = await _scanBarcodeNormal();
}
}
}
if(barcodeList.isEmpty) {
UiUtils.showToast(message: "No barcode scanned, abort", toastLength: Toast.LENGTH_SHORT);
}
else{
UiUtils.showToast(message: "${barcodeList.length} barcodes scanned", toastLength: Toast.LENGTH_SHORT);
// your work
}
}

Unhandled Exception: Null check operator used on a null value Uint8List

Unhandled Exception: Null check operator used on a null value
Friends, I have indicated the place where the error is below and I tried a few things, but I still could not get rid of this error. Does anyone know the solution?
When I remove ! from _image!, I still get an error.
Uint8List? _image;
signUpUser() async {
// set loading to true
setState(() {
_isLoading = true;
});
String res = await AuthMethods().signUpUser(
email: _emailController.text,
password: _passwordController.text,
username: _usernameController.text,
bio: _bioController.text,
gender: _genderController.text,
file: _image! //--> error);
if (res == "success") {
setState(() {
_isLoading = false;
});
Navigator.push(context, MaterialPageRoute(builder: (context) => HomePage()));
} else {
setState(() {
_isLoading = false;
});
// show the error
showSnackBar(context, res);
}
}
selectImage() async {
Uint8List im = await pickImage(ImageSource.gallery);
setState(() {
_image = im;
});
}
The _image is null. I don't see where you call the selectImage() function, and other than that function _image is never defined. Even if selectImage() was called, when the picker appears the user may not actually pick an image and cancel it or another error can happen. The AuthMethods().signUpUser() method seems to not allow the file parameter to be null, that's why when you remove the ! it doesn't work.

How to use function output as a conditional of IF ELSE or Ternary operator

So for some background, I implemented a function that reads from Firebase's real-time database and returns a child node. I have built a button that is meant to check if that function returns the object or null if the function returns an object I want the snack bar to display a message.
ElevatedButton(
onPressed: () {
if (validateUsername() != null) {
print("conditional: test");
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content: Text(
"Username has been taken please try a different one"),
duration: Duration(seconds: 5)));
} else {
return null;
}
},
I had some success with the function by turning it into an async function
validateUsername() async {
final database = FirebaseDatabase.instance.ref().child("/takenUsernames");
await database
.child(_usernameController.text.trim())
.once()
.then((DatabaseEvent event) {
final snapShot = event.snapshot;
final value = snapShot.value;
print("function result: $value");
return value;
});
}
When I turn it to an async function the snack bar displays the message but unfortunately even when the conditional is equal to a null, it for some reason continues to display the message and prints the "test"output. But if I were to try taking away the async the snack bar doesn't print and the "test" in the conditional doesn't print.non-async output
Any help would be appreciated and thanks for your time.
Try this approach, using the await in a variable will wait for the value then the if will evaluate what the result.
ElevatedButton(
onPressed: () async {
String validation = await validateUsername(); // I used type String but you should use the type that will be return.
if (validation != null) {
print("conditional: test");
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content: Text(
"Username has been taken please try a different one"),
duration: Duration(seconds: 5)));
} else {
return;
}
},
)
add try/catch
validateUsername() async {
try {
final database = FirebaseDatabase.instance.ref().child("/takenUsernames");
await database
.child(_usernameController.text.trim())
.once()
.then((DatabaseEvent event) {
final snapShot = event.snapshot;
final value = snapShot.value;
print("function result: $value");
return value;
});
} catch(e) {
print("err $e");
return null;
}
}
Thanks to some help from #WilsonToribio, I was able to use the information he gave and also implement a few changes to the validateUsername() function
as seen here
validateUsername() async {
try {
final database = FirebaseDatabase.instance.ref().child("/usernames");
final response = await database
.child(_usernameController.text.trim())
.once()
.then((event) {
final dataSnapshot = event.snapshot;
if (dataSnapshot.value != null) {
return dataSnapshot.value;
}
});
return response;
} catch (e) {
print("err $e");
return null;
}
}

Flutter Multiple setState in Async Function

I'm trying to create a Login Page for my app. I want it to say 'Logging In' then update if there's an issue.
When run, the app doesn't appear to do anything for the timeout duration. I'm guessing the 'Logging In' status is appearing microseconds before the AlertDialog shows as both the status and AlertDialog seem to appear at the same time. Once I click OK, the status updates to 'Retry Login'.
What I want is the 'Logging In' to appear as the text for my status box as soon as the button is pressed.
Button onPressed
ElevatedButton(
onPressed: () async {
setState((){
statusText = 'Logging In';
});
LoginNow();
TimeOutCheck();
},
child: Text('Log In'),
),
TimeOutCheck()
Future<void> TimeOutCheck() async{
hasTimedOut = false;
Duration timeoutDuration = GLOBAL_CONFIG.DEFAULT_TIMEOUT;
final endTime = DateTime.now().add(timeoutDuration);
while (!isComplete && DateTime.now().isBefore(endTime)){
sleep(Duration(milliseconds: 500));
print('Waiting ${DateTime.now()}');
}
hasTimedOut = !isComplete;
if (hasTimedOut){
await _showAlert('Timeout', 'Login Attempt Timed Out.\n\nPlease try again.');
setState(() {
_StatusText = 'Retry Login';
});
}
}
LoginNow()
Future<void> LoginNow(BuildContext context) async {
final String funcName = 'LoginNow';
bool doLogin = false;
_LoginForm.currentState!.validate();
setState(() {
if (LoginInfo['username'] == null || LoginInfo['username'] == ''){
_StatusText = 'Please Enter your User Name';
isComplete = true;
}
else if (LoginInfo['password'] == null || LoginInfo['password'] == '') {
_StatusText = 'Please Enter your Password';
isComplete = true;
}
else {
logger.wtf('[${className}.${funcName}]\t Setting Status to:\t "Logging In"\n');
//_StatusText = 'Logging In';
doLogin = true;
}
});
if (doLogin){
logger.d('[$className.$funcName\t Do Login: $doLogin');
logger.d('[$className.$funcName\t _StatusText Value: $_StatusText');
String URL = GetLoginRequest();
Uri uri = Uri.parse(URL);
var response = await http.get(uri);
isComplete = true;
// Do the rest of login stuff
}
I think what you need is to simply await the functions.
...
await LoginNow();
await TimeOutCheck();
// Make sure to update the status text at the end.
setState(() {
statusText = "Finished";
}
The problem is in the TimeOutCheck(). During the while, I'm using Sleep. Sleep is a synchronous function. Sleep is holding up all changes that may be completed by other async processes.
while (!isComplete && DateTime.now().isBefore(endTime)){
sleep(Duration(milliseconds: 500));
}
Instead of Sleep, I should've used Future.delayed(). Future.delayed() allows other threads to run while the thread it's called in waits for the delay.
while (!isComplete && DateTime.now().isBefore(endTime)){
await Future.delayed(Duration(milliseconds: 500));
}
The working code looks like:
Future<void> TimeOutCheck() async{
this.hasTimedOut = false;
Duration timeoutDuration = GLOBAL_CONFIG.DEFAULT_TIMEOUT;
final endTime = DateTime.now().add(timeoutDuration);
while (!(this.isComplete) && DateTime.now().isBefore(endTime)){
await Future.delayed(Duration(milliseconds: 500));
print('Waiting ${DateTime.now()}\n\t isComplete\t ${this.isComplete}');
}
this.hasTimedOut = !this.isComplete;
if (this.hasTimedOut && !this.isComplete){
await _showAlert('Timeout', 'Login Attempt Timed Out.\n\nPlease try again.');
setState(() {
this._StatusText = 'Retry Login';
});
}
}

Disable Button While processing Flutter

How can I disable a a button while in processing? I have made a like button, but it takes some delay to register the like on db. How can I prevent the button press while processing is done. ??
onPressed: () {
setState(() {
_color = Colors.green;
_icon = Icon(Icons.favorite);
final like = LikeData(
campaingID: donation.campaignID,
dateTime: Timestamp.now(),
userName: FirebaseAuth.instance.currentUser.displayName,
userId: user.uid,
);
likeService.newLike(donation, like);
how can i prevent double tap on this button event??
try this
bool isProcessing = false; // in your class before build method
onPressed: !isProcessing ? () {
setState(() {
isProcessing = true;
_color = Colors.green;
_icon = Icon(Icons.favorite);
final like = LikeData(
campaingID: donation.campaignID,
dateTime: Timestamp.now(),
userName: FirebaseAuth.instance.currentUser.displayName,
userId: user.uid,
);
likeService.newLike(donation, like).then((val) {
setState() {
isProcessing = false;
}
});
});
} : null,
Think of it as three different states, e.g. "initial", "pressedConfirmationPending" and "pressedAndConfirmed". The button can then be enabled depending on this status, e.g.:
String buttonStatus = 'initial';
...
MyButton(
...
enabled: buttonStatus == 'initial' || buttonStatus == 'pressedAndConfirmed',
...
);
Set the status to 'pressedConfirmationPending' just before you start the input processing, and then to 'pressedAndConfirmed' after your receive confirmation that processing is finished.
Also, the button's design and child widget will most likely be different, and possibly also it's onPressed functionality.
You can do adding a variable between processing.
I think that 'newLike' is synchronous method.
So you set checking variable true and processing 'newLike'.
After that set checking variable false to enable button.
bool isProcessing = false;
...
onPressed: isProcessing ? null : () async {
setState(() {
isProcessing = true;
final like = LikeData(
campaingID: donation.campaignID,
dateTime: Timestamp.now(),
userName: FirebaseAuth.instance.currentUser.displayName,
userId: user.uid,
);
await likeService.newLike(donation, like);
});
setState(() {
_color = Colors.green;
_icon = Icon(Icons.favorite);
isProcessing = false;
});
firstly you need to create bool variable like this
'
bool _isworking=false;
onPressed:(){
if(_isworking==false){
setState(() {
_isworking=true;
});
{{ here your function }}
setState(() {
_isworking=false;
});
}
else
{
print('the Function is working now please wait ');
//you can edit this and show the user a dialog that the process is going on and he need to wait
}
}