I have a problem with the listview builder with the radio button. I used two listview builders first one for questions and the second for the answers list but the problem arrived in the second listview builder with a radio button example: I have 3 questions and I select option b answer for question first so all question answers are selected b and after that when I select next question answer c then all question answers select c in the radio button
please help me I shared my code below
class SurveyScreen extends StatefulWidget {
const SurveyScreen({Key? key}) : super(key: key);
@override
_SurveyScreenState createState() => _SurveyScreenState();
}
Future<GetSurveyData> getData() async {
GetSurveyData? getSurveyData;
var response = await http.get(
Uri.https('ehubapi.ascs.link', 'api/GetUserSurveyData'),
headers: {
"content-type": "application/json",
"accept": "application/json",
"Authorization":
"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy9304M2E4LTAwNTA1NjQ3N2Y5YyIsIlhhZlNlY3VyaXR5QXV0aFBhc3NlZCI6IlhhZlNlY3VyaXR5QXV0aFBhc3NlZCIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL25hbWUiOiJtdXN0YWZhLnNhZWVkIiwiWGFmU2VjdXJpdHkiOiJYYWZTZWN1cml0eSIsIlhhZkxvZ29uUGFyYW1zIjoicTFZcUxVNHQ4a3ZNVFZXeVVzb3RMUzVKVEV2VUswNU1UVTFSMGxFcVNDd3VMczh2U2dGS0dRYWFGQnViK0ZRbzFRSUEiLCJleHAiOjE2NjI3ODI3MDV9.OFZho2bs_ZIR_bcPdL9fXHSmWH5_K58E66OV0VDFOvc"
},
);
var jsonResponse = json.decode(response.body);
print(jsonResponse);
getSurveyData = GetSurveyData.fromJson(jsonResponse);
return getSurveyData;
}
class _SurveyScreenState extends State<SurveyScreen> {
var groupValue = -1;
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xff98C337),
body: Stack(
children: [
SingleChildScrollView(
child: Container(
alignment: Alignment.center,
padding: const EdgeInsets.only(
// top: MediaQuery.of(context).size.height * 0.5,
top: 50,
left: 30,
right: 30),
child: Column(
children: [
Align(
alignment: Alignment.centerLeft,
child: IconButton(
onPressed: () {
Navigator.of(context).pop();
},
icon: const Icon(Icons.arrow_back_ios),
),
),
const SizedBox(
height: 30,
),
FutureBuilder(
future: getData(),
builder: (context, AsyncSnapshot<GetSurveyData> snapshot) {
if (snapshot.data != null) {
return Column(
children: [
Align(
alignment: Alignment.centerLeft,
child: Text(
snapshot.data?.data.enabledSurveys[0]
.englishName ??
"",
style: const TextStyle(
color: Colors.black,
fontSize: 25,
fontFamily: 'Josefin Sans',
fontWeight: FontWeight.w700),
)),
const SizedBox(
height: 10,
),
Align(
alignment: Alignment.centerLeft,
child: Text(
snapshot.data?.data.enabledSurveys[0]
.arabicName ??
"",
style: const TextStyle(
color: Colors.black, fontSize: 12),
)),
const SizedBox(
height: 30,
),
SizedBox(
height: 500,
child: Align(
alignment: Alignment.center,
child: ListView.builder(
shrinkWrap: true,
padding: const EdgeInsets.all(0.0),
scrollDirection: Axis.horizontal,
// itemCount: userDashboard.joinedUpcomingClasses.length,
itemCount: snapshot.data?.data
.enabledSurveys[0].questions.length,
itemBuilder:
(BuildContext context, int index) {
return SizedBox(
width: 300,
child: Card(
color: Colors.white,
child: Padding(
padding: const EdgeInsets.all(10),
child: Column(
children: [
Text(
snapshot
.data
?.data
.enabledSurveys[0]
.questions[index]
.englishText ??
'',
style: const TextStyle(
color: Colors.black,
fontSize: 18,
fontFamily:
'Josefin Sans',
fontWeight:
FontWeight.w700),
),
SizedBox(
height: 10,
),
Text(
snapshot
.data
?.data
.enabledSurveys[0]
.questions[index]
.arabicText ??
'',
style: const TextStyle(
color: Colors.grey,
fontSize: 12)),
Builder(builder: (context) {
if (snapshot
.data
?.data
.enabledSurveys[0]
.questions[index]
.answers
.length ==
0) {
return textField();
} else {
return answeringListData(
snapshot
.data
?.data
.enabledSurveys[
0]
.questions[
index]
.answers ??
[]);
}
}),
],
),
)),
);
},
),
),
),
],
);
} else {
return SizedBox(
height: double.infinity,
child: Center(
child: Column(
//Horizontal
crossAxisAlignment: CrossAxisAlignment.center,
//Vertical
mainAxisAlignment: MainAxisAlignment.center,
children: const [
CircularProgressIndicator(),
Text("Loading..."),
],
)),
);
}
}),
const SizedBox(
height: 20,
),
],
),
))
],
),
);
}
Widget answeringListData(List<Answers> answers) {
return SizedBox(
height: 200,
child: ListView.builder(
padding: EdgeInsets.zero,
physics: ClampingScrollPhysics(),
itemCount: answers.length,
itemBuilder: (context, index) => ButtonBar(
buttonPadding: EdgeInsets.zero,
alignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(answers[index].englishText,
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
color: Colors.black)),
Radio(
groupValue: groupValue,
value: index,
onChanged: (newValue) =>
setState(() => groupValue = newValue as int),
),
],
),
),
);
}
Widget textField() {
return const Flexible(
child: Align(
alignment: Alignment.topCenter,
child: TextField(
textAlignVertical: TextAlignVertical.center,
decoration: InputDecoration(
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.grey),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.grey),
),
border: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.grey),
),
hintText: "Please Type Your Answer Here...",
hintStyle: TextStyle(color: Colors.black),
),
style: TextStyle(color: Colors.black),
textInputAction: TextInputAction.next),
),
);
}
}
My Model Class
class GetSurveyData {
GetSurveyData({
required this.message,
required this.data,
});
late final String message;
late final Data data;
GetSurveyData.fromJson(Map<String, dynamic> json){
message = json['message'];
data = Data.fromJson(json['data']);
}
Map<String, dynamic> toJson() {
final _data = <String, dynamic>{};
_data['message'] = message;
_data['data'] = data.toJson();
return _data;
}
}
class Data {
Data({
required this.doneBy,
required this.userName,
this.email,
this.mobileNumber,
required this.fullNameInEnglish,
required this.fullNameInArabic,
required this.enabledSurveys,
required this.shops,
});
late final String doneBy;
late final String userName;
late final Null email;
late final Null mobileNumber;
late final String fullNameInEnglish;
late final String fullNameInArabic;
late final List<EnabledSurveys> enabledSurveys;
late final List<Shops> shops;
Data.fromJson(Map<String, dynamic> json){
doneBy = json['doneBy'];
userName = json['userName'];
email = null;
mobileNumber = null;
fullNameInEnglish = json['fullNameInEnglish'];
fullNameInArabic = json['fullNameInArabic'];
enabledSurveys = List.from(json['enabledSurveys']).map((e)=>EnabledSurveys.fromJson(e)).toList();
shops = List.from(json['shops']).map((e)=>Shops.fromJson(e)).toList();
}
Map<String, dynamic> toJson() {
final _data = <String, dynamic>{};
_data['doneBy'] = doneBy;
_data['userName'] = userName;
_data['email'] = email;
_data['mobileNumber'] = mobileNumber;
_data['fullNameInEnglish'] = fullNameInEnglish;
_data['fullNameInArabic'] = fullNameInArabic;
_data['enabledSurveys'] = enabledSurveys.map((e)=>e.toJson()).toList();
_data['shops'] = shops.map((e)=>e.toJson()).toList();
return _data;
}
}
class EnabledSurveys {
EnabledSurveys({
required this.referenceTemplate,
required this.englishName,
required this.arabicName,
required this.sequence,
required this.questions,
});
late final String referenceTemplate;
late final String englishName;
late final String arabicName;
late final int sequence;
late final List<Questions> questions;
EnabledSurveys.fromJson(Map<String, dynamic> json){
referenceTemplate = json['referenceTemplate'];
englishName = json['englishName'];
arabicName = json['arabicName'];
sequence = json['sequence'];
questions = List.from(json['questions']).map((e)=>Questions.fromJson(e)).toList();
}
Map<String, dynamic> toJson() {
final _data = <String, dynamic>{};
_data['referenceTemplate'] = referenceTemplate;
_data['englishName'] = englishName;
_data['arabicName'] = arabicName;
_data['sequence'] = sequence;
_data['questions'] = questions.map((e)=>e.toJson()).toList();
return _data;
}
}
class Questions {
Questions({
required this.referenceQuestion,
required this.englishText,
required this.arabicText,
required this.questionType,
required this.required,
required this.evidencePhotoRequired,
required this.sequence,
required this.answers,
});
late final String referenceQuestion;
late final String englishText;
late final String arabicText;
late final String questionType;
late final bool required;
late final bool evidencePhotoRequired;
late final int sequence;
late final List<Answers> answers;
Questions.fromJson(Map<String, dynamic> json){
referenceQuestion = json['referenceQuestion'];
englishText = json['englishText'];
arabicText = json['arabicText'];
questionType = json['questionType'];
required = json['required'];
evidencePhotoRequired = json['evidencePhotoRequired'];
sequence = json['sequence'];
answers = List.from(json['answers']).map((e)=>Answers.fromJson(e)).toList();
}
Map<String, dynamic> toJson() {
final _data = <String, dynamic>{};
_data['referenceQuestion'] = referenceQuestion;
_data['englishText'] = englishText;
_data['arabicText'] = arabicText;
_data['questionType'] = questionType;
_data['required'] = required;
_data['evidencePhotoRequired'] = evidencePhotoRequired;
_data['sequence'] = sequence;
_data['answers'] = answers.map((e)=>e.toJson()).toList();
return _data;
}
}
class Answers {
Answers({
required this.referenceAnswer,
required this.englishText,
required this.arabicText,
required this.evidencePhotoRequired,
required this.commentRequired,
required this.sequence,
});
late final String referenceAnswer;
late final String englishText;
late final String arabicText;
late final bool evidencePhotoRequired;
late final bool commentRequired;
late final int sequence;
Answers.fromJson(Map<String, dynamic> json){
referenceAnswer = json['referenceAnswer'];
englishText = json['englishText'];
arabicText = json['arabicText'];
evidencePhotoRequired = json['evidencePhotoRequired'];
commentRequired = json['commentRequired'];
sequence = json['sequence'];
}
Map<String, dynamic> toJson() {
final _data = <String, dynamic>{};
_data['referenceAnswer'] = referenceAnswer;
_data['englishText'] = englishText;
_data['arabicText'] = arabicText;
_data['evidencePhotoRequired'] = evidencePhotoRequired;
_data['commentRequired'] = commentRequired;
_data['sequence'] = sequence;
return _data;
}
}
class Shops {
Shops({
required this.id,
required this.title,
required this.nameInEnglish,
required this.nameInArabic,
required this.addressInEnglish,
required this.addressInArabic,
required this.contactNumber,
required this.latitude,
required this.longitude,
required this.channel,
});
late final String id;
late final String title;
late final String nameInEnglish;
late final String nameInArabic;
late final String addressInEnglish;
late final String addressInArabic;
late final String contactNumber;
late final double latitude;
late final double longitude;
late final String channel;
Shops.fromJson(Map<String, dynamic> json){
id = json['id'];
title = json['title'];
nameInEnglish = json['nameInEnglish'];
nameInArabic = json['nameInArabic'];
addressInEnglish = json['addressInEnglish'];
addressInArabic = json['addressInArabic'];
contactNumber = json['contactNumber'];
latitude = json['latitude'];
longitude = json['longitude'];
channel = json['channel'];
}
Map<String, dynamic> toJson() {
final _data = <String, dynamic>{};
_data['id'] = id;
_data['title'] = title;
_data['nameInEnglish'] = nameInEnglish;
_data['nameInArabic'] = nameInArabic;
_data['addressInEnglish'] = addressInEnglish;
_data['addressInArabic'] = addressInArabic;
_data['contactNumber'] = contactNumber;
_data['latitude'] = latitude;
_data['longitude'] = longitude;
_data['channel'] = channel;
return _data;
}
}
CodePudding user response:
The problem is that you're using one state, groupValue
for all the answers for all questions. To solve it split groupValue
between the questions. A Map<int, int>
would do that where the key is the question index and the value is the answer index. It would be like the following:
Pass the question index to answeringListData
and set the map accordingly.
Widget answeringListData(int questionIndex, List<Answers> answers) {
return SizedBox(
height: 200,
child: ListView.builder(
padding: EdgeInsets.zero,
physics: ClampingScrollPhysics(),
itemCount: answers.length,
itemBuilder: (context, index) => ButtonBar(
buttonPadding: EdgeInsets.zero,
alignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(answers[index].englishText,
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
color: Colors.black)),
Radio(
groupValue: groupValue[questionIndex],
value: index,
onChanged: (newValue) =>
setState(() => groupValue[questionIndex] = newValue!),
),
],
),
),
);
}
The call site is going to be like this:
answeringListData(index, snapshot.data?.data.enabledSurveys[0]
.questions[index].answers ??[]);
And here is the groupValue
map declaration from just int
to a Map
:
final groupValue = <int, int>{};
Here's the result:
CodePudding user response:
This answer is base on you model.
First change your question model to this:
class Questions {
Questions({
required this.referenceQuestion,
required this.englishText,
required this.arabicText,
required this.questionType,
required this.required,
required this.evidencePhotoRequired,
required this.sequence,
required this.answers,
required this.groupValue,
});
late final String referenceQuestion;
late final String englishText;
late final String arabicText;
late final String questionType;
late final bool required;
late final bool evidencePhotoRequired;
late final int sequence;
late final List<Answers> answers;
late final int groupValue;
Questions.fromJson(Map<String, dynamic> json) {
referenceQuestion = json['referenceQuestion'];
englishText = json['englishText'];
arabicText = json['arabicText'];
questionType = json['questionType'];
required = json['required'];
evidencePhotoRequired = json['evidencePhotoRequired'];
sequence = json['sequence'];
groupValue = -1;
answers =
List.from(json['answers']).map((e) => Answers.fromJson(e)).toList();
}
Map<String, dynamic> toJson() {
final _data = <String, dynamic>{};
_data['referenceQuestion'] = referenceQuestion;
_data['englishText'] = englishText;
_data['arabicText'] = arabicText;
_data['questionType'] = questionType;
_data['required'] = required;
_data['evidencePhotoRequired'] = evidencePhotoRequired;
_data['sequence'] = sequence;
// _data['groupValue'] = groupValue;
_data['answers'] = answers.map((e) => e.toJson()).toList();
return _data;
}
}
then change your answeringListData
to this, as you can see I use StatefulBuilder, in this way your hole code wont rebuild and every time your api wont call:
Widget answeringListData(
List<Answers> answers, int questionIndex, List<Questions>? questions) {
return SizedBox(
height: 200,
child: StatefulBuilder(builder: (context, innerSetState) {
return ListView.builder(
padding: EdgeInsets.zero,
physics: ClampingScrollPhysics(),
itemCount: answers.length,
itemBuilder: (context, index) => ButtonBar(
buttonPadding: EdgeInsets.zero,
alignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(answers[index].englishText,
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
color: Colors.black)),
Radio(
groupValue: questions![questionIndex].groupValue,
value: index,
onChanged: (newValue) => innerSetState(() {
var oldQuestions = questions![questionIndex];
var newQuestion = Questions(
referenceQuestion: oldQuestions.referenceQuestion,
englishText: oldQuestions.englishText,
arabicText: oldQuestions.arabicText,
questionType: oldQuestions.questionType,
required: oldQuestions.required,
evidencePhotoRequired: oldQuestions.evidencePhotoRequired,
sequence: oldQuestions.sequence,
answers: oldQuestions.answers,
groupValue: newValue as int,
);
questions[questionIndex] = newQuestion;
}),
),
],
),
);
}),
);
}
then pass answeringListData
like this in your main widget :
...
} else {
return answeringListData(
snapshot.data?.data.enabledSurveys[0].questions[index].anwers ?? [],
index,
snapshot.data?.data.enabledSurveys[0].questions);
}