List view radio buton not selecting when selected - flutter

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);
},

Related

Why dart objects are modified when the associated variables are modified?

I want to display markers on a Flutter Map ('package:flutter_map/flutter_map.dart')
But all map markers take the same parameters when I add one.
When the user clicks on the map, a popup opens with fields to define the attributes of the marker.
These fields values are associated to class variables.
Is it because the Marker builder uses the pointers of the primitive variables and not their values?
Or it's because Dart understands primitive types as objects?
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:latlong2/latlong.dart';
import 'package:positioned_tap_detector_2/positioned_tap_detector_2.dart';
///
/// Widget that allows display intervention
///
class DisplayIntervention extends StatefulWidget {
const DisplayIntervention({Key? key}) : super(key: key);
#override
_DisplayInterventionState createState() => _DisplayInterventionState();
}
class _DisplayInterventionState extends State<DisplayIntervention> {
// Initialize map controller
late final MapController mapController;
// Size of the left panel
final int leftPaneProportion = 20;
// Map settings
List<Marker> map_markers = [];
List<Map> availableColors = [
{'name': 'Red', 'value': Colors.red},
{'name': 'Green', 'value': Colors.green},
{'name': 'Blue', 'value': Colors.blue},
{'name': 'Black', 'value': Colors.black},
];
// Map capture (start capture many points)
bool mapCapture = false;
// History of taps
List<LatLng> tapHistory = [];
// ---- END DRAWER SECTION ----- //
// ---- START NEW MARKER SECTION ----- //
final _markerFormKey = GlobalKey<FormState>();
String _markerLabelController = "";
IconData _markerTypeController = Icons.directions_car;
int _markerRotationController = 0;
double _markerSizeController = 30.0;
Color _markerColorController = Colors.red;
List<Map> availableVehicles = [
{'name': 'Car', 'value': Icons.directions_car},
{'name': 'Truck', 'value': Icons.local_shipping},
];
// ---- END NEW MARKER SECTION ----- //
#override
void initState() {
super.initState();
mapController = MapController();
}
void _handleTap(TapPosition tapPosition, LatLng latlng) {
tapHistory.add(latlng);
openMarkerPopup();
}
void computeMarker(){
print("Add new marker");
map_markers.add(
Marker(
width: _markerSizeController,
height: _markerSizeController,
point: tapHistory.last,
builder: (ctx) =>
Container(
child: Icon(_markerTypeController, color: _markerColorController, size: _markerSizeController),
),
));
setState(() {
map_markers;
});
}
openMarkerPopup(){
showDialog(context: context, builder: (BuildContext context) {
return AlertDialog(
scrollable: true,
title: Text('Add marker'),
content: Padding(
padding: const EdgeInsets.all(8.0),
child: Form(
key: _markerFormKey,
child: Column(
children: <Widget>[
TextFormField(
initialValue: _markerLabelController,
onChanged: (value) {
_markerLabelController = value;
},
decoration: const InputDecoration(
labelText: 'Label',
icon: Icon(Icons.abc_rounded),
),
),
DropdownButtonFormField(
decoration: const InputDecoration(
icon: Icon(Icons.border_color),
),
value: _markerTypeController,
items: availableVehicles.map((map) {
return DropdownMenuItem(
child: Text(map['name']),
value: map['value'],
);
}).toList(),
onChanged: (value) {
setState(() {
if (value != null){
_markerTypeController = value as IconData;
}
});
},
),
DropdownButtonFormField(
decoration: const InputDecoration(
icon: Icon(Icons.brush_rounded),
),
value: _markerColorController,
items: availableColors.map((map) {
return DropdownMenuItem(
child: Text(map['name']),
value: map['value'],
);
}).toList(),
onChanged: (value) {
setState(() {
if (value != null){
_markerColorController = value as Color;
}
});
},
),
TextFormField(
initialValue: _markerSizeController.toString(),
keyboardType: TextInputType.number,
onChanged: (value) {
_markerSizeController = double.parse(value);
},
decoration: const InputDecoration(
labelText: 'Size',
icon: Icon(Icons.expand_more),
),
),
TextFormField(
initialValue: _markerRotationController.toString(),
keyboardType: TextInputType.number,
onChanged: (value) {
_markerRotationController = int.parse(value);
},
decoration: const InputDecoration(
labelText: 'Angle',
icon: Icon(Icons.zoom_in_rounded),
),
)
],
),
),
),
actions: [
ElevatedButton(
child: Text("Back"),
onPressed: () {
Navigator.of(context).pop();
}),
ElevatedButton(
child: Text("Valid"),
onPressed: () {
computeMarker();
Navigator.of(context).pop();
}),
],
);
});
}
#override
Widget build(BuildContext context) {
return Flex(
direction: Axis.horizontal,
children: [
Flexible(
flex: leftPaneProportion,
child: Container(
color: Colors.white,
child: Scaffold(
resizeToAvoidBottomInset: true,
body: ListView(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom),
children: const <Widget>[
Card(
child: ListTile(
title: Text("Debug"),
trailing: Icon(
Icons.arrow_circle_right_outlined))),
],
))),
),
Flexible(
flex: 100 - leftPaneProportion,
child: FlutterMap(
mapController: mapController,
options: MapOptions(
center: LatLng(48.117266, -1.6777926),
zoom: 10,
onTap: _handleTap
),
layers: [
MarkerLayerOptions(
markers: map_markers
),
],
children: <Widget>[
TileLayerWidget(
options: TileLayerOptions(
urlTemplate:
'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
subdomains: ['a', 'b', 'c'],
),
),
],
),
),
],
);
}
}
The problem came from the marker builder
By giving directly the global variable the builder uses the pointer.
While by passing the value via an intermediate variable the problem is solved.

How do I execute Navigator.of(context).pop() when the state doesn't return an error in BLoC?

I'm building a very simple 2FA application, which scans a QR code and then it shows the TOTP code on the screen. However, I don't know how to go back to previous screen using Navigator.of(context).pop(). When I do that in my app, it crashes.
Here's a short video how my app reacts to this: https://streamable.com/ao6qql
How do I check if the form is empty? If it is, I want to show the Alert Dialog, but it returns to the previous screen and it is showing the AlertDialog. How do I do it so my Navigator.of(context).pop() executes when the form is filled?
Code:
The block which crashes my app:
BlocListener<ManualInputBloc, ManualInputState>(
listener: (context, state) {
if (state is ManualInputError) {
Platform.isAndroid
? CustomAlertDialog.showAndroidAlertDialog(
context,
state.alertDialogErrorTitle,
state.alertDialogErrorContent,
)
: CustomAlertDialog.showIosAlertDialog(
context,
state.alertDialogErrorTitle,
state.alertDialogErrorContent,
);
}
},
child: TextButton(
onPressed: () {
final manualInputBloc =
BlocProvider.of<ManualInputBloc>(context);
manualInputBloc.add(
GetFormTextEvent(
secretKey,
issuer,
accountName,
numberOfDigits,
timeStep,
),
);
Navigator.of(context).pop();
},
child: Text('add').tr(),
),
)
The whole screen:
import 'dart:io';
import 'package:duckie/blocs/manual_input/manual_input_bloc.dart';
import 'package:duckie/screens/widgets/alert_dialog.dart';
import 'package:duckie/screens/widgets/custom_text_field.dart';
import 'package:duckie/shared/text_styles.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class ManualInputScreen extends StatefulWidget {
#override
_ManualInputScreenState createState() => _ManualInputScreenState();
}
class _ManualInputScreenState extends State<ManualInputScreen> {
String secretKey;
String issuer;
String accountName;
String numberOfDigits = '6';
String timeStep = '30';
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
'manual-input',
style: TextStyles.appBarText,
).tr(),
centerTitle: true,
elevation: 0.0,
actions: [
BlocListener<ManualInputBloc, ManualInputState>(
listener: (context, state) {
if (state is ManualInputError) {
Platform.isAndroid
? CustomAlertDialog.showAndroidAlertDialog(
context,
state.alertDialogErrorTitle,
state.alertDialogErrorContent,
)
: CustomAlertDialog.showIosAlertDialog(
context,
state.alertDialogErrorTitle,
state.alertDialogErrorContent,
);
}
},
child: TextButton(
onPressed: () {
final manualInputBloc =
BlocProvider.of<ManualInputBloc>(context);
manualInputBloc.add(
GetFormTextEvent(
secretKey,
issuer,
accountName,
numberOfDigits,
timeStep,
),
);
Navigator.of(context).pop();
},
child: Text('add').tr(),
),
)
],
),
body: Container(
padding: EdgeInsets.all(8.0),
child: ListView(
children: [
CustomTextField(
labelText: 'secret-key'.tr(),
onChanged: (value) {
setState(() {
secretKey = value;
});
},
),
SizedBox(
height: 8.0,
),
CustomTextField(
labelText: 'issuer'.tr(),
onChanged: (value) {
issuer = value;
},
),
SizedBox(
height: 8.0,
),
CustomTextField(
labelText: 'account-name'.tr(),
onChanged: (value) {
setState(() {
accountName = value;
});
},
),
SizedBox(
height: 8.0,
),
Platform.isAndroid
? ListBody(
children: [
Text('number-of-digits').tr(),
SizedBox(
height: 5.0,
),
DropdownButton(
value: numberOfDigits,
onChanged: (value) {
setState(() {
numberOfDigits = value;
});
},
items: [
DropdownMenuItem(
value: '6',
child: Text('6'),
),
DropdownMenuItem(
value: '8',
child: Text('8'),
),
],
)
],
)
: ListBody(
children: [
Text('number-of-digits').tr(),
SizedBox(
height: 5.0,
),
CupertinoSegmentedControl(
groupValue: numberOfDigits,
children: {
'6': Text('6'),
'8': Text('8'),
},
onValueChanged: (value) {
setState(() {
numberOfDigits = value;
});
},
),
],
),
SizedBox(
height: 8.0,
),
Platform.isAndroid
? ListBody(
children: [
Text('time-step').tr(),
SizedBox(
height: 5.0,
),
DropdownButton(
value: timeStep,
onChanged: (value) {
setState(() {
timeStep = value;
});
},
items: [
DropdownMenuItem(
value: '30',
child: Text('30'),
),
DropdownMenuItem(
value: '60',
child: Text('60'),
),
],
)
],
)
: ListBody(
children: [
Text('time-step').tr(),
SizedBox(
height: 5.0,
),
CupertinoSegmentedControl(
groupValue: timeStep,
children: {
'30': Text('30'),
'60': Text('60'),
},
onValueChanged: (value) {
setState(() {
timeStep = value;
});
},
),
],
),
],
),
),
);
}
}

How to change event in BLoC in Flutter?

I have an app which creates a 2FA totp key. When I click "Add" button when a form is not filled, it shows an Alert Dialog. If I click it second time (still, the form isn't filled), the alert dialog doesn't show up. How do I show the Alert Dialog for the infinite times? I used BlocConsumer to listen to changes and show the Alert Dialog when the state is ManualInputError, and a BlocBuilder to show the actual TextButton.
Code:
import 'dart:io';
import 'package:duckie/blocs/manual_input/manual_input_bloc.dart';
import 'package:duckie/screens/widgets/alert_dialog.dart';
import 'package:duckie/screens/widgets/custom_text_field.dart';
import 'package:duckie/shared/text_styles.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class ManualInputScreen extends StatefulWidget {
#override
_ManualInputScreenState createState() => _ManualInputScreenState();
}
class _ManualInputScreenState extends State<ManualInputScreen> {
String secretKey;
String issuer;
String accountName;
String numberOfDigits = '6';
String timeStep = '30';
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
'manual-input',
style: TextStyles.appBarText,
).tr(),
centerTitle: true,
elevation: 0.0,
actions: [
BlocConsumer<ManualInputBloc, ManualInputState>(
listener: (context, state) {
if (state is ManualInputError) {
Platform.isAndroid
? CustomAlertDialog.showAndroidAlertDialog(
context,
state.alertDialogErrorTitle,
state.alertDialogErrorContent)
: CustomAlertDialog.showIosAlertDialog(
context,
state.alertDialogErrorTitle,
state.alertDialogErrorContent);
BlocProvider.of<ManualInputBloc>(context).close();
}
},
builder: (context, state) {
if (state is ManualInputInitial || state is ManualInputFinal) {
return TextButton(
onPressed: () {
BlocProvider.of<ManualInputBloc>(context).add(
GetFormTextEvent(secretKey, issuer, accountName,
numberOfDigits, timeStep));
},
child: Text('add').tr(),
);
}
return TextButton(
onPressed: () {},
child: Text('add').tr(),
);
},
)
],
),
body: Container(
padding: EdgeInsets.all(8.0),
child: ListView(
children: [
CustomTextField(
labelText: 'secret-key'.tr(),
onChanged: (value) {
setState(() {
secretKey = value;
});
},
),
SizedBox(
height: 8.0,
),
CustomTextField(
labelText: 'issuer'.tr(),
onChanged: (value) {
issuer = value;
},
),
SizedBox(
height: 8.0,
),
CustomTextField(
labelText: 'account-name'.tr(),
onChanged: (value) {
setState(() {
accountName = value;
});
},
),
SizedBox(
height: 8.0,
),
Platform.isAndroid
? ListBody(
children: [
Text('number-of-digits').tr(),
SizedBox(
height: 5.0,
),
DropdownButton(
value: numberOfDigits,
onChanged: (value) {
setState(() {
numberOfDigits = value;
});
},
items: [
DropdownMenuItem(
value: '6',
child: Text('6'),
),
DropdownMenuItem(
value: '8',
child: Text('8'),
),
],
)
],
)
: ListBody(
children: [
Text('number-of-digits').tr(),
SizedBox(
height: 5.0,
),
CupertinoSegmentedControl(
groupValue: numberOfDigits,
children: {
'6': Text('6'),
'8': Text('8'),
},
onValueChanged: (value) {
setState(() {
numberOfDigits = value;
});
},
),
],
),
SizedBox(
height: 8.0,
),
Platform.isAndroid
? ListBody(
children: [
Text('time-step').tr(),
SizedBox(
height: 5.0,
),
DropdownButton(
value: timeStep,
onChanged: (value) {
setState(() {
timeStep = value;
});
},
items: [
DropdownMenuItem(
value: '30',
child: Text('30'),
),
DropdownMenuItem(
value: '60',
child: Text('60'),
),
],
)
],
)
: ListBody(
children: [
Text('time-step').tr(),
SizedBox(
height: 5.0,
),
CupertinoSegmentedControl(
groupValue: timeStep,
children: {
'30': Text('30'),
'60': Text('60'),
},
onValueChanged: (value) {
setState(() {
timeStep = value;
});
},
),
],
),
],
),
),
);
}
}
The reason is that you close(kill) the BLoC here
BlocProvider.of<ManualInputBloc>(context).close();
Change the listener to that
listener: (context, state) {
if (state is ManualInputError) {
Platform.isAndroid
? CustomAlertDialog.showAndroidAlertDialog(
context,
state.alertDialogErrorTitle,
state.alertDialogErrorContent)
: CustomAlertDialog.showIosAlertDialog(
context,
state.alertDialogErrorTitle,
state.alertDialogErrorContent);
}
},

How to select a default radio button in flutter

I have implemented three radio buttons in my flutter project. But after executing the application, none of the radio buttons are showing selected. Can anyone help me where I am getting wrong please !
There is no radio button is getting selected although I click on it. I am unable to figure out the reason. Please help me solving it. Here is my code :
class AdminHomeContent extends StatefulWidget {
#override
_AdminHomeContentState createState() => _AdminHomeContentState();
}
class _AdminHomeContentState extends State<AdminHomeContent> {
static final Map<String, Color> options = {
'All': Colors.black, 'Cancelled':Colors.red,
'Pending': Colors.yellow, 'Visited': Colors.green[900],
};
List keyList = options.keys.toList();
List keyColor = options.values.toList();
String selectedOption = 'All';
int groupValue = 0 ;
void buttonValue(int v){
setState(() {
groupValue = v ;
});
}
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider<PatientDataNotifier>(
create: (context) => PatientDataNotifier(),
child: MaterialApp(
home: Scaffold(
// some of my other codes
----------
-----------
//method that defines three radio buttons
Future<dynamic> getStatus(BuildContext context) {
return showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
content: SingleChildScrollView(
child: Container(
child: Column(
children: [
Row(
children: [
Radio(value: 1,
activeColor: Colors.blue,
groupValue: groupValue,
onChanged: (int v){
print(v);
buttonValue(v);
}
),
Text(keyList[2] , style: TextStyle(color: keyColor[2]),)
],
),
Row(
children: [
Radio(value: 2,
activeColor: Colors.blue,
groupValue: groupValue,
onChanged: (int v) {
print(v);
buttonValue(v);
}
),
Text(keyList[3] , style: TextStyle(color: keyColor[3]))
],
),
Row(
children: [
Radio(value: 3,
activeColor: Colors.blue,
groupValue: groupValue,
onChanged: (int v) {
print(v);
buttonValue(v);
}
),
Text(keyList[1] , style: TextStyle(color: keyColor[1]))
],
)
],
),
),
),
);
}
);
}
// some codes
------------------
-----------------
//calling the getStatus() inside the onTap() property of a Text
GestureDetector(
child: Text(patient.status,
style: getTextStyle('Poppins-Regular',15.0, FontWeight.bold, options[status]),
),
onTap: () async{
await getStatus(context);
},
),
}
}
None of the radio buttons are getting selected even after clicking on it. Please help me solving it.
First of all, none of Radio is selected by default because your initial groupValue is 0 and none of your Radio has this value.
Here a fully example of working Radio
class MyRadio extends StatefulWidget {
#override
_MyRadioState createState() => _MyRadioState();
}
int groupValue = 1;
class _MyRadioState extends State<MyRadio> {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: Column(
children: [
Radio(
value: 1,
activeColor: Colors.blue,
groupValue: groupValue,
onChanged: (int v) {
buttonValue(v);
},
),
Radio(
value: 2,
activeColor: Colors.blue,
groupValue: groupValue,
onChanged: (int v) {
buttonValue(v);
},
),
Radio(
value: 3,
activeColor: Colors.blue,
groupValue: groupValue,
onChanged: (int v) {
buttonValue(v);
},
),
],
),
),
),
);
}
void buttonValue(int v) {
setState(() {
groupValue = v;
});
}
}

Highlighting two radio buttons at once

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.