I'm using flutter and GetX, so i'm implementing Obx
inside my code.
I have 3 files:
questionnaire.dart
questionnnaire_controller.dart
popup.dart
Inside the popup.dart
I have the layout of the popup.
Inside questionnaire.dart
I have the code that displays the content of a popup that displays a questionnaire to be answered.
Inside questionnaire_controller.dart
i have some variables and functions that are used, like the function getQuestionnaires()
that gets the questionnaires' data asynchronously, or the list questionnaires
, or a variable selectedQuestionnaire
that keeps the instance of a questionnaire that has been selected.
Inside the popup.dart
I have to display on top of the popup dialog, the title of the questionnaire, if a questionnaire has been selected. A part of the code is the following:
static Future<void> showQuestionnaireInput({String title, Widget child, Widget icon}) async {
bool mobileSize = Get.size.width <= ResponsiveSizingConfig.instance.breakpoints.desktop;
if (mobileSize) {
await Get.to(InputScreenWidget(child, title));
} else {
await showDialog(
context: Get.context,
builder: (context) {
return AlertDialog(
titlePadding: EdgeInsets.all(8),
contentPadding: EdgeInsets.all(8),
title: Container(
decoration: BoxDecoration(border: Border(bottom: BorderSide(color: Colors.grey.shade200, width: 2))),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Padding(
padding: EdgeInsets.only(left: 4),
child: Row(
children: [
if (icon != null) icon,
if (icon != null) SizedBox(width: 4),
Text(title),
Obx(() {
if(questionnaireController.selectedQuestionnaireTitle.value != '')
return Text(questionnaireController.selectedQuestionnaireTitle.value);
else
return Container();
}),
],
),
),
CloseButton(
onPressed: () {
Get.back();
},
)
],
),
),
content: child,
);
});
}
}
}
As you can see, inside the obx i'm getting the value of the selectedQuestionnaireTitle, which is a variable that exists inside questionnnaire_controller.dart
.
Inside the questionnaire.dart
i have a future builder
that bring my questionnaire data, in order for the user to choose one of them via a dropdown and answer the respective questions by clicking next. A part of the code that is useful for our case is the following:
child: Obx(() {
if (questionnaireController.questionnaireState.value == QuestionnaireController.QUESTIONNAIRE_CHOOSE) {
return Container(
width: screenWide ? Get.size.width * 0.5 : Get.size.width * 1,
child: Center(
child: FutureBuilder(
future: questionnaireController.getQuestionnaires(),
builder: (context, snapshot) {
questionnaireController.dialIsBuilt.value = true;
print(questionnaireController.dialIsBuilt.value);
switch (snapshot.connectionState) {
case ConnectionState.waiting:
questionnaireController.dialIsBuilt.value = false;
print(questionnaireController.dialIsBuilt.value);
return CircularProgressIndicator();
default:
if (snapshot.hasData) {
print(questionnaireController.dialIsBuilt.value);
return Column(
children: [
Text(
'choose_questionnaire'.tr,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: TextSize.TEXT_LARGE,
fontWeight: FontWeight.w600,
color: EnvironmentVariables.mainColor,
),
),
SizedBox(
height: 8,
),
Dropdown(
questionnaires: questionnaireController.questionnaires,
selectedQuestionnaire: questionnaireController.selectedQuestionnaire,
),
Obx(
() {
if (questionnaireController.buttonDisplay.value == true) {
return Container(
margin: EdgeInsets.all(16),
child: defaultButton(
text: 'next_question'.tr,
onPressed: () {
questionnaireController.answerQuestionnaire();
},
),
);
} else {
return Container();
}
},
),
],
);
} else
return Column(
children: [
Text(
'choose_questionnaire'.tr,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: TextSize.TEXT_LARGE,
fontWeight: FontWeight.w600,
color: EnvironmentVariables.mainColor,
),
),
// Text("no_data".tr),
SizedBox(
height: 32,
)
],
);
}
}),
),
);
}
You can see, inside the code above, a widget named Dropdown
. It is a stateful widget that i have created. This widget, also exists inside questionnaire.dart
. The code for Dropdown
is as follows.
class Dropdown extends StatefulWidget {
final List questionnaires;
final Questionnaire selectedQuestionnaire;
Dropdown({
this.questionnaires,
this.selectedQuestionnaire,
});
@override
_DropdownState createState() => _DropdownState(
questionnaires: questionnaires,
// dropdownValue: selectedQuestionnaire.title,
);
}
class _DropdownState extends State<Dropdown> {
List questionnaires;
String dropdownValue = questionnaireController.selectedQuestionnaire.title;
_DropdownState({
this.questionnaires,
// this.dropdownValue,
});
@override
Widget build(BuildContext context) {
questionnaireController.setSelectedQuestionnaire(questionnaireController.selectedQuestionnaire);
return DropdownButton(
isExpanded: true,
value: dropdownValue,
icon: Icon(Icons.arrow_downward),
iconSize: 24,
elevation: 16,
style: TextStyle(color: EnvironmentVariables.mainColor, fontSize: TextSize.TEXT_SMALL),
underline: Container(
height: 1.6,
color: EnvironmentVariables.mainColor,
),
onChanged: (newValue) {
widget.questionnaires.forEach((questionnaire) {
if (questionnaire.title == newValue) {
questionnaireController.setSelectedQuestionnaire(questionnaire);
// questionnaireController.selectedQuestionnaire = questionnaire;
}
});
Future.delayed(Duration(seconds: 5), () => setState(() {
dropdownValue = questionnaireController.selectedQuestionnaire.title;
}));
//Show continue button
questionnaireController.showButton();
//Used in reminder
},
items: widget.questionnaires.map((questionnaire) {
return DropdownMenuItem(
value: questionnaire.title,
child: Text(
questionnaire.title,
style: TextStyle(color: EnvironmentVariables.secondaryColor),
),
);
}).toList(),
);
}
}
When i run the code, and i open the popup dialog, i get the following error
The following assertion was thrown building Dropdown(dirty, state: _DropdownState#97b88): setState() or markNeedsBuild() called during build.
This Obx widget cannot be marked as needing to build because the framework is already in the process of building widgets. A widget can be marked as needing to be built during the build phase only if one of its ancestors is currently building. This exception is allowed because the framework builds parent widgets before children, which means a dirty descendant will always be built. Otherwise, the framework might not visit this widget during this build phase. The widget on which setState() or markNeedsBuild() was called was: Obx state: _ObxState#d84a8 The widget which was currently being built when the offending call was made was: Dropdown dirty state: _DropdownState#97b88 The relevant error-causing widget was: Dropdown
My question is, how do i solve this error? I know that the following function can be helpful
WidgetsBinding.instance.addPostFrameCallback((_) {
// executes after build
})
But where should i implement the above function?
Thank you for your time