For context, this app consist on a form divided in different cards constructed automatically from a list which says how many questions per card we have and which are those questions. All this is being showed on screen from the provider, in which I have a map with all the answers. This is sent via API at the end of the form.
These questions have different types of answer. In this case the stateful widget which doesn't initiates is the radiobutton (as far as I know it can only be stateful).
How was the bug detected? Navigating into specific cards the answers of radio buttons appear but are not read from the provider because those fields in the form map of the answer still don't exist. What have I tried? I was printing a debug flag on the init method in the radio buttons. What was the result? Some cards seem to start correctly, and never give troubles, but some other cards only load the first radio button.
Additional info: it seems like the back button also causes some malfunctioning because when I answer these questions and go back they won't show the real value from the provider.
For some reference I leave some code, but it's really messy. I thank any kind of help, I'm not looking for the correct code, I just want to know where could the problem be or if anyone has ever been in this situation.
class CustomRadioButton extends StatefulWidget {
final List<String> opcions;
final Map map;
final dynamic field;
const CustomRadioButton({
Key? key,
required this.opcions,
required this.map,
required this.field,
}) : super(key: key);
@override
State<CustomRadioButton> createState() => _CustomRadioButtonState();
}
class _CustomRadioButtonState extends State<CustomRadioButton> {
String _opcio = '';
@override
void initState() {
print('radio: ${widget.field}'); //this line doesn't always execute
if (widget.field.length == 1) {
_opcio = widget.map[widget.field[0]] != null
? widget.map[widget.field[0]] ==
'true' //es string, no booleano (20 min debugando)
? widget.opcions[0]
: widget.opcions[1]
: '';
} else {
_opcio = '';
for (int i = 0; i < widget.field.length; i++) {
//si true set opcio
if (widget.map[widget.field[i]] == 'true') {
_opcio = widget.opcions[i];
break; //sortir del for
}
}
}
super.initState();
}
@override
Widget build(BuildContext context) {
return Column(
children: widget.opcions
.map((e) => RadioListTile(
title: Text(e),
value: e,
groupValue: _opcio,
onChanged: (String? val) {
setState(() {
_opcio = val!;
print(_opcio);
});
if (widget.field.length == 1) {
if (val == widget.opcions[0]) {
widget.map[widget.field[0]] = 'true';
} else {
widget.map[widget.field[0]] = 'false';
}
} else {
for (int i = 0; i < widget.field.length; i++) {
if (val == widget.opcions[i]) {
widget.map[widget.field[i]] = 'true';
} else {
widget.map[widget.field[i]] = 'false';
}
}
}
}))
.toList(),
);
}
}
Methods to build the content of the card
Widget generarPreguntes() {
final entretienProvider =
Provider.of<EntretienProvider>(context, listen: false);
List<Widget> llistatPreguntes = [];
int preguntesPerCard =
entretienProvider.preguntesPerCard[entretienProvider.card];
//calcul de la pregunta que s'ha de mostrar
int preguntaActual = 0;
for (int tarja = entretienProvider.card - 1; tarja >= 0; tarja--) {
preguntaActual =
preguntaActual + entretienProvider.preguntesPerCard[tarja];
}
entretienProvider.pregunta = preguntaActual;
//construccio de les preguntes
for (int i = 0; i < preguntesPerCard; i++) {
//afegir pregunta al llistat final
llistatPreguntes.add(generarPregunta());
//passar a la seguent pregunta
entretienProvider.seguentPregunta();
print('prg: ${entretienProvider.pregunta}');
}
return Column(children: llistatPreguntes);
}
//genera la pregunta actual
Widget generarPregunta() {
final entretienProvider =
Provider.of<EntretienProvider>(context, listen: false);
//accedir a la pregunta amb la pregunta actual
Pregunta pregunta =
entretienProvider.preguntesDeManteniment[entretienProvider.pregunta];
List<Widget> widgetList = [];
int i = 0;
int picCounter = 0;
//comptador de imatges dins d'una mateixa pregunta
//afegir el titol de la pregunta al widget de la pregunta
List<Widget> titolPregunta = [
Flexible(
child: Text((entretienProvider.pregunta + 1).toString() +
'. ' +
pregunta.titol),
),
];
widgetList.add(
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: titolPregunta,
),
);
//iterem per cada camp de la resposta
for (var camp in pregunta.camps) {
String cas = camp.tipus;
//seleccionem un dels tipus de resposta...
switch (cas) {
case 'checkList':
//nomes s'utilitza en la imatge de les diferents seccions de l'escala
widgetList.add(StackImage(entretienProvider: entretienProvider));
break;
case 'boolea':
{
//afegim la opcio booleana
if (camp.descripcio == null) {
titolPregunta.add(
EntretienCheckBox(
title: camp.descripcio ?? '',
providerValue: entretienProvider
.fitxaMantenimentMap[pregunta.respostes[i]] ==
'true'
? true
: false,
map: entretienProvider.fitxaMantenimentMap,
field: pregunta.respostes[i],
),
);
} else {
widgetList.add(
EntretienCheckBox(
title: camp.descripcio,
providerValue: entretienProvider
.fitxaMantenimentMap[pregunta.respostes[i]] ==
'true'
? true
: false,
map: entretienProvider.fitxaMantenimentMap,
field: pregunta.respostes[i],
),
);
}
}
break;
case 'text':
{
//afegim un textfield per resopndre amb text
TextEditingController controller = TextEditingController();
controller.text =
entretienProvider.fitxaMantenimentMap[pregunta.respostes[i]] ??
'';
widgetList.add(Padding(
padding: const EdgeInsets.all(8.0),
child: EntretienCommentInput(
title: camp.descripcio!,
hint: camp.descripcio!,
controller: controller,
field: pregunta.respostes[i],
map: entretienProvider.fitxaMantenimentMap,
),
));
}
break;
case 'number':
{
//afegim un textfield per respondre amb numeros
TextEditingController controller = TextEditingController();
controller.text = entretienProvider
.fitxaMantenimentMap[pregunta.respostes[i].toString()] ??
'';
widgetList.add(Padding(
padding: const EdgeInsets.all(8.0),
child: EntretienCommentInput(
title: camp.descripcio!,
hint: camp.descripcio!,
controller: controller,
field: pregunta.respostes[i],
map: entretienProvider.fitxaMantenimentMap,
obligatori: pregunta.campsObligatoris != null ? true : false,
inputKey: TextInputType.number),
));
}
break;
case 'radioButton':
{
//afegim les opcions de radiobutton, nomes agafarem 1
camp.descripcio != null
? widgetList.add(Padding(
padding: const EdgeInsets.all(8.0),
child: Text(camp.descripcio!),
))
: widgetList.add(
const SizedBox(),
);
print('radioButton selected');
print(camp.opcions);
print(pregunta.respostes[i]);
widgetList.add(Padding(
padding: const EdgeInsets.all(8.0),
child: CustomRadio(
opcions: camp.opcions!,
field: pregunta.respostes[i],
map: entretienProvider.fitxaMantenimentMap,
),
));
}
break;
case 'fotos':
{
//afegim el boto de fotos
picCounter++;
widgetList.add(Padding(
padding: const EdgeInsets.all(8.0),
child: ElevatedButton(
onPressed: () {
Navigator.pushNamed(context, 'entretienImg',
arguments: [pregunta, picCounter]);
},
child: const Icon(Icons.camera_alt)),
));
//no tenim en compte aquest camp ja que s'envia al final
i--;
}
break;
}
i++;
}
return Column(
children: widgetList,
);
}
}
Provider (constructing the questions from here)
Map<String, dynamic> _fitxaMantenimentMap = {};
Map<String, dynamic> get fitxaMantenimentMap => _fitxaMantenimentMap;
set fitxaMantenimentMap(Map<String, dynamic> value) {
_fitxaMantenimentMap = value;
notifyListeners();
}
...
int _posicioFinal = 0;
int get posicioFinal => _posicioFinal;
set posicioFinal(int quantitatCards) {
_posicioFinal = quantitatCards;
notifyListeners();
}
int _card = 0;
int get card => _card;
set card(int card) {
_card = card;
notifyListeners();
}
int _pregunta = 0;
int get pregunta => _pregunta;
seguentPregunta() {
_pregunta++;
}
set pregunta(int pregunta) {
_pregunta = pregunta;
}
seguentCard() {
_card++;
notifyListeners();
}
anteriorCard() {
_card--;
pregunta = pregunta - (preguntesPerCard[card] + preguntesPerCard[card + 1]);
notifyListeners();
}