I am trying to activate a button when a boolean becomes true. I have a class which holds a bool which is set as false. Then I have a custom widget in which I have a final bool and I am setting it to true when I call the widget in the main app. When the final bool is true I set the bool from the class to true. Then I use ternary operators on a button. What I am trying to do is when the users answer the last question the submit button should be enabled. Even though the class bool is set up as true the button doesn't activate. If I refresh the page, the button activates. I think it's a state issue
P.S. I removed some of the unnecessary code.
Score class
class Score {
static int score = 0;
static bool allQuestionsAnswered = false; //THIS VARIABLE WILL BE CHANGED BASED ON THE FINAL VARIABLE
}
Main Widget with the Submit button
class Quiz extends StatefulWidget {
const Quiz({Key? key}) : super(key: key);
@override
_QuizState createState() => _QuizState();
}
class _QuizState extends State<Quiz> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: IconButton(
icon: Icon(Icons.arrow_back_rounded),
onPressed: () {
var route = ModalRoute.of(context);
var name = route?.settings.name;
if (name == null) {
Score.score = 0;
Navigator.push(context,
MaterialPageRoute(builder: (context) => Homepage()));
}
},
),
title: const Text('CyberQuiz'),
),
body: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Column(
children: [
Text('Score: ' Score.score.toString()),
SizedBox(
height: 150,
),
Text(
'Online Behaviour Questions',
style: TextStyle(
fontSize: 35,
color: Colors.white,
),
),
SizedBox(
height: 20,
),
Text(
'Scale based answers, Strongly Disagree (1) to Agree (5)',
style: TextStyle(
fontSize: 20,
color: Colors.white,
),
),
SizedBox(
height: 50,
),
OnlineBehaviour(
'6. I tend to use similar passwords for multiple accounts',
'Using the same password accross all online accounts, users give '
'hackers easy access to their whole digital life. It is like leaving '
'the keys under the doormat. If a hacker gains access to one user account,'
' he or she can easily take over all online account and impersonate them',
false),
SizedBox(
height: 80,
),
OnlineBehaviour(
'7. I like posting stuff on social media to socialise with my friends',
'While it may seem like'
'the information is being share with your friends and family,'
'it can also be share with hackers and scammers who troll the social media sites',
false),
SizedBox(
height: 80,
),
OnlineBehaviour(
'8. I have a lot of accounts, thus I am using a notebook to write them down',
'Anyone can take your notebook '
'and access all of your personal data including bank account credentials'
'.There are many programs out there that act as a safe "journal for passwords"',
false),
SizedBox(
height: 80,
),
OnlineBehaviour(
'9. I tend to ignore requests from apps to access my location, files, camera etc.',
'Ignoring these requests might be very dangerous'
"as many online predators are in a continuous hunt of new people's location and their personal data."
'E.g. They can check whether your are home or not and they can schedule a potential robbery',
false),
SizedBox(
height: 80,
),
OnlineBehaviour(
'10. I accept all terms & conditions of all apps/websites without reading them',
'All of us are guilty for this one because a lot of websites/apps have loads of pages '
'talking about their conditions when accessing their services. If you ignore these they might'
'use the data stored on you in bad ways such as selling it to other '
'dodgy companies who might use your data in malicious ways',
true), //THIS IS WHERE I SET THE FINAL VARIABLE TO TRUE
ElevatedButton(
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.all(Colors.orange)),
onPressed: Score.allQuestionsAnswered == true //THIS IS WHERE I USED TERNARY OPERATOR
? () {
showDialog(
barrierDismissible: false,
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('Well done!'),
content: Text(
'Score: ' Score.score.toString()),
elevation: 24.0,
shape: RoundedRectangleBorder(),
actions: [
TextButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
Homepage()));
},
child: Text('OK'))
],
);
});
}
: null,
child: Text('Submit'))
],
)));
}
}
Custom Widget
class OnlineBehaviour extends StatefulWidget {
final String behaviourQuestion;
final String explanation;
final bool lastQuestionAnswered;
bool buttonTapped = false;
bool questionsAnswered = false;
OnlineBehaviour(
this.behaviourQuestion,
this.explanation,
this.lastQuestionAnswered,
);
@override
_OnlineBehaviourState createState() => _OnlineBehaviourState();
}
class _OnlineBehaviourState extends State<OnlineBehaviour> {
disableButton() {
setState(() {
widget.questionsAnswered = true;
});
}
activateSubmitButton() {
setState(() {
if (widget.lastQuestionAnswered == true) {
Score.allQuestionsAnswered = true; //FUNCTION THAT SETS THE BOOL IN THE SCORE CLASS TO TRUE
}
});
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Text(
widget.behaviourQuestion,
style: TextStyle(fontSize: 30, color: Colors.white),
),
SizedBox(
height: 20,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
ElevatedButton(
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.all(Color(0xFF45687b))),
onPressed: widget.questionsAnswered == false
? () {
setState(() {
Score.score = 5;
activateSubmitButton();
widget.buttonTapped = true;
});
if (widget.buttonTapped == true) {
disableButton();
print(Score.allQuestionsAnswered);
}
}
: null,
child: Text(
'1',
style: TextStyle(
color: Color(0xFFe5e0e4),
fontSize: 15,
fontWeight: FontWeight.bold,
),
)),
CodePudding user response:
I am guessing the issue is that the button remains disabled even after you update the value on the static class.
If that is the issue, then it is probably not because you are failing to change the value, or because your ternary operator is wrong, but simply because you haven't told the main widget to check the value of the static class again. in order to do this, you should change the value within the setState
method (which you do, but you do it using the custom widget's setState
method, which is not going to update the main widget).
There are many ways to try and change this, but what I would do is declare a new parameter to your custom widget, onAllQuestionsAnswered
:
on custom widget
final String behaviourQuestion;
final String explanation;
final bool lastQuestionAnswered;
final VoidCallback onAllQuestionsAnswered; // <--
then when you set the value of the static class, instead you can do this:
if (widget.lastQuestionAnswered == true) {
onAllQuestionsAnswered();
}
finally, on your main widget, you would pass this callback:
OnlineBehaviour(
...
onAllQuestionsAnswered: () => setState(() =>Score.allQuestionsAnswered = true),
);
In general you could probably also get rid of the static variable entirely, and only store the state on the main widget, but that goes out of the scope of this answer.
CodePudding user response:
The issue is here, your Button on Quiz
widget but you are updating the OnlineBehaviour
widgets state, that's why ui is not updating , I will suggest you using state-management
and for now you can use callback method for this.
ON your OnlineBehaviour
I am adding another nullable callBack function so that you can only one time and avoid rest of it
class OnlineBehaviour extends StatefulWidget {
final String behaviourQuestion;
final String explanation;
final bool lastQuestionAnswered;
final Function? callBack;
bool buttonTapped = false;
bool questionsAnswered = false;
OnlineBehaviour(
this.behaviourQuestion,
this.explanation,
this.lastQuestionAnswered, {
this.callBack = null,
});
And Call it on onpress
onPressed: widget.questionsAnswered == false
? () {
if (widget.callBack != null) { // -<this
widget.callBack!();
}
setState(() {
Score.score = 5;
activateSubmitButton();
widget.buttonTapped = true;
});
if (widget.buttonTapped == true) {
disableButton();
print(Score.allQuestionsAnswered);
}
}
: null,
And just for last Question where you like to update the UI use it
OnlineBehaviour(
'10. I accept all terms & conditions of all apps/websites without reading them',
'All of us are guilty for this one because a lot of websites/apps have loads of pages '
'talking about their conditions when accessing their services. If you ignore these they might'
'use the data stored on you in bad ways such as selling it to other '
'dodgy companies who might use your data in malicious ways',
true,
callBack: () {
setState(() {});
},
), //THIS Is
CodePudding user response:
The issue is not about updating the bool value of "allQuestionsAnswered". The issue you face is that you need to update your UI after initializing your main widget. You can achieve this by adding this to your main widget, no callback from another class is needed:
void initState() {
super.initState();
WidgetsBinding.instance
.addPostFrameCallback((_) =>
setState(() {});
}
This code will ensure that your widget will get an extra update of its UI after finishing initializing.