I need to press the button 2 times in order to function (flutter) - flutter

I have a problem implementing a login button, when I press it, it will not function at first, but when I press it again (same value fields) it works.
here's my code
Button:
Center(child: RaisedButton(onPressed: (){
setState(() {
onPressedLogin(userName.text,password.text);
});
}
The OnPressedLogin():
void onPressedLogin(String userName,String password) async{
bool isValid = false;
var value = await dataBaseHelper.getUserList();
for(User userOB in value){
//print(userOB.password+" "+password),
if(userName == userOB.username && password == userOB.password) {
isValid = true;
this.password.clear();
this.userName.clear();
inputTextColor = Colors.grey[850];
invalidCredentials = "";
print("YES");
//Navigator.push(context, MaterialPageRoute(builder: (context) => Home()));
break;
}
}
if(!isValid){
inputTextColor = Colors.red[800];
invalidCredentials = "Invalid Credentials";
}

You are using a Future but in setState() you are not waiting for it so that's way it work in the second press (the value take time to change).
To make it work with single press you have to a wait the Future to complete before rebuilding, here how:
First change the return type of the function
Future<void> onPressedLogin(String userName,String password)
Then in the RaisedButton
onPressed: () async {
await onPressedLogin(userName.text,password.text);
setState(() {});
},

The moment you setState(), the UI will refresh!
Probably that's the issue, let me explain:
What you should do is to call your function before setState(), so that the screen is refreshed with the new info.
Center(child: RaisedButton(onPressed: (){
onPressedLogin(userName.text,password.text);
setState(() {
//Variables that change for the refresh.
});
}
In your specific case, I don't see the need for SetState() as you are only printing values in log, not changing the UI.
Hope it is helpful.

Related

Automatically set State of Button WITHOUT pressing it

I have got a State Management Problem I couldn't get rid of and I want to reach out to you.
Basically, I activate with the Buttons a game and I am sending a String to the uC. The uC does its stuff and sends a response to Flutter including gameFinished=true (that works).
Now I want to reset the State of the Button to the init state WITHOUT pressing the Button. Following are some things I tried that didn't work.
#override
void initState() {
super.initState();
setState(() {
gameAktivated = false;
gameStarted = false;
});
}
void asyncSetState() async {
setState(() async {
gameAktivated = false;
gameStarted = false;
});
}
I am changing the style from "Start" to "Stop" when the Button is pressed and I send Data to the uC. (Works)
Edit: Ofc I have a second button that triggers gameAktivated=true :)
ElevatedButton(
onPressed: () {
if (gameAktivated) {
setState(() {
gameStarted = !gameStarted;
});
if (gameStarted) {
//Send Data to uC
} else if (!gameStarted) {
//Send Data to uC
}
}
},
child:
!gameStarted ? const Text('Start') : const Text('Stop'),
),
Button Displays Stop now.
Following I am receiving a String from the uC that I jsonEncode and I receive gameFinished=true. (Works)
Container(
child: streamInit
? StreamBuilder<List<int>>(
stream: stream,
builder: (BuildContext context,
AsyncSnapshot<List<int>> snapshot) {
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
}
if (snapshot.connectionState ==ConnectionState.active) {
// getting data from Bluetooth
var currentValue =const BluetoothConnection().dataParser(snapshot.data);
config.jsonDeserializeGameFinished(currentValue);
if(config.gameFinished){
setState(() {
gameAktivated = false;
gameStarted = false;
});
asyncSetState();//Tested both methods seperate!
}
return Column(
children: [
Text(config.time.toString()),
],
);
} else {
return const Text(
'Check the stream',
textAlign: TextAlign.center,
);
}
},
): const Text("NaN",textAlign: TextAlign.center,),
),
When I try to reset the state like in the code above this error occures:
Calling setState Async didnt work for me either.
Where and how can I set the state based on the response from the uC?
Is it possible without using Provider Lib?
Thanks in advance Manuel.
Actually this error is not about the changing the state of button. Its a common mistake to update the widget state when its still building the widget tree.
Inside your StreamBuilder, you are trying to update the state before creating the UI which is raising this issue.
if(config.gameFinished){
setState(() {
gameAktivated = false;
gameStarted = false;
});
This will interrupt the build process of StreamBuilder as it will start updating the whole page. You need to move it out of the StreamBuilder's builder method.
To do that simply convert your stream to a broadcast, which will allow you to listen your stream multiple time.
var controller = StreamController<String>.broadcast();
Then inside the initState of the page you can setup a listener method to listen the changes like this
stream.listen((val) => setState((){
number = val;
}));
Here you can change the state values because from here it will not interrupt the widget tree building cycle.
For more details see this example I created
https://dartpad.dev/?id=a7986c44180ef0cb6555405ec25b482d
If you want to call setState() immediately after the build method was called you should use:
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
// this method gets called once directly after the previous setState() finishes.
});
Answer to my own Question:
In initState()
added this:
stream.listen((event) {
String valueJSON = const BluetoothConnection().dataParser(event);
config.jsonDeserializeGameFinished(valueJSON);
if (config.gameFinished) {
setState(() {
gameAktivated = false;
gameStarted = false;
});
}
});
The Code above listens to the stream, UTF-8 Decodes and JSON-Decodes the data. After this you can access the variable to set a state.

how to update a text field on a button pressed with a http result?

The example in flutter doc only shows during initState not onPressed.
I wonder what the correct syntax, to update a text field with a button press that triggers an async http request.
onPressed: () async {
await http
.get(Uri.parse('https://localhost:5001/api/Blog'))
.then((value) => setState(() {
_httpResult = value.body;
}));
}
If you could add more information on it that would be helpful from what I understand:
You’re trying to update a text field, from a button press that fetched content from an url?
If so then the textfield is filled with a variable so in your StatefulWidget class you
Define:
final String yourText = “text blah blah”;
Then in your widget:
Text(“${widget.yourText}”)
And then on the button onPress
Onpress: () async{
try{
await [yourhttpcall].then(
(value) =>
setState(() {
widget.yourText = value;});
);
} catch(e) {
print(e);
}
}
Not sure if this is what you mean?
Again with the info you provided,
It’s hard to go into specifics.

How to wait for action of user in Flutter?

I have a problem with a function. I need to realize a func which will be work correctly when user taps on button. I need to froze this func and wait for user's action. (tap on button).
Maybe y have already to faced to the same prob
void submitAnswer(String submittedAnswer) async {
timerAnimationController.stop();
if (!context.read<QuestionsCubit>().questions()[currentQuestionIndex].attempted) {
context.read<QuestionsCubit>().updateQuestionWithAnswerAndLifeline(context.read<QuestionsCubit>().questions()[currentQuestionIndex].id, submittedAnswer);
updateTotalSecondsToCompleteQuiz();
//change question
await Future.delayed(Duration(seconds: inBetweenQuestionTimeInSeconds));
if (currentQuestionIndex != (context.read<QuestionsCubit>().questions().length - 1)) {
updateSubmittedAnswerForBookmark(context.read<QuestionsCubit>().questions()[currentQuestionIndex]);
timerAnimationController.stop();
//I want to froze this part and wait for user's action.
//When user taps on 'next', it continues working.
//////////////////////////////////////////////////////
changeQuestion();
if (widget.quizType == QuizTypes.audioQuestions) {
timerAnimationController.value = 0.0;
showOptionAnimationController.forward();
} else {
timerAnimationController.forward(from: 0.0);
}
//////////////////////////////////////////////////////
} else {
updateSubmittedAnswerForBookmark(context.read<QuestionsCubit>().questions()[currentQuestionIndex]);
navigateToResultScreen();
}
}
}
When user taps on button, this func continue working
TextButton(
child: Text("Next >>"),
onPressed: () {
setState(() {
callMyFun();
});
//global.setMyStatus == true;
}
)
Disclaimer: while this will solve your problem, it's far away from
good practice, you should split your function into two
different ones
You can achieve that using Completer
Example:
Completer<void>? nextButtonCompleter;
Future<void> submitAnswer(String submittedAnswer) async {
// Your code (removed to make answer short)
// ...
//I want to froze this part and wait for user's action.
//When user taps on 'next', it continues working.
final completer = new Completer<void>();
nextButtonCompleter = completer;
// This line will wait until onPressed called
await completer.future;
// your other code
}
And in your button
TextButton(
child: Text("Next >>"),
onPressed: () {
setState(() {
callMyFun();
});
nextButtonCompleter?.complete();
nextButtonCompleter = null;
}
)

Flutter web, onTap in being called twice on safari

On Safari for some of my onTap functions, my code is being executed twice. It doesn't always happen just from times to times.
for example:
goToSearchPage() {
Navigator.pushNamed(
context, '/searchPage',
arguments: SearchPageArguments(
matchcode: _controllerSearchInput.text.trim().toString()
)
);
_controllerSearchInput.text = '';
}
onTap: () {
_controllerSearchInput.text = matchcode;
goToSearchPage();
},
When onTap sometimes it loads 2 times searchpage, first time with a correct argument and second time with an empty argument.
An other example:
onTap: () {
setState(() {
_loading = true;
});
http.Client()
.get("https://myApi")
.then((response) =>response.body)
.then(json.decode)
.then((json) {
setState(() {
_loading = false;
});
Navigator.popAndPushNamed(
context, '/paymentPage',
arguments: PaymentPageArguments(
ordersToPay: json
)
);
});
}
In this case after my API is being called paymentPage is not showing because I think navigator is being pop twice instead of one time. My API is only being call one time though.
Those weird behaviors only happens in safari, I'm not sure how i could debug it.

How do I interact with UI and async functions by a button press?

So I have a login button that would initiate an async function logMeIn() for logging in, which returns a Future<bool> indicating if logging in was successful.
What I want to achieve is after pressing the button, the text within the button would change into a CircularProgressIndicator, and become plain text when logMeIn() has finished.
I know that I could use a FutureBuilder for one-time async tasks, but I simply can't think of a way to use it here.
How am I supposed to achieve this?
Thanks in advance.
Edit
I didn't provide more information because I'm pretty sure it wasn't how it's supposed to be done. But I'd share what I tried whatsoever.
My original button looks somewhat like this:
RaisedButton(
child: Text("Login")
)
I have a async login function that returns a Future<bool> which indicates if the login is successful or not.
Future<bool> logMeIn() async {
// login tasks here
}
What I want to do is to change the button to the following and run logMeIn() when I press the button, and change it back to the plain text version after it's finished:
RaisedButton(
child: CircularProgressIndicator()
onPressed: () {}
)
What I tried
I tried adding a logging_in boolean as a flag to control it, here I call the button with plain text StaticButton(), the other named ActiveButton:
Then I wrapped the ActiveButton with a FutureBuilder:
logging_in ? FutureBuilder(
future: logMeIn(),
builder: (_, snapshot) {
if(snapshot.hasData)
setState(() {
logging_in = false;
});
else
return ActiveButton();
}
) : StaticButton();
and when I pressed the button it would setState and set logging_in = true.
StaticButton:
RaisedButton(
child: Text("Login"),
onPressed: () {
setState(() {
logging_in = true;
});
}
)
The above is what I tried. Should accomplish the effect, but is definitely not elegant. Is there a better way to achieve this?
What you have isn't that far off. You have a logging_in variable that tells you when to show the CircularProgressIndicator already, which is a good start. The only thing is that FutureBuilder has no use here. It's meant for retrieving data from Futures that need to be displayed in the UI.
What you need to do is call logMeIn in the onPressed of your "Static button" as shown here:
StaticButton:
RaisedButton(
child: Text("Login"),
onPressed: () async {
setState(() {
logging_in = true;
});
await logMeIn();
setState(() {
logging_in = false;
});
}
)
You don't need to switch out the whole button when logging_in changes, just the child of the button.
StaticButton(with child switching):
RaisedButton(
child: logging_in ? CircularProgressIndicator() : Text("Login"),
onPressed: () async {
setState(() {
logging_in = true;
});
await logMeIn();
setState(() {
logging_in = false;
});
}
)
You also probably want to add another bool to prevent multiple clicks of the button while its loading. As a note in using FutureBuilder and essentially any other builder in flutter, you always need to return a widget from the builder function, which your current implementation does not do.