Highlighting two radio buttons at once - flutter

I'm trying to build an app in flutter in which during quiz, I'm using radio buttons. I want to highlight the correct answer and the answer selected by the user if the correct answer is not selected by the user.
If the correct answer is selected then I just want to select the user selected answer.
I cannot find any way to do it.
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
widget.content.getQuestion(),
style: Constants.articleQuestionStyle,
),
Container(),
Column(
children: widget.content
.getOptions()
.map<Widget>((value) => _buildRadioBtn(value))
.toList()),
//
// {
// return Row(children: [
// Radio(
// value: value,
// groupValue: widget.content.getGuess(),
// onChanged: (val){
// print("value: ${value}");
// print("isChecked: ${widget.content.isChecked()}");
// return //_buildRadioBtn(val);
//// widget.content.isChecked()
//// ? null :
// _buildRadioBtn(val);//_handleValueChanged(val);
// },
// activeColor: (widget.content.getGuess() == widget.content.getCorrectAnswer())? Colors.orange: Colors.red,
// ),
//
// Text(
// value,
// style: Constants.articleBodyTextStyle,
// )
// ]);
// }
// ).toList()),
and
_buildRadioBtn(value) {
// bool isCorrect = widget.content.getCorrectAnswer().contains(value);
// bool isChosen = widget.content.getGuess().contains(value);
return Row(
children: <Widget>[
Radio(
value: widget.content.isChecked(),
groupValue: widget.content.getGuess(),
onChanged: (value){
if(!widget.content.isChecked()) {
// print("ffffff");
// widget.content.registerGuess(value);
// print("abc");
// setState(() {});
_handleValueChanged(value);
}
},
activeColor: (
widget.content.getGuess() == widget.content.getCorrectAnswer())? Colors.orange: Colors.red,
),
Text(
// "hello",
value,
style: Constants.articleBodyTextStyle,
)
],
);
}
}
The way I think it will work is to rebuild the radio button once the user selects the answer, but I cannot do so. Please help.

Method: 1
String question = 'Q 1', answer = 'A 3', defaultValue = 'nil';
List<String> options = ['A 1', 'A 2', 'A 3', 'A 4'], info = ['', '', '', ''];
List<Color> bgs = [Colors.white, Colors.white, Colors.white, Colors.white];
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: Column(
children: <Widget>[
ListTile(title: Text(question)),
ListView.builder(
shrinkWrap: true,
itemCount: options.length,
itemBuilder: (cc, ii) {
return Card(
color: bgs[ii],
child: ListTile(
title: Text(options[ii]),
subtitle: Text(info[ii]),
leading: Radio(
value: options[ii],
groupValue: defaultValue,
onChanged: (String value) {
setState(() {
defaultValue = value;
});
},
),
),
);
}),
RaisedButton(
onPressed: () {
if (defaultValue == answer) {
setState(() {
int ind = options.indexOf(defaultValue);
bgs[ind] = Colors.green;
info[ind] = 'Correct Answer !';
});
} else {
setState(() {
int wrongInd = options.indexOf(defaultValue);
bgs[wrongInd] = Colors.redAccent;
info[wrongInd] = 'Wrong Answer !';
int correctInd = options.indexOf(answer);
bgs[correctInd] = Colors.green;
info[correctInd] = 'Correct Answer !';
});
}
},
child: Text('Submit'))
],
),
),
);
}
Method: 2
String question = 'Q 1', answer = 'A 3', defaultValue = 'nil';
List<String> options = ['A 1', 'A 2', 'A 3', 'A 4'], info = ['', '', '', ''],radioValues=[];
List<Color> bgs = [Colors.black, Colors.black, Colors.black, Colors.black];
#override
void initState(){
super.initState();
radioValues.addAll(options);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: Column(
children: <Widget>[
ListTile(title: Text(question)),
ListView.builder(
shrinkWrap: true,
itemCount: options.length,
itemBuilder: (cc, ii) {
return ListTile(
title: Text(options[ii],
style:TextStyle(color:bgs[ii])),
subtitle: Text(info[ii],
style:TextStyle(color:bgs[ii])),
leading: Radio(
value: radioValues[ii],
groupValue: defaultValue,
onChanged: (String value) {
setState(() {
defaultValue = value;
});
},
),
);
}),
RaisedButton(
onPressed: () {
if (defaultValue == answer) {
setState(() {
int ind = options.indexOf(defaultValue);
bgs[ind] = Colors.green;
info[ind] = 'Correct Answer !';
});
} else {
setState(() {
int wrongInd = options.indexOf(defaultValue);
bgs[wrongInd] = Colors.redAccent;
info[wrongInd] = 'Wrong Answer !';
int correctInd = options.indexOf(answer);
bgs[correctInd] = Colors.green;
info[correctInd] = 'Correct Answer !';
radioValues[wrongInd] = defaultValue;
radioValues[correctInd] = defaultValue;
});
}
},
child: Text('Submit'))
],
),
),
);
}

I suggest that you leave the select state of what the user picked. What you can do is change the colors or styling of the text of the items to reflect which the user picked vs which is the correct answer.

Related

RangeError (index): Invalid value: Not in inclusive range 0..3: 4 when i try to retrieve all list items from firestore

I've been working on a project in part of it I needed to bring a list of data and do some filtering on it, some of those filters are just working fine but I've been facing that problem where the part of getting all the data when I press the button all retrieve all the items of the list and show them into listview.builder() with different Card shapes based on grouping similar data i.e a card designed for data.type[tests] & another card designed for data.type[offers] ..etc.
So when I press all button it shows only the first 4 items inside the listview + it doesn't show data in the card design that supposed to have base on it's group filtering.
here I'm getting the data from firestore
import 'package:cloud_firestore/cloud_firestore.dart';
class Test{
final String details;
final String name;
final String price;
final String type;
Test({
this.details,
this.name,
this.price,
this.type,
});
factory Test.fromDocument(DocumentSnapshot doc){
return Test(
details: doc.data()['details'],
name: doc.data()['name'],
price: doc.data()['price'],
type: doc.data()['type'],
);
}
}
..........................
import 'package:ilab/services/User.dart';
class Services {
final _db = FirebaseFirestore.instance.collection('tests');
// test list from snapshot
List<Test> _testsListFromSnapshot(QuerySnapshot snapshot) {
return snapshot.docs.map((doc) {
return Test(
details: doc.data()['details'] ?? '',
name: doc.data()['name'] ?? '',
price: doc.data()['price'] ?? '',
type: doc.data()['type'] ?? '');
}).toList();
}
// Get tests stream
Stream<List<Test>> get Tests {
return _db.snapshots().map(_testsListFromSnapshot);
}
my component starts here
List<String> alphabets = [
'all',
'a',
'b',
'c',
'd',
... etc
]
List<Test> filteredTests = List();
List<Test> tests = List();
Color color = KWhiteColor;
int Index;
#override
void initState() {
super.initState();
filteredTests = tests;
}
here is the code of giving a card desgin based on the type of data
// return different cardshape for different group of data
Widget _card(int index) {
if (filteredTests
.where((user) => user.type.toLowerCase().contains('باقة'))
.toList()
.isNotEmpty) {
return PackageCardDesign(
packageName: filteredTests[index].name,
price: '${filteredTests[index].price} YR',
details: filteredTests[index].details.toLowerCase(),
colour: Packgecolors[index],
icon: Icons.ac_unit_outlined,
type: filteredTests[index].type,
);
} else if (filteredTests
.where((user) => user.type.toLowerCase().contains('تحليل'))
.toList()
.isNotEmpty) {
return TestCardDesign(
colour: TestOffercolors[index],
testName: filteredTests[index].name,
details: filteredTests[index].details.toLowerCase(),
price: '${filteredTests[index].price} YR',
type: filteredTests[index].type,
);
} else if (filteredTests
.where((user) => user.type.toLowerCase().contains('عرض'))
.toList()
.isNotEmpty) {
return OfferCardDesign(
colour: TestOffercolors[index],
testName: filteredTests[index].name,
// details: filteredUsers[index].details.toLowerCase(),
price: '${filteredTests[index].price} %',
// type: filteredUsers[index].type,
);
}
}
here is the code of creating and printing the top three buttons
ReusableTestChip mainThreeButtonChip(
{#required String text, String buttonName, Function onTap}) {
return ReusableTestChip(
ontap: onTap,
cardChild: Text(
text,
textAlign: TextAlign.center,
style: TextStyle(
color: selectedButton == buttonName ? KWhiteColor : KInActiveColor,
fontSize: 18.0, //25.0,
fontFamily: 'Cairo-Italic',
fontWeight: FontWeight.w600,
),
),
colour: selectedButton == buttonName ? KInActiveColor : KWhiteColor,
);
}
// print Main Three Top Button method using for loop to iterate through loop of strings
List<ReusableTestChip> printMainThreeButtonMethod() {
List<ReusableTestChip> allButtons = [];
for (int i = 0; i < buttons.length; i++) {
String button = buttons[i];
var newItem = mainThreeButtonChip(
text: button,
onTap: () {
setState(() {
selectedButton = buttons[i];
if (buttons[i] == 'تحاليل') {
// setState(() {
// _card = offerList();
// });
filteredTests = tests
.where((u) => (u.type.toLowerCase().contains('تحليل')))
.toList();
} else if (buttons[i] == 'عروض') {
filteredTests = tests
.where((u) => (u.type.toLowerCase().contains('عرض')))
.toList();
} else if (buttons[i] == 'باقات') {
filteredTests = tests
.where((u) => (u.type.toLowerCase().contains('باقة')))
.toList();
}
});
},
buttonName: buttons[i],
);
allButtons.add(newItem);
}
return allButtons;
}
here is the code of creating and printing the all button
ReusableAlphabetChip alphabetChip(
{#required String text, String char, Function onTap}) {
return ReusableAlphabetChip(
ontap: onTap,
cardChild: Text(
text,
textAlign: TextAlign.center,
style: TextStyle(
color: selectedAlphabet == char ? KInActiveColor : KSecondaryColor,
fontSize: 18.0, //25.0,
fontFamily: 'Cairo-Italic',
fontWeight: FontWeight.w600,
),
),
colour: selectedAlphabet == char ? KWhiteColor : KInActiveColor,
);
}
// print all button
List<ReusableAlphabetChip> printAlphabetMethod() {
List<ReusableAlphabetChip> chars = [];
for (int i = 0; i < alphabets.length; i++) {
String char = alphabets[i];
var newItem = alphabetChip(
text: char,
onTap: () {
setState(() {
selectedAlphabet = alphabets[i];
if (alphabets[i] == 'الكل') {
filteredTests = tests;
// _foundUsers = _allUsers;
} else {
filteredTests = tests
.where((u) => (u.name.toLowerCase().startsWith(alphabets[i])))
.toList(); //json filter first filter && firebase second filter
// _foundUsers = _allUsers.where((u) => (u["name"].toLowerCase().startsWith(alphabets[i]))).toList();
}
});
},
char: alphabets[i],
);
chars.add(newItem);
}
return chars;
}
#override
Widget build(BuildContext context) {
tests = Provider.of<List<Test>>(context);
ScrollController scrollController = ScrollController(
initialScrollOffset: 10, // or whatever offset you wish
keepScrollOffset: true,
);
return SafeArea(
child: Scaffold(
appBar: AppBar(
toolbarHeight: 100,
title: Image.asset('images/logo.jpeg',
height: 100.0, alignment: Alignment.center),
),
drawer: AppDrawer(),
body: ListView(
shrinkWrap: true,
children: [
// applogo(),
Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(height: 10.0),
Row(
// top filters
mainAxisAlignment: MainAxisAlignment.center,
children: printMainThreeButtonMethod(),
),
Container(
// get all list items
margin: EdgeInsets.symmetric(vertical: 4.0),
height: 50.0,
child: ListView(
scrollDirection: Axis.horizontal,
shrinkWrap: true,
children: printAlphabetMethod()),
),
SizedBox(
height: 390,
child: Column(
children: [
Expanded(
child: ListView.builder(
shrinkWrap: true,
padding: EdgeInsets.all(10.0),
controller: scrollController,
scrollDirection: Axis.vertical,
itemCount: filteredUsers.length,
itemBuilder: (BuildContext context, int index) {
Index = index;
if (index < filteredTests.length) {
return Card(
child: Padding(
padding: EdgeInsets.all(10.0),
child:_card(Index)
),
);
} else {
return Center(child: CircularProgressIndicator());
}
},
// itemCount: filteredUsers.length + 1,
),
),
],
),
),
],
),
],
),
bottomNavigationBar: MyBottomBar(),
),
);
}
I hope I explained what I'm facing clearly, any help will be appreciated and thanks in advance.
I found out that the problem was in color's list. where I made a list of colors to allow ListView.builder prints Cards of data that will retrieve with different colors, it turns out that due to color's list is finite and when the ListView.builder reaches the end of it, it returns that error [RangeError (index): Invalid value: Not in inclusive range 0..3: 4], So I've made a change on my color's list so when it reaches the end of the list it start over from the beginning and printing new data using the specified colors, like this
Color selectedColour(index) {
Color c;
if (index % 4 == 0) c = Colors.cyan;
if (index % 4 == 1) c = Colors.blueGrey;
if (index % 4 == 2) c = Colors.blue;
if (index % 4 == 3) c = Color(0xFFea9999);
return c;
}
this is my previous color's list before changing it
var Paccolors = [
Colors.blue,
Colors.cyan,
Colors.blueGrey,
Colors.pink,
Colors.black45,
Colors.lightGreen,
Colors.green,
Colors.red
];

Create dynamic radio buttons in Flutter

I am trying to create a dynamic form which contains some textbox and radio button. I am using RadioListTile for the same.
In the below code you can see I am using var nameController = TextEditingController(); to get the value of textbox. I am not sure what can be used for RadioListTile.
I am also struggling to show Radio Button Dynamically. I have added full code in the below. How can I get the radio button working and get the value of the selected items, so they can be saved to the database?
class Price extends StatefulWidget {
#override
_PriceState createState() => _PriceState();
}
class FruitsList {
String name;
int index;
FruitsList({this.name, this.index});
}
class _PriceState extends State<Price> {
static final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final _scaffoldKey = GlobalKey<ScaffoldState>();
int currentIndex = 0;
String person;
String age;
String job;
// Default Radio Button Item
String radioItem = 'Mango';
// Group Value for Radio Button.
int id = 1;
List<FruitsList> fList = [
FruitsList(
index: 1,
name: "Mango",
),
FruitsList(
index: 2,
name: "Banana",
),
FruitsList(
index: 3,
name: "Apple",
),
FruitsList(
index: 4,
name: "Cherry",
),
];
#override
void initState() {
super.initState();
cards.add(createCard());
}
var nameTECs = <TextEditingController>[];
var ageTECs = <TextEditingController>[];
var jobTECs = <TextEditingController>[];
--- Need to help to add Controller for Radio Button ---
var cards = <Card>[];
Card createCard() {
var nameController = TextEditingController();
var ageController = TextEditingController();
var jobController = TextEditingController();
nameTECs.add(nameController);
ageTECs.add(ageController);
jobTECs.add(jobController);
return Card(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text('Service ${cards.length + 1}'),
TextFormField(
style: TextStyle(color: Colors.blue),
controller: nameController,
decoration: InputDecoration(labelText: 'Name'),
validator: validatetext,
onSaved: (String val) {
person = val;
},
),
TextFormField(
style: TextStyle(color: Colors.blue),
controller: ageController,
decoration: InputDecoration(labelText: 'age'),
validator: validatetext,
onSaved: (String val) {
age = val;
},
),
TextFormField(
style: TextStyle(color: Colors.blue),
controller: jobController,
decoration: InputDecoration(labelText: 'Job'),
validator: validatetext,
onSaved: (String val) {
job = val;
},
),
//Expanded(
// child: Container(
// height: 350.0,
// child:
Row(
children:
fList.map((data) => RadioListTile(
title: Text("${data.name}"),
groupValue: id,
value: data.index,
onChanged: (val) {
setState(() {
radioItem = data.name ;
id = data.index;
});
},
)).toList(),
),
//)),
/* CheckboxListTile(
title: Text("title text"),
value: checkedValue,
onChanged: (newValue) {
setState(() {
checkedValue = newValue;
});
},
//onChanged: (newValue) { ... },
controlAffinity: ListTileControlAffinity.leading, // <-- leading Checkbox
), */
SizedBox(height: 10),
],
),
// ),
);
}
void _validateInputs() {
print('button');
if (_formKey.currentState.validate()) {
_formKey.currentState.save();
_onDone();
} else {
}
}
_onDone() {
updateProfile();
List<PersonEntry> entries = [];
for (int i = 0; i < cards.length; i++) {
var name = nameTECs[i].text;
var age = ageTECs[i].text;
var job = jobTECs[i].text;
entries.add(PersonEntry(name, age, job));
}
}
///////// Save to DB ////////////////////
Future updateProfile() async{
try{
for (int i = 0; i < cards.length; i++) {
var name = nameTECs[i].text;
var age = ageTECs[i].text;
var job = jobTECs[i].text;
Map<String, dynamic> body = {'name': name, 'age': age, 'job' : job };
print(body);
nameTECs[i].clear();
//if(rang == true){
Response response =
await Dio().post("http://192.168.1.102:8080/adddetails.php", data: body);
print(response.statusCode);
if(response.statusCode == 404){
print('404');
}
if(response.statusCode == 200){
nameTECs[i].clear();
}
}
} catch (e) {
print("Exception Caught: $e");
}
}
///////////////////////////////
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
appBar: myAppBar(),
endDrawer: myDrawer(),
body: Column(
children: <Widget>[
Expanded(
child:new Form(
key: _formKey,
child: ListView.builder(
itemCount: cards.length,
itemBuilder: (BuildContext context, int index) {
return cards[index];
},
),
),
),
Container(
padding: EdgeInsets.symmetric(horizontal: 2.0),
color: Colors.grey,
child:Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// Container(
Padding(
padding: const EdgeInsets.all(16.0),
child: FloatingActionButton(
heroTag: "btn1",
child: Icon(Icons.add),
onPressed: () => setState(() => cards.add(createCard())),
backgroundColor: Colors.green,
)
/*RaisedButton(
child: Text('Add new'),
onPressed: () => setState(() => cards.add(createCard())),
),*/
),
Padding(
padding: const EdgeInsets.all(16.0),
child: FloatingActionButton(
heroTag: "btn2",
child: Icon(Icons.remove), onPressed: () => setState(() => cards.removeLast()),
backgroundColor: Colors.red,
)
),
Padding(
padding: const EdgeInsets.all(16.0),
child: FloatingActionButton(
heroTag: "btn3",
child: Icon(Icons.save), onPressed: _validateInputs),
)
],
),
),
],
),
);
}
);
}
}
class PersonEntry {
final String name;
final String age;
final String studyJob;
PersonEntry(this.name, this.age, this.studyJob);
#override
String toString() {
return 'Person: name= $name, age= $age, study job= $studyJob';
}
}
Size get preferredSize => Size.fromHeight(kToolbarHeight);
String validatetext(String value) {
if (value.length < 5)
return 'More than 5 char is required';
else
return null;
}
Update
I want to show Radio buttons that user can select and once user submit the form I can get those value for http request. As you can I have added options to add or remove cards. So, these radio buttons will also generated.
Create field int _selectedRadioIndex
and change code
fList.map((data) => RadioListTile(
title: Text("${data.name}"),
groupValue: id,
value: data.index,
onChanged: (val) {
setState(() {
radioItem = data.name ;
id = data.index;
});
},
)).toList(),
to
fList.map((data) => RadioListTile(
title: Text("${data.name}"),
groupValue: id,
value: data.index,
onChanged: (val) {
setState(() {
radioItem = data.name ;
id = data.index;
_selectedRadioIndex = val;
});
},
)).toList(),
then in code just get it fList.firstWhere((element) => element.index == _selectedRadioIndex)

In Flutter Unable to change the state of DropDown Menu in ListView

I'm trying to change the state of DropDown using setState, value is changing but it is not reflecting on UI, it's only reflecting on the new widget when I'm adding a new Widget.
App Function
Initially, it's a blank screen
When I click on Add it will add dropdown menu & text field
Similarly, we can add many. Those widgets will be added to _mypets list
When I click on save I'm printing an array of lists
How can I change the state?
This is a Stateful Widget
Please help me to resolve this issue
class _MyPetNameState extends State<MyPetName> {
var locationArray = [];
var _myPets = List<Widget>();
String sampleData = 'Hello';
int _index = 1;
var dataForm;
String partnerName;
List<_dropListItem> _weekItems = [
_dropListItem(1, "Pet Type 1"),
_dropListItem(2, "Pet Type 2"),
_dropListItem(3, "Pet Type 3"),
_dropListItem(3, "Pet Type 4"),
];
List<DropdownMenuItem<_dropListItem>> _weekMenuItems;
_dropListItem _selectedWeekItem;
List<DropdownMenuItem<_dropListItem>> buildDropDownMenuItems(List listItems) {
List<DropdownMenuItem<_dropListItem>> items = List();
for (_dropListItem listItem in listItems) {
items.add(
DropdownMenuItem(
child: Text(listItem.name),
value: listItem,
),
);
}
return items;
}
void _addLocation() {
Map<String, String> _formData= {};
int keyValue = _index;
_myPets = List.from(_myPets)
..add(Column(
key: Key("${keyValue}"),
children: <Widget>[
Container(
padding: EdgeInsets.all(20.0),
child: DropdownButton<_dropListItem>(
value: _selectedWeekItem,
items: _weekMenuItems,
onChanged: (value) {
_formData['location'] = value.name;
setState(() {
_weekMenuItems = buildDropDownMenuItems(_weekItems);
_selectedWeekItem = value;
});
}),
),
Container(
child: TextFormField(
initialValue: '',
onChanged: (val) {
_formData['locationType'] = val;
setState(() {
sampleData = val;
});
},
),
),
],
));
setState(() => ++_index);
locationArray.add(_formData);
}
void _sub(int _deleteIndex){
setState(() {
_myPets = List.of(_myPets)..removeAt(_deleteIndex - 1);
--_index;
});
}
#override
void initState() {
// TODO: implement initState
super.initState();
_weekMenuItems = buildDropDownMenuItems(_weekItems);
}
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () {
print('');
print(locationArray);
setState(() {
dataForm = [];
});
},
child: Text('Save'),
),
appBar: AppBar(
title: Text('Add your pets'),
actions: <Widget>[
FlatButton(
child: Text('Add'),
onPressed: (){
_addLocation();
},
),
],
),
body: Column(
children: [
Expanded(
child: ListView(
children: _myPets,
),
),
],
),
);
}
}
class _dropListItem {
int value;
String name;
_dropListItem(this.value, this.name);
}
I have made some changes in your code
This may help you
class _MyPetNameState extends State<MyPetName> {
List<dynamic> radioval = [];
setSelectedRadio(int val, int idx) {
setState(() {
radioval[idx]["value"] = val;
});
}
Widget _radioui(int keyValue) {
return Column(
children: <Widget>[
ButtonBar(
alignment: MainAxisAlignment.center,
children: <Widget>[
Radio(
value: 0,
groupValue: radioval[keyValue]["value"],
activeColor: Colors.green,
onChanged: (val) {
print("Radio $val");
setSelectedRadio(val, keyValue);
},
),
Radio(
value: 1,
groupValue: radioval[keyValue]["value"],
activeColor: Colors.blue,
onChanged: (val) {
print("Radio $val");
setSelectedRadio(val, keyValue);
},
),
],
)
],
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
radioval.add({"name": "pet1", "value": 0});
});
},
child: Icon(Icons.add),
),
appBar: AppBar(
title: Text('Add your pets'),
),
body: Column(
children: [
Expanded(
child: ListView(
children: [for (int i = 0; i < radioval.length; i++) _radioui(i)],
),
),
],
),
);
}
}

StatefulWidget - FLutter

I need to edit this code, in a way to define only one variable widget which can be able to change on every state to a different widget type.
I need to be able to make a dynamic form no matter what the question and its type is, the way i handle it is somehow complex and not efficient.
so is there any idea on how to change the same variable for different widget on every setState()
`Column(
children: <Widget>[
questionText,
textCounter > 0 ? textField : SizedBox(),
selectCounter > 0 ? selectField : SizedBox()
],
)),`FutureBuilder(
future: fetchQuestions(),
builder: (context, snapshot) {
if (snapshot.hasData) {
for (var i = 0; i < snapshot.data.length; i++) {
var temp = snapshot.data[i]['question_value'].toString();
var type = snapshot.data[i]['question_type'].toString();
questionsList.add(temp);
typeList.add(type);
}
return Align(
alignment: Alignment.bottomRight,
child: RaisedButton(
onPressed: () {
changeQuest(questionsList, typeList,
snapshot.data.length, snapshot.data);
},
child: Text('next'),
),
);
} else
return Center(child: CircularProgressIndicator());
},
),
changeQuest(List questions, List type, length, data) {
setState(() {
textCounter = 0;
selectCounter = 0;
integerCounter = 0;
if (counter < length) {
questionText = Text(questions[counter]);
if (type[counter] == 'Integer') {
textCounter++;
textField = TextFormField(
decoration: new InputDecoration(labelText: "Enter your number"),
keyboardType: TextInputType.number,
inputFormatters: <TextInputFormatter>[
WhitelistingTextInputFormatter.digitsOnly
], // Only numbers can be entered
);
} else if (type[counter] == 'Text') {
textCounter++;
textField = TextFormField(
decoration: new InputDecoration(labelText: "Enter a text"),
keyboardType: TextInputType.text,
);
} else if (type[counter] == 'Select') {
selectCounter++;
for (var i = 0; i < data[counter]['answers'].length; i++) {
answersList
.add(data[counter]['answers'][i]['answer_value'].toString());
}
dropDownValue = answersList[0];
selectField = DropdownButton<String>(
value: dropDownValue,
icon: Icon(Icons.arrow_downward),
iconSize: 24,
elevation: 16,
style: TextStyle(color: Colors.deepPurple),
underline: Container(
height: 2,
color: Colors.deepPurpleAccent,
),
onChanged: (value) {
setState(() {
dropDownValue = value;
});
},
items: answersList
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
);
print (dropDownValue);
}
}
counter++;
});
}
as #proversion said in the comments, you can check in the widget tree, if a condition returns true or false.
Before you enter the child you could check with an inline if-statement like so:
questionType == 'dropdown' ? (Widget for True) : (Widget for False)
Or if you have to do a complex check, I would do this in the build Method before the return of the widget and set a boolean value there, which represents your check result.
Then you can use this value (example: isTrue) in the widget tree like isTure ? (Widget for True) : (Widget for False).
Here is a sample code, that should work.
import 'package:flutter/material.dart';
class WidgetWithDifferentChildren extends StatefulWidget {
#override
_WidgetWithDifferentChildrenState createState() =>
_WidgetWithDifferentChildrenState();
}
class _WidgetWithDifferentChildrenState
extends State<WidgetWithDifferentChildren> {
String questionType = '';
String dropdownValue = 'SelectItem';
String textValue = '';
TextEditingController txtCtrl = TextEditingController();
#override
void dispose() {
// TODO: implement dispose when using TextEditingController
txtCtrl.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Container(
child: questionType == ''
? Text('no Question Type')
: questionType == 'dropdown'
? DropdownButton<String>(
value: dropdownValue,
onChanged: (String newValue) {
// Do something with the new Value
print('New DropDown value = $newValue');
setState(() {
dropdownValue = newValue;
});
},
items: <String>[
'SelectItem',
'Item 1',
'Item 2',
'Item 3',
].map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: new Text(value),
);
}).toList(),
)
: questionType == 'textfield'
? TextFormField(
controller: txtCtrl,
onChanged: (value) {
// Do something with the new Value
print('New TextField value = $value');
setState(() {
textValue = value;
});
},
)
: Text('Question Type does not match'),
);
}
}
UPDATE
acc. to your provided code, you may want to check the following. I created a separate class which will return the right widget for the question. Just pass the type and additional the dropDownList to the function.
General I would suggest to store the questions and the corresponding answers in the same array, this would be a easy call of the function like getInputWidget(type:question[i].type, dropDownList:question[i].dropDownList).
Source Code for above example
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class WidgetWithDifferentChildren extends StatefulWidget {
#override
_WidgetWithDifferentChildrenState createState() =>
_WidgetWithDifferentChildrenState();
}
class _WidgetWithDifferentChildrenState
extends State<WidgetWithDifferentChildren> {
String questionType = '';
String inputValue = '';
List<String> answers = [];
int questionID = 0;
TextEditingController txtCtrl = TextEditingController();
List<Map<String, String>> questionList = [
{'question_value': 'text question ', 'question_type': 'text'},
{'question_value': 'number question ', 'question_type': 'number'},
{'question_value': 'select question ', 'question_type': 'select'},
{'question_value': 'last question ', 'question_type': 'text'},
];
List<String> dropDownList = [
'Select an Item',
'Answer A',
'Answer B',
'Answer C',
];
#override
void dispose() {
// TODO: implement dispose when using TextEditingController
txtCtrl.dispose();
super.dispose();
}
Widget getInputWidget({#required String type, List<String> dropDownList}) {
Widget inputW;
if (type == 'number' || type == 'text') {
inputW = TextFormField(
controller: txtCtrl,
decoration: new InputDecoration(labelText: "Enter a $type"),
keyboardType:
type == 'text' ? TextInputType.text : TextInputType.number,
inputFormatters: <TextInputFormatter>[
type == 'text'
? LengthLimitingTextInputFormatter(50)
: WhitelistingTextInputFormatter.digitsOnly
], // Only numbers can be entered
onChanged: (value) {
setState(() {
inputValue = value;
});
},
);
} else if (type == 'select') {
if (inputValue.length == 0) {
// set the input Value for the first time
inputValue = dropDownList[0];
}
inputW = DropdownButton<String>(
value: inputValue,
icon: Icon(Icons.arrow_downward),
iconSize: 24,
elevation: 16,
style: TextStyle(color: Colors.deepPurple),
underline: Container(
height: 2,
color: Colors.deepPurpleAccent,
),
onChanged: (value) {
setState(() {
inputValue = value;
});
},
items: dropDownList.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
);
}
return inputW;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.symmetric(vertical: 30, horizontal: 30),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
RaisedButton(
onPressed: () {
setState(() {
answers.add(inputValue);
inputValue = '';
txtCtrl.clear();
questionID = questionID + 1;
});
// unfocus to close the Keyboard
// conrtibution to: https://flutterigniter.com/dismiss-keyboard-form-lose-focus/
FocusScopeNode currentFocus = FocusScope.of(context);
if (!currentFocus.hasPrimaryFocus) {
currentFocus.unfocus();
}
},
child: Text('next'),
),
getInputWidget(
type: questionList[questionID]['question_type'],
dropDownList: dropDownList),
Divider(thickness: 2),
Text('You enter: $inputValue'),
Divider(thickness: 2),
Text('Your answers are:'),
Flexible(
child: ListView.builder(
itemCount: answers.length,
itemBuilder: (context, index) {
return ListTile(
title: Text('$index. ${answers[index]}'),
);
}),
),
],
),
),
);
}
}

List view radio buton not selecting when selected

When i run the program on a device when i tick 2nd or 3rd term it does not take effect.
I am developing an Electronic student attendance tracking system so i decided to use a radio to track the term and also use check box to track the attendance that is checked if the student is present and unchecked if the student is not present but when i check the term radio it gives the correct output on the console but does not take effect on the physical screen.
import 'package:flutter/material.dart';
import 'package:atttendance_register/dataFiles/pupils.dart';
import 'package:atttendance_register/dataFiles/attendance.dart';
import 'package:intl/intl.dart';
class attendance extends StatefulWidget {
static Future<void> show(BuildContext context) async {
await Navigator.of(context).push(
MaterialPageRoute(builder: (context)=>attendance(),fullscreenDialog: true),
);
}
#override
_attendanceState createState() => _attendanceState();
}
class _attendanceState extends State<attendance> {
// final List<Pupils> pupils =[
// Pupils('John', ' Doe', 'Brad', 'Male', '001', DateTime.now(), '21'),
// Pupils('Jane', ' Doe', 'Mary', 'Female', '002', DateTime.now(), '21'),
// Pupils('Mohamed', ' James', '', 'Male', '003', DateTime.now(), '33'),
// Pupils('Titus', ' Nabieu', 'Jusu', 'Male', '004', DateTime.now(), '21'),
// Pupils('Steven', ' kai', 'Rogers', 'Male', '005', DateTime.now(), '21'),
// Pupils('Josephine', ' Bah', 'Neneh', 'Female', '006', DateTime.now(), '23')
//
// ];
final List<Attendance> attendance =[
Attendance(false,'John Doe Brad',DateTime.now(),0),
Attendance(true,'Jane Doe Mary',DateTime.now(),2),
Attendance(false,'Mohamed James',DateTime.now(),1),
Attendance(false,'Titus Nabieu Jusu',DateTime.now(),2),
Attendance(false,'Steven kai Rogers',DateTime.now(),2),
Attendance(false,'Josephine Bah Neneh',DateTime.now(),1)
];
bool selectedCheck = false;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Enter Attendance'),
backgroundColor: Colors.blue[900],
),
backgroundColor: Colors.blue[100],
body:Container(
child: ListView.builder(
itemCount:attendance.length,
itemBuilder:(BuildContext context, int index){
int selectedRadio = attendance[index].Term;
bool selectedCheck = attendance[index].attendance;
return Container(
child: Card(
child: Column(
//final pupil =pupils[index];
children: <Widget>[
Text(attendance[index].pupilName),
Text('Select Term'),
Row(
children: <Widget>[
Radio(
value:0,
groupValue: selectedRadio,
activeColor: Colors.blue,
onChanged: (T){
print(T);
setState(() {selectedRadio = T;}
);},
),
new Text(
'1st Term'
),
new Radio(
value: 1,
groupValue: selectedRadio,
activeColor: Colors.blue,
onChanged: (T){
print(T);
setState(() {selectedRadio = T;}
);
},
),
new Text(
'2nd Term'
),
new Radio(
value: 2,
groupValue: selectedRadio,
activeColor: Colors.blue,
onChanged: (T){
print(T);
setState(() {selectedRadio = T;}
);
},
),
new Text(
'3rd Term',
),
],
),
Row(
children: <Widget>[
Checkbox(
value: selectedCheck,
activeColor: Colors.blue,
onChanged: (bool value){
print(value);
setState(() {selectedCheck = value;}
);},
),
new Text(
'Present'
),
],
),
],
),
),
);
} ,),
),
);
}
// Widget pupilsCard(BuildContext context, int index){
// final pupil =pupils[index];
// bool selectedRadio = false;
//
// return Container(
// child: Card(
// child: Column(
// children: <Widget>[
// Text(pupil.FirstName+' '+pupil.OtherName+' '+pupil.LastName),
// Text('Select Term'),
// Row(
// children: <Widget>[
//
//
// ],
// ),
// Checkbox(
// value: selectedRadio,
// activeColor: Colors.blue,
// onChanged: (bool value){
// print(value);
// setState(() {selectedRadio = value;}
// );},
// ),
// new Text(
// 'Present'
//
// ),
// ],
// ),
// ),
// );
// }
}
In the onChanged property of your Radio widgets and your Checkbox widget, you are assigning the user selected value to the variable selectedRadio / selectedCheck and here is the problem, because when the new State is created through setState, the ListView is rebuilding and you are reassigning selectedRadio / selectedCheck the old value of the objects in this lines:
int selectedRadio = attendance[index].Term;
bool selectedCheck = attendance[index].attendance;
So you were not changing the actual value of the objects in the List, but you have too:
onChanged: (T) {
print(T);
setState(() => attendance[index].Term = T);
},
and
onChanged: (value) {
print(value);
setState(() => attendance[index].attendance = value);
},