I am trying to dynamically create some checkboxes based on data pulled from an API. My checkboxes are created but when I click on them they are all being checked or unchecked.
I am pretty sure I can identify why I am getting is, I am just unsure how to overcome it.
The variable I create is for one check box, so when my list is created from ListView.builder, it is using the same variable thereby making all boxes check and uncheck. I know that I need to create that variable based on how many items are in the list. I am just not sure how to do this and where within my code structure. I tried different methods of using .length or trying to use a .forEach but none of it was correct in the method I was implementing it. I have included my code that shows how I am creating my list of tags.
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:gateway_device/backend/api/api_services.dart';
import 'package:gateway_device/flutter_flow/flutter_flow_theme.dart';
import 'package:gateway_device/models/tag_list_model.dart';
class TagsListWidget extends StatefulWidget {
final int companyId;
const TagsListWidget({Key? key, required this.companyId}) : super(key: key);
#override
State<TagsListWidget> createState() => _TagsListWidgetState(companyId);
}
class _TagsListWidgetState extends State<TagsListWidget> {
final int companyId;
late bool checkboxListTileValue = false;
final scaffoldKey = GlobalKey<ScaffoldState>();
_TagsListWidgetState(this.companyId);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
"Tags",
textAlign: TextAlign.center,
style: FlutterFlowTheme.of(context).title2.override(
fontFamily: 'Heebo',
fontSize: 18.sp,
fontWeight: FontWeight.w500),
),
elevation: 0,
actions: <Widget>[
IconButton(
hoverColor: Colors.transparent,
iconSize: 40,
icon: Icon(
Icons.search,
color: Colors.black,
size: 20,
),
onPressed: () {
print("Test");
},
)
],
leading: IconButton(
hoverColor: Colors.transparent,
iconSize: 40,
icon: Icon(
Icons.keyboard_return_sharp,
color: Colors.black,
size: 30,
),
onPressed: () {
Navigator.pop(context);
},
),
centerTitle: true,
backgroundColor: Colors.white,
iconTheme: IconThemeData(color: Colors.black),
),
backgroundColor: Colors.white,
body: SafeArea(
child: FutureBuilder(
future: ApiService().getTagList(companyId),
builder: (context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
Tags tags = snapshot.data[index];
return GestureDetector(
onTap: (() {
FocusScope.of(context).unfocus();
}),
child: Center(
child: CheckboxListTile(
value: checkboxListTileValue,
onChanged: (newValue) => setState(() {
checkboxListTileValue = newValue!;
}),
title: Text(tags.tag,
textAlign: TextAlign.start,
style: FlutterFlowTheme.of(context)
.title3
.override(
fontFamily: 'Heebo',
color: Colors.black,
fontSize: 18.sp)),
),
),
);
});
}
return Center(
child: CircularProgressIndicator(),
);
}),
),
);
}
}
I appreciate the help!
You can create List:
List<bool> checkboxValue = [];
then use it like this:
return StatefulBuilder(// add this
builder: (c, innerSetState) {
return GestureDetector(
onTap: (() {
FocusScope.of(context).unfocus();
}),
child: ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
Tags tags = snapshot.data[index];
checkboxValue = List<bool>.generate(snapshot.data.length,(counter) => false); // add this
return GestureDetector(
onTap: (() {
FocusScope.of(context).unfocus();
}),
child: Center(
child: CheckboxListTile(
value: checkboxValue[index], //add this
onChanged: (newValue) {
innerSetState(() {// add this
checkboxValue[index] = newValue;
});
},
title: Text(tags.tag,
textAlign: TextAlign.start,
style: FlutterFlowTheme.of(context)
.title3
.override(
fontFamily: 'Heebo',
color: Colors.black,
fontSize: 18.sp)),
),
),
);
}),
);
},
);
note that this work when checkbox value not come from api, if it is let me know so I can Update my answer.
Here is the problem, that you use varieble checkboxListTileValue in global scope. Then yo set new value in:
onChanged: (newValue) => setState(() {
checkboxListTileValue = newValue!;})
Global checkboxListTileValue applayed to all ListView items.
You can made the List checkboxListTileValue, with defaul value set. And onChage set the new value only for item with index which was clicked.
onChanged: (newValue) => setState(() {
checkboxListTileValue[index] = newValue!;})
You are using single bool to control the checked status of a list. You can create a model class with a bool filed that will be like bool isCheked=false , Once you click on item check it is already checked or not and change the value.
Or create a List that will hold tags.tag value.
List<String> selected = [];
....
value: selected.contains(tags.tag),
onChanged: (newValue)
{
if(selected.contains(tags.tag)){
selected.remove(tags.tag); }
else{
selected.add(tags.tag);
}
setState((){});
Related
I have a page that the user can add students to the list by entering their name in the listtile in the listview, i wanted to have 2 specific radio buttons for each name one green one red for their presence or absence. I have created my version of it already but when you click on radio button it changes all in that column. is there any other way that this can be done?
1
2
my code:
import 'package:flutter/material.dart';
import 'package:flutter_speed_dial/flutter_speed_dial.dart';
class InsideList extends StatefulWidget {
final String name;
InsideList(this.name);
#override
State<InsideList> createState() => _InsideListState();
}
class _InsideListState extends State<InsideList> {
List<String> _students = [];
late int selectedRadio;
late TextEditingController _textController;
#override
void initState() {
super.initState();
_textController = TextEditingController();
selectedRadio = 0;
}
SetselectedRadio(int? val) {
setState(() {
selectedRadio = val!;
});
}
#override
void dispose() {
_textController.dispose();
super.dispose();
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.name),
centerTitle: true,
backgroundColor: const Color.fromARGB(255, 22, 37, 50),
toolbarHeight: 65,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
bottom: Radius.circular(30),
),
),
),
body: _students.length > 0
? ListView.separated(
itemCount: _students.length,
itemBuilder: (_, index) {
return ListTile(
leading: const Icon(Icons.person),
trailing: FittedBox(
fit: BoxFit.fill,
child: Row(
children: [
Radio(
activeColor: Colors.green,
value: 0,
groupValue: selectedRadio,
onChanged: (val) {
SetselectedRadio(val);
}),
Radio(
activeColor: Colors.red,
value: 1,
groupValue: selectedRadio,
onChanged: (val) {
SetselectedRadio(val);
},
)
],
),
),
title: Center(child: Text(_students[index])),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: ((context) =>
InsideList(_students[index]))));
},
onLongPress: (() async {
await showDialog(
context: context,
builder: ((context) {
return AlertDialog(
title: const Text(
"Are you sure you want to delete this student?",
style: TextStyle(fontSize: 15),
),
actions: [
TextButton(
child: Text("cancel"),
onPressed: (() {
Navigator.pop(context);
})),
TextButton(
child: Text('Delete'),
onPressed: () {
setState(() {
_students.removeAt(index);
Navigator.pop(context);
});
},
),
],
);
}));
}),
);
},
separatorBuilder: (BuildContext context, int index) =>
const Divider(
color: Colors.black,
),
)
: const Center(
child: Text("You currently have no students. Add from below."),
),
floatingActionButton: SpeedDial(
animatedIcon: AnimatedIcons.menu_arrow,
spacing: 6,
spaceBetweenChildren: 6,
backgroundColor: const Color.fromARGB(255, 22, 37, 50),
foregroundColor: const Color.fromARGB(255, 255, 255, 255),
children: [
SpeedDialChild(
child: const Icon(Icons.group_add),
label: "add student",
onTap: () async {
final result = await showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: const Text('Add a new student'),
content: TextField(
controller: _textController,
autofocus: true,
decoration: const InputDecoration(
hintText: "Enter the name of the student."),
),
actions: [
TextButton(
child: Text('Cancel'),
onPressed: () {
Navigator.pop(context);
},
),
TextButton(
child: Text('Add'),
onPressed: () {
Navigator.pop(context, _textController.text);
_textController.clear();
},
),
],
);
},
);
if (result != null) {
result as String;
setState(() {
_students.add(result);
});
}
},
),
],
),
);
}
}
It's because basically you are assigning same values for each Radio Button Group. There is a better way but I just have modified your code a bit to show you how to do it.
First, you assign a list for radio values along with students.
List<String> _students = [];
List<int> _selectedRadio = [];
And for assigning a value to a radio button, you need index of the radio button as well.
void _selectRadio(int index, int? val) {
setState(() {
_selectedRadio[index] = val ?? 0;
});
}
Then for Radio Buttons, assign a group value with index.
Radio(
activeColor: Colors.green,
value: 0,
groupValue: _selectedRadio[index],
onChanged: (val) {
_selectRadio(index, val);
},
),
Radio(
activeColor: Colors.red,
value: 1,
groupValue: _selectedRadio[index],
onChanged: (val) {
_selectRadio(index, val);
},
)
Then finally, when you create a student, you add a radio button value to the list of radio button value.
if (result != null) {
result as String;
setState(() {
_students.add(result);
_selectedRadio.add(0);
});
}
And below is the full working code. Hope this helps.
class InsideList extends StatefulWidget {
final String name;
InsideList(this.name);
#override
State<InsideList> createState() => _InsideListState();
}
class _InsideListState extends State<InsideList> {
List<String> _students = [];
List<int> _selectedRadio = [];
late TextEditingController _textController;
#override
void initState() {
super.initState();
_textController = TextEditingController();
}
void _selectRadio(int index, int? val) {
setState(() {
_selectedRadio[index] = val ?? 0;
});
}
#override
void dispose() {
_textController.dispose();
super.dispose();
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.name),
centerTitle: true,
backgroundColor: const Color.fromARGB(255, 22, 37, 50),
toolbarHeight: 65,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
bottom: Radius.circular(30),
),
),
),
body: _students.length > 0
? ListView.separated(
itemCount: _students.length,
itemBuilder: (_, index) {
return ListTile(
leading: const Icon(Icons.person),
trailing: FittedBox(
fit: BoxFit.fill,
child: Row(
children: [
Radio(
activeColor: Colors.green,
value: 0,
groupValue: _selectedRadio[index],
onChanged: (val) {
_selectRadio(index, val);
}),
Radio(
activeColor: Colors.red,
value: 1,
groupValue: _selectedRadio[index],
onChanged: (val) {
_selectRadio(index, val);
},
)
],
),
),
title: Center(child: Text(_students[index])),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: ((context) =>
InsideList(_students[index]))));
},
onLongPress: (() async {
await showDialog(
context: context,
builder: ((context) {
return AlertDialog(
title: const Text(
"Are you sure you want to delete this student?",
style: TextStyle(fontSize: 15),
),
actions: [
TextButton(
child: Text("cancel"),
onPressed: (() {
Navigator.pop(context);
})),
TextButton(
child: Text('Delete'),
onPressed: () {
setState(() {
_students.removeAt(index);
_selectedRadio.removeAt(index);
Navigator.pop(context);
});
},
),
],
);
}));
}),
);
},
separatorBuilder: (BuildContext context, int index) =>
const Divider(
color: Colors.black,
),
)
: const Center(
child: Text("You currently have no students. Add from below."),
),
floatingActionButton: SpeedDial(
animatedIcon: AnimatedIcons.menu_arrow,
spacing: 6,
spaceBetweenChildren: 6,
backgroundColor: const Color.fromARGB(255, 22, 37, 50),
foregroundColor: const Color.fromARGB(255, 255, 255, 255),
children: [
SpeedDialChild(
child: const Icon(Icons.group_add),
label: "add student",
onTap: () async {
final result = await showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: const Text('Add a new student'),
content: TextField(
controller: _textController,
autofocus: true,
decoration: const InputDecoration(
hintText: "Enter the name of the student."),
),
actions: [
TextButton(
child: Text('Cancel'),
onPressed: () {
Navigator.pop(context);
},
),
TextButton(
child: Text('Add'),
onPressed: () {
Navigator.pop(context, _textController.text);
_textController.clear();
},
),
],
);
},
);
if (result != null) {
result as String;
setState(() {
_students.add(result);
_selectedRadio.add(0);
});
}
},
),
],
),
);
}
}
You have to create List < int > SelectedRadio , which will always has your students list length. Next in method SetSelectedRadio you have to change value in SelectedRadio[student_index]
You have done it wrong you have given the radioButtons a single variable which all the radioButtons are referring to this cause them to share the same value and change accordingly(meaning all the radioButtons with corresponding values will change).
You can use various methods to pass this FOR EXAMPLE :
You can generate a secondary list that will hold all the bool values for each and every list item you can use list.generate() to generate the list depending on the length of the _student list.
You can create a model class where you save both name and the int value for the radio buttons (Most preferred as it gives more flexibility for future changes) I have mentioned the same below
Full code
// Here I have created the model class to create a list.
// do not make the arguments final as they will not change as we need them to change.
class student {
String nameOfStudent;
int isPresent;
student({
required this.nameOfStudent,
required this.isPresent,
});
}
class InsideList extends StatefulWidget {
final String name;
InsideList(this.name);
#override
State<InsideList> createState() => _InsideListState();
}
class _InsideListState extends State<InsideList> {
// As this list is not final one can change the values dynamically.
// You can add the items using _students.add(student(
// nameOfStudent: "Name",
// isPresent: 0,
// ));
List<student> _students = [];
late TextEditingController _textController;
#override
void initState() {
super.initState();
_textController = TextEditingController();
}
#override
void dispose() {
_textController.dispose();
super.dispose();
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.name),
centerTitle: true,
backgroundColor: const Color.fromARGB(255, 22, 37, 50),
toolbarHeight: 65,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
bottom: Radius.circular(30),
),
),
),
body: _students.length > 0
? ListView.separated(
itemCount: _students.length,
itemBuilder: (_, index) {
return ListTile(
leading: const Icon(Icons.person),
trailing: FittedBox(
fit: BoxFit.fill,
child: Row(
children: [
Radio(
activeColor: Colors.green,
value: 0,
groupValue: _students[index].isPresent,
onChanged: (val) {
setState(() {
_students[index].isPresent = val!;
});
}),
Radio(
activeColor: Colors.red,
value: 1,
// this will go to the list with the idex and fetch the value
groupValue: _students[index].isPresent,
onChanged: (val) {
// this will assign a new value to the item with the corresponding index
// this will give each and every item its own radioButton variable resulting in proper value change for each item in the list.
setState(() {
_students[index].isPresent = val!;
});
},
)
],
),
),
title: Center(child: Text(_students[index].nameOfStudent)),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: ((context) =>
InsideList(_students[index]))));
},
onLongPress: (() async {
await showDialog(
context: context,
builder: ((context) {
return AlertDialog(
title: const Text(
"Are you sure you want to delete this student?",
style: TextStyle(fontSize: 15),
),
actions: [
TextButton(
child: Text("cancel"),
onPressed: (() {
Navigator.pop(context);
})),
TextButton(
child: Text('Delete'),
onPressed: () {
setState(() {
_students.removeAt(index);
Navigator.pop(context);
});
},
),
],
);
}));
}),
);
},
separatorBuilder: (BuildContext context, int index) =>
const Divider(
color: Colors.black,
),
)
: const Center(
child: Text("You currently have no students. Add from below."),
),
);
}
}
As I have mentioned there are many more ways to do the same (using Map as well) Hope this is help full and keep in mind about making variables final as it will not change will the application is running.
I want to persist value after user leaves page, also I would like to persist selected values, so I found out shared prefernces and I save it locally, but when I left page and return it remains unselected.
So I decided to convert my multipleSelected list to String, because sharedprefernces can't save list of ints and sfter that save selected values in lists. So how can i solve that problem when user leaves page and selected items become unselected.
class DataBaseUser extends StatefulWidget {
const DataBaseUser({Key? key}) : super(key: key);
#override
State<DataBaseUser> createState() => _DataBaseUserState();
}
class _DataBaseUserState extends State<DataBaseUser> {
int index = 1;
/// add selected items from list
List multipleSelected = [];
/// another list to form the new list above previous one
List chosenListsAbove = [];
List basesNames = [];
SharedPreferences? sharedPreferences;
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Typographys.primaryColor,
appBar: PreferredSize(
preferredSize: const Size(125, 125),
child: AppBarService(),
),
body: Column(
children: [
// chosenOne(),
Card(
color: Typographys.gradientCard2,
child: ExpansionTile(
iconColor: Colors.white,
maintainState: true,
title: Text(
'Bases',
style: TextStyle(
fontFamily: 'fonts/Montserrat',
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 35),
),
children: [
SizedBox(
height: 10,
),
getDataBaseList(),
SizedBox(
height: 22,
),
getUpdateBaseButtons(),
SizedBox(
height: 10,
),
],
),
),
],
),
);
}
Widget getDataBaseList() {
return FutureBuilder<List>(
future: BasesService().GetBases(),
builder: (context, snapshot) {
List? baseNames = snapshot.data;
print(baseNames);
return ListView.builder(
shrinkWrap: true,
itemCount: baseNames?.length ?? 0,
itemBuilder: (context, i) {
Future<void> _onCategorySelected(bool selected, id) async {
final pref = await SharedPreferences.getInstance();
if (selected == true) {
setState(() {
multipleSelected.add(id);
List<String> stringsList =
multipleSelected.map((i) => i.toString()).toList();
// store your string list in shared prefs
pref.setStringList("stringList", stringsList);
List<String> mList =
(pref.getStringList('stringList') ?? <String>[]);
print('HERE');
print(mList);
print('HERE 2');
});
} else {
setState(
() {
multipleSelected.remove(id);
},
);
}
}
return Column(
children: [
ListTile(
title: Padding(
padding: const EdgeInsets.only(left: 1.0),
child: Text(
baseNames?[i]['name'] ?? 'not loading',
style: TextStyle(
fontFamily: 'fonts/Montserrat',
fontSize: 24,
fontWeight: FontWeight.w900,
color: Colors.white),
),
),
leading: Checkbox(
activeColor: Colors.green,
checkColor: Colors.green,
side: BorderSide(width: 2, color: Colors.white),
value: multipleSelected.contains(
baseNames?[i]['id'],
),
onChanged: (bool? selected) {
_onCategorySelected(selected!, baseNames?[i]['id']);
},
)
//you can use checkboxlistTile too
),
],
);
},
);
},
);
}
Widget getUpdateBaseButtons() {
return Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
FutureBuilder<bool>(
future: BasesService().SelectBaseAsync(multipleSelected.cast()),
builder: (context, snapshot) {
return ElevatedButton(
onPressed: () {
if (snapshot.data == true) {
BasesService().SelectBaseAsync(multipleSelected.cast());
print(multipleSelected.cast());
print(multipleSelected);
successSnackBar();
} else {
notSuccessSnackBar();
}
},
child: Text(
'Send bases',
style: TextStyle(
fontFamily: 'fonts/Montserrat',
fontSize: 22,
fontWeight: FontWeight.w900,
color: Colors.white,
letterSpacing: 2),
),
style: ElevatedButton.styleFrom(
minimumSize: Size(200, 40),
primary: Colors.green,
onPrimary: Colors.white,
),
);
return Container();
})
],
),
);
}
If I understand you correclty, cant you just save items in WillPopScope like
return WillPopScope(
onWillPop: () async => SaveMyPreferences,
child: const Scaffold(
body: Container(
color: Colors.red,
size: 50.0,
),
),
);
I found a solution. If your data that you want to save comes from the API and is constantly updated (as it was in my case), then you do not need to use the shared preference package. This package will not help you. In my case, in order to save the checkboxes selected by the user and after reloading the page to show him which items in the list were selected (I use checkboxes), I write to a file on the device and then read the saved data from this file. So you are going to need path_provider package and dart:io and these two functions
to write from function where you choose items
_onCategorySelected(bool selected, id) async {
final Directory directory =
await getApplicationDocumentsDirectory();
if (selected == true) {
multipleSelected.add(id);
} else {
multipleSelected.remove(id);
}
final File file = File('${directory.path}/my_file.json');
file.writeAsStringSync('{"selected": $multipleSelected}');
setState(() {});
}
to read from file:
Future<String> read() async {
String text = '';
try {
final Directory directory =
await getApplicationDocumentsDirectory();
final File file = File('${directory.path}/my_file.json');
text = await file.readAsString();
print('HELLO');
multipleSelected = json.decode(text)["selected"];
} catch (e) {
print("Couldn't read file");
}
return text;
}
and before the listview.builder comes, you need to use read() function ro read the saved values from file.
It is not the greatest solution (maybe, the worst one), but if you haven't got enough time and you don't have any state management and you just need to solve issue right now, it can be really helpfull.
So I have problem with the changing color of an icon when it tabbed
thus, when I tab on icon it's only changes for its prober colors but for seconds and then disappears
I have used Provider as shown in the below code, and I also used isChecked = true but when I tab on one icon, all of them change as well.
So what should I do with this problem?
this this the code but my problem remains in Icon Button which's will be below it
import 'package:flutter/material.dart';
import 'package:flutter/src/widgets/container.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:note/Designs/Favorite_List.dart';
import 'package:note/Models/Favorite_List_Provider.dart';
import 'package:note/Models/Food_Models.dart';
import 'package:provider/provider.dart';
import '../Services/Fetch_Data.dart';
import 'Details_DesignOfDesignOne.dart';
class DesignOne extends StatefulWidget {
const DesignOne({super.key, required this.Image, required this.Desc});
final String Image;
final String Desc;
#override
State<DesignOne> createState() => _DesignOneState();
}
class _DesignOneState extends State<DesignOne> {
late Future<Food> futureFood;
#override
void initState() {
super.initState();
futureFood = fetchData(widget.Image, widget.Desc);
}
bool ischecked = false;
#override
Widget build(BuildContext context) {
final provider = favorite.of(context);
return Scaffold(
backgroundColor: Colors.grey.shade200,
appBar: AppBar(
title: Text('Design one'),
actions: [
Padding(
padding: const EdgeInsets.all(8.0),
child: InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => FavoriteScreen(Desc: '', Image: ''),
),
);
},
child: Icon(
Icons.list,
size: 30,
),
),
)
],
),
body: Consumer<favorite>(
builder: (BuildContext context, favorite value, child) {
return Center(
child: FutureBuilder<Food>(
future: fetchData(widget.Image, widget.Desc),
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
shrinkWrap: true,
itemCount: snapshot.data!.categories.length,
itemBuilder: (contxt, index) {
final fav = snapshot.data!.categories[index];
return InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailsDo(
Desc: snapshot.data!.categories[index]
.strCategoryDescription,
Image: snapshot
.data!.categories[index].strCategoryThumb,
),
),
);
},
child: Card(
elevation: 2,
shape: RoundedRectangleBorder(
side: BorderSide(color: Colors.white70, width: 1),
borderRadius: BorderRadius.circular(50),
),
child: ListTile(
title: Text(
snapshot.data!.categories[index].strCategory
.toString(),
style: GoogleFonts.montserrat(
fontSize: 20,
fontWeight: FontWeight.w600,
fontStyle: FontStyle.italic,
),
),
leading: CircleAvatar(
backgroundColor:
Color.fromARGB(213, 255, 251, 251),
child: Text(
snapshot.data!.categories[index].idCategory
.toString(),
style: GoogleFonts.montserrat(
fontSize: 20,
fontWeight: FontWeight.w600,
fontStyle: FontStyle.italic,
color: Color.fromARGB(255, 148, 148, 135)),
),
),
trailing: IconButton(
icon: (provider.isExist(fav) && ischecked)
? const Icon(Icons.favorite,
color: Colors.red)
: const Icon(Icons.favorite_border),
onPressed: () {
provider.toggleFavorite(fav);
setState(() {
ischecked = !ischecked;
});
},
),
),
),
);
});
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
return const CircularProgressIndicator();
},
),
);
}),
);
}
}
and this is the specific problem with the Icon Button :
trailing: IconButton(
icon: (provider.isExist(fav) && ischecked)
? const Icon(Icons.favorite,
color: Colors.red)
: const Icon(Icons.favorite_border),
onPressed: () {
provider.toggleFavorite(fav);
setState(() {
ischecked = !ischecked;
});
},
),
And here is the problem :
Would you try this way instead of isChecked variable??
class _DesignOneState extends State<DesignOne>{
static late List<bool> isChecked;
///*** your code ****
Widgetbuild(BuildContext context){
///*** your code ***
if(snapshot.hasData){
isChecked = List.filled(snapshot.data!.categories.length,false);
/// *** your code ***
icon: (provider.isExist(fav) && isChecked[index])
/// *** your code ***
setState((){
isChecked[index] = !isChecked[index];
});
}
}
}
I'm working on a quiz app as a personal project and what I want to do is make it possible for the user to name a question set. (Kind of like a folder for questions on a particular subject). I am using Riverpod. (I've worked with the provider package a couple of times) for state management but it seems I've missed a step or two because when I type in the name, I don't see it on the page. I hope I can be pointed in the right direction. Thanks
Class forRiverpod model which shows a list of type QuestionSetConstructor for taking the title. There is also a method for accepting the question title to add to the list
class RiverpodModel extends ChangeNotifier {
final List<QuestionSetConstructor> _questionSetList = [];
UnmodifiableListView<QuestionSetConstructor> get questionSet {
return UnmodifiableListView(_questionSetList);
}
void addTitleT(String title) {
final addQuestionTitle = (QuestionSetConstructor(title: title));
_questionSetList.add(addQuestionTitle);
notifyListeners();
}
int get count{
return questionSet.length;
}
}
`
This is for the alert dialog that will take the question title.
In the elevated button, I stated that I want the contents of the
text field to be added to the list in the Riverpod model.
void setQuestionNameMethod(context) {
showDialog(
context: context,
barrierDismissible: true,
builder: (BuildContext context) {
return SetQuestionNameAlertDialog();
});
}
class SetQuestionNameAlertDialog extends ConsumerStatefulWidget {
#override
_SetQuestionNameAlertDialogState createState() =>
_SetQuestionNameAlertDialogState();
}
class _SetQuestionNameAlertDialogState
extends ConsumerState<SetQuestionNameAlertDialog> {
final TextEditingController questionNameController = TextEditingController();
final riverPodModelProvider2 =
ChangeNotifierProvider((ref) => RiverpodModel());
#override
Widget build(
BuildContext context,
) {
final questionNameRef = ref.watch(riverPodModelProvider2);
return AlertDialog(
title: Text("Name of Question Set",
style: TextStyle(
color: Colors.blue[400],
fontSize: 20,
fontWeight: FontWeight.w600)),
content: TextField(
controller: questionNameController,
),
actions: [
Center(
child: ElevatedButton(
onPressed: () {
setState(() {
questionNameRef.addTitleT(questionNameController.text) ;
});
print(questionNameRef.questionSet.first.title);
Navigator.pop(context);
},
child: const Text("Save"))),
],
);
}
}
`
This is the page where the question title is shown as a list. However, for some reason it is not showing.
class QuestionSetPage extends ConsumerStatefulWidget {
#override
_QuestionSetPageState createState() => _QuestionSetPageState();
}
class _QuestionSetPageState extends ConsumerState<QuestionSetPage> {
final riverPodModelProvider =
ChangeNotifierProvider((ref) => RiverpodModel());
#override
Widget build(BuildContext context) {
final questionSetRef = ref.watch(riverPodModelProvider);
return Scaffold(
appBar: AppBar(
elevation: 0,
backgroundColor: Colors.grey[50],
centerTitle: true,
actions: [
IconButton(
onPressed: () {
setState(() {
setQuestionNameMethod(context);
// modalSheetMethod(context);
});
},
icon: Icon(
Icons.add,
size: 25,
color: Colors.blue[400],
))
],
),
body: Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
"Question Set List",
style: TextStyle(
color: Colors.blue[400],
fontSize: 30,
fontWeight: FontWeight.w600),
),
),
Expanded(
child: ListView.builder(
itemCount: questionSetRef.count,
itemBuilder: (BuildContext context, int index) {
return Tiles(
title: questionSetRef.questionSet[index].title,
);
}),
)
],
),
);
}
}
class Tiles extends StatelessWidget {
String title;
Tiles({required this.title});
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.fromLTRB(15, 3, 15, 3),
child: Material(
elevation: 2,
child: GestureDetector(
onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (context) {
return QuestionSetsQuestionPage();
}));
},
child: ListTile(
title: Text(
title,
style: TextStyle(
color: Colors.blue[400],
fontSize: 17,
fontWeight: FontWeight.w400),
),
tileColor: Colors.white,
// subtitle: Text(
// "${questionSets[index].numberOfQuestions} number of questions",
// ),
leading: const Icon(
Icons.add_box_outlined,
size: 30,
),
),
),
),
);
}
}
Want to change the color of Icon when tapped. By default if item if already favorite, Icon is red while others are of default color.
If user taps on Icon to make it favorite or Unfavorite, I want to change the color after update.
new ListTile(
trailing: InkWell(
child: Icon(Icons.share),
),
leading: InkWell(
onTap: () {
snapshot.data[index].isFavorite == 0
? makeFavorite(snapshot.data[index].id)
: makeUnfavorite(
snapshot.data[index].id);
},
child: snapshot.data[index].isFavorite == 1
? Icon(
Icons.favorite,
color: Colors.red,
)
: Icon(Icons.favorite)),
title: new Text(snapshot.data[index].body,
style: new TextStyle(
fontWeight: FontWeight.bold, fontSize: 14.0)),
),
Create an Statefull widget for change it's state
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('Title'),
),
body: new ListView.builder(itemBuilder: (context, index) {
return new ListItem();
}),
);
}
class ListItem extends StatefulWidget {
#override
State<StatefulWidget> createState() => new _ItemView();
}
class _ItemView extends State<ListItem>{
bool isFavorite = false;
#override
Widget build(BuildContext context) {
return new ListTile(
trailing: InkWell(
child: Icon(Icons.share),
),
leading: InkWell(
onTap: () {
isFavorite = !isFavorite;
setState(() {
});
},
child: isFavorite ? Icon(
Icons.favorite,
color: Colors.red,
): Icon(Icons.favorite)),
title: new Text('Your Text',
style: new TextStyle(
fontWeight: FontWeight.bold, fontSize: 14.0)),
);
}
}
Solved the problem this way (Updated Code)
Code of List Tile
new ListTile(
trailing: InkWell(
child: Icon(Icons.share),
),
leading: InkWell(
onTap: () {
snapshot.data[index].isFavorite == 0
? makeFavorite(
snapshot.data[index].id, index)
: makeUnfavorite(
snapshot.data[index].id, index);
},
child: (indexes[index] == 1)
? Icon(
Icons.favorite,
color: Colors.red,
)
: Icon(Icons.favorite)),
title: new Text(snapshot.data[index].body,
style: new TextStyle(
fontWeight: FontWeight.bold, fontSize: 14.0)),
),
Functions for changing state
makeFavorite(int id, int index) {
// operations to be performed
// end of operations to be performed
setState(() {
indexes[index] = 1;
});
}
use setState inside onTap function and assign color there.