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
}
CodePudding user response:
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";
}
CodePudding user response:
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';
});
}
}