How to search JSON items inside ListView flutter? - flutter

I decided not to use the searchdelegate but to use the regular search in JSON (cause I need to solve this problem in the near 2 hours). The problem is that everything seems to work well (I have no errors in the stacktrace), but I just can't understand why the search does not work. I decided that it was necessary to use the Future inside the search widget, but this also did not give any results. Can someone point me to what exactly is going wrong? Maybe I need to create an array to add the search results too? I use not just an ordinary JSON, but it goes through a sorting algorithm and I have seen in other solutions that people use a list of elements, and then fetch one element from all: like this - Future <List<Post>> fetchAllPosts and Future<Post> fetchPost. But I'm doing this:
class MarshrutesPage extends StatefulWidget {
final int ttId;
MarshrutesPage({this.ttId});
#override
_MarshrutesPageState createState() => _MarshrutesPageState();
}
class _MarshrutesPageState extends State<MarshrutesPage> {
Box<RouteWithStops> favoriteRoutesBox;
TransportService service = getIt<TransportService>();
#override
void initState() {
super.initState();
favoriteRoutesBox = Hive.box(favoritesBox);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: [
IconButton(
icon: Icon(
Icons.search,
color: Colors.white,
),
onPressed: () {
showSearch(context: context, delegate: SearchBar());
},
),
IconButton(
icon: Icon(
Icons.favorite,
color: Colors.white,
),
onPressed: () {
Navigator.pushNamed(context, 'favorite');
},
),
],
elevation: 0.0,
backgroundColor: Colors.green,
title: Text(
'numbers',
style: GoogleFonts.montserrat(
color: Colors.white, fontWeight: FontWeight.w400),
),
),
body: FutureBuilder(
future: service.getMarshrutWithStops(widget.ttId),
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
List<RouteWithStops> routes = snapshot.data;
print(routes?.toString());
return (routes == null)
? Center(child: CircularProgressIndicator())
: ValueListenableBuilder(
valueListenable: favoriteRoutesBox.listenable(),
builder: (BuildContext context, value, Widget child) {
return ListView.builder(
itemCount: routes.length + 1,
itemBuilder: (context, index) {
return index == 0
? _searchBar()
: _listItem(index - 1, routes);
},
);
},
);
}),
bottomNavigationBar: BottomAppBar(
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
IconButton(
icon: Icon(
Icons.message,
color: Colors.grey,
),
tooltip: 'to devs',
onPressed: () async {
await launch("mailto: #gmail.com");
}),
IconButton(
icon: Icon(Icons.home, color: Colors.grey), onPressed: () {}),
IconButton(
icon: Icon(Icons.notifications, color: Colors.grey),
onPressed: () {}),
IconButton(
icon: Icon(Icons.map, color: Colors.grey), onPressed: () {}),
]),
),
);
}
Widget getIcon(int index) {
if (favoriteRoutesBox.containsKey(index)) {
return Icon(Icons.favorite, color: Colors.red);
}
return Icon(Icons.favorite_border);
}
void onFavoritePress(int index) {
List<RouteWithStops> routes;
if (favoriteRoutesBox.containsKey(index)) {
favoriteRoutesBox.delete(index);
return;
}
favoriteRoutesBox.put(index, routes[index]);
}
_listItem(index, List<RouteWithStops> routes) {
return ListTile(
title: Text(routes[index].route.mrTitle),
leading: Text(
routes[index].route.mrNum,
style: TextStyle(color: Colors.green, fontSize: 20),
),
trailing: IconButton(
icon: getIcon(index),
onPressed: () => onFavoritePress(index),
),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => StopsPage(
routeWithStops: routes[index],
)));
},
);
}
_searchBar() {
return FutureBuilder(
future: service.getMarshrutWithStops(widget.ttId),
builder:
(BuildContext context, AsyncSnapshot<List<RouteWithStops>> snapshot) {
List<RouteWithStops> routes = snapshot.data;
print('test1');
return (routes == null)
? Center(child: CircularProgressIndicator()) : Padding(
padding: const EdgeInsets.all(8),
child: TextField(
decoration: InputDecoration(hintText: 'Search',
hoverColor: Colors.green,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
prefixIcon: Icon(Icons.search),),
onChanged: (text) {
text = text.toLowerCase();
setState(() {
routes = routes.where((element) {
var routesTitle = element.route.mrTitle.toLowerCase();
return routesTitle.contains(text);
}).toList();
print('test2');
});
},
),
);
},
);
}
}
Also i think that it dosen't work because I use the parametrs - ttId.
The algorithm I use
Future<List<RouteWithStops>> getMarshrutWithStops(int ttId) async {
if (routesbyTransportType.isEmpty) {
await fetchTransportWithRoutes();
}
List<Routes> routes = routesbyTransportType[ttId].routes;
List<ScheduleVariants> variants = [];
variants.addAll(await api.fetchSchedule());
List<RouteWithStops> routesWithStops = [];
for (Routes route in routes) {
final routeWithStops = RouteWithStops();
routesWithStops.add(routeWithStops);
routeWithStops.route = route;
routeWithStops.variant =
variants.where((variant) => variant.mrId == route.mrId).first;
}
return routesWithStops;
}
Stacktrace after putting several letters in search bar:
W/IInputConnectionWrapper(25362): beginBatchEdit on inactive InputConnection
W/IInputConnectionWrapper(25362): endBatchEdit on inactive InputConnection
I/flutter (25362): [Instance of 'RouteWithStops', Instance of 'RouteWithStops', Instance of 'RouteWithStops', Instance of 'RouteWithStops', Instance of 'RouteWithStops', Instance of 'RouteWithStops', Instance of 'RouteWithStops', Instance of 'RouteWithStops', Instance of 'RouteWithStops', Instance of 'RouteWithStops', Instance of 'RouteWithStops', Instance of 'RouteWithStops', Instance of 'RouteWithStops', Instance of 'RouteWithStops', Instance of 'RouteWithStops', Instance of 'RouteWithStops', Instance of 'RouteWithStops', Instance of 'RouteWithStops']
I/flutter (25362): test2
I/flutter (25362): Basic Vk9MR0E6TkVU

routes = routes.where((element) {
This is setting your local variable named "routes". It won't do anything. I cannot really tell what you wanted to do, maybe set a variable of your state?

The problem was solved quite simply, I just needed to be a little more careful: in general, I need to declare two arrays at the top in the widget tree itself:
List<RouteWithStops> _routes = [];
List<RouteWithStops> _routesToDisplay = [];
The first array is responsible for all the elements of the list that need to be displayed under the search bar. The second array is needed so that when you set up the search and enter a certain name / value there, then it does not leave just the value you just entered, but loads others as well. For example, you are looking for a title by author: Tolstoy "War and Peace". If you leave it as it is, without adding a second array, then you will still have Tolstoy "War and Peace" at the bottom under the search bar as the only item until the next reload. It doesn't have to be that way.
To avoid this, you need to do the following:
#override
void initState() {
service.getMarshrutWithStops(widget.ttId).then((value) {
setState(() {
_routes.addAll(value);
_routesToDisplay = _routes;
});
});
super.initState();
}
Data from the first array is written to the empty second array, so the search becomes, so to speak, dynamic and now all the author's books are displayed and when the author's surname is erased from the search, the rest of the array is loaded, and not just what was typed in the search.
In the tree of widgets, we do everything the same as in my question, with the only difference that in _listItem we pass only index and nothing else, and in order for the loading to occur correctly, I just need to add the load in the main widget in front of ListView.builder, using ternary operators:
body: (_routes == null) ? CircularProgressIndicator() : ValueListenableBuilder(
builder: (BuildContext context, value, Widget child) {
return ListView.builder(
itemCount: _routesToDisplay.length + 1,
itemBuilder: (context, index) {
return index == 0
? _searchBar()
: _listItem(index-1);
},
);
P.S There is no need for a ValueListenableBuilder, I started to save the selected elements in the database, only this line is needed to load the list items: (_routes == null)? CircularProgressIndicator (): ListView.builder
Also, in the widgets for displaying the list and search, instead of _routes, add _routesToDisplay and it turns out like this:
_listItem(index) {
return ListTile(
title: Text(_routesToDisplay[index].route.mrTitle),
leading: Text(
_routesToDisplay[index].route.mrNum,
style: TextStyle(color: Colors.green, fontSize: 20),
),
trailing: IconButton(
icon: getIcon(index),
onPressed: () => onFavoritePress(index, _routes),
),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => StopsPage(
routeWithStops: _routes[index],
)));
},
);
}
_searchBar() {
print('test1');
return Padding(
padding: const EdgeInsets.all(8),
child: TextField(
decoration: InputDecoration(
hintText: 'Search',
hoverColor: Colors.green,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
prefixIcon: Icon(Icons.search),
),
onChanged: (text) {
text = text.toLowerCase();
setState(() {
print('object');
_routesToDisplay = _routes.where((element) {
var routesTitle = element.route.mrTitle.toLowerCase();
print(routesTitle);
return routesTitle.contains(text);
}).toList();
});
},
),
);
}
Of course, you can remove all prints and use it as you want. This is quite simple task, but I am noob and wasn't careful as much as it needed.

Related

Is there a way to make new pages without manually creating classes for it? flutter

I have an empty page lets call it "class list" which the user can create a new "class" in the listview by clicking a button and giving it a name and when you click on that "class" it opens a new page with that name on the app bar, lets call this new page the "students lists", now you can create another list in it, and I used hive to store the data.
now the problem is when you create a list of students in the "student list" page when you come back and go to "class lists" and click on another "class", the student list you created earlier will appear for every "class" page.
the reason for this is because I have created a class(the syntax) for student list page and it will show for every "class".
since its the user who creates "classes" and I haven't putted any limitation on it, I cant create infinite classes(the syntax) for the pages created so what can I do?
class list page
inside class 1
inside class 2
here is the codes:
the "list class" codes:
import 'package:attendance/data/database.dart';
import 'package:flutter/material.dart';
import 'package:flutter_speed_dial/flutter_speed_dial.dart';
import 'package:attendance/insideList.dart';
import 'package:hive_flutter/hive_flutter.dart';
class lists extends StatefulWidget {
const lists({super.key});
#override
State<lists> createState() => _listsState();
}
class _listsState extends State<lists> {
final _myBox = Hive.box('mybox');
ListDataBase db = ListDataBase();
late TextEditingController _textController;
#override
void initState() {
if (_myBox.get("NAMES") == null) {
db.InitialData();
} else {
db.LoadData();
}
super.initState();
_textController = TextEditingController();
}
#override
void dispose() {
_textController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
db.Items.sort();
return Scaffold(
body: db.Items.length > 0
? ListView.separated(
itemCount: db.Items.length,
itemBuilder: (_, index) {
return ListTile(
leading: const Icon(Icons.school),
trailing: const Icon(Icons.arrow_forward),
title: Center(child: Text(db.Items[index])),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: ((context) =>
InsideList(db.Items[index]))));
},
onLongPress: (() async {
await showDialog(
context: context,
builder: ((context) {
return AlertDialog(
title: const Text(
"Are you sure you want to delete this class?",
style: TextStyle(fontSize: 15),
),
actions: [
TextButton(
child: Text("cancel"),
onPressed: (() {
Navigator.pop(context);
})),
TextButton(
child: Text('Delete'),
onPressed: () {
setState(() {
db.Items.removeAt(index);
db.UpdateDataBase();
Navigator.pop(context);
});
},
),
],
);
}));
}),
);
},
separatorBuilder: (BuildContext context, int index) =>
const Divider(
color: Colors.black,
),
)
: const Center(
child: Text("You currently have no classes. 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.school),
label: "add class",
onTap: () async {
final result = await showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: const Text('Add a new class'),
content: TextField(
controller: _textController,
autofocus: true,
decoration: const InputDecoration(
hintText: "Enter the name of the class."),
),
actions: [
TextButton(
child: Text('Cancel'),
onPressed: () {
Navigator.pop(context);
},
),
TextButton(
child: Text('Add'),
onPressed: () {
Navigator.pop(context, _textController.text);
db.UpdateDataBase();
_textController.clear();
},
),
],
);
},
);
if (result != null) {
result as String;
setState(() {
db.Items.add(result);
});
}
},
)
],
),
);
}
}
the student page codes:
import 'package:attendance/data/StudentsDatabase.dart';
import 'package:flutter/material.dart';
import 'package:flutter_speed_dial/flutter_speed_dial.dart';
import 'package:hive_flutter/hive_flutter.dart';
class InsideList extends StatefulWidget {
final String name;
InsideList(this.name);
#override
State<InsideList> createState() => _InsideListState();
}
class _InsideListState extends State<InsideList> {
final _myBox = Hive.box('mybox2');
StudentsDatabase db = StudentsDatabase();
late TextEditingController _textController;
#override
void initState() {
if (_myBox.get("NAMES") == null) {
db.InitialData();
} else {
db.LoadData();
}
super.initState();
_textController = TextEditingController();
}
void _selectRadio(int index, int? val) {
setState(() {
db.SelectedRadio[index] = val ?? 0;
});
}
#override
void dispose() {
_textController.dispose();
super.dispose();
}
Widget build(BuildContext context) {
db.Students.sort();
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: db.Students.length > 0
? ListView.separated(
itemCount: db.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: db.SelectedRadio[index],
onChanged: (val) {
_selectRadio(index, val);
db.UpdateDataBase();
}),
Radio(
activeColor: Colors.red,
value: 1,
groupValue: db.SelectedRadio[index],
onChanged: (val) {
_selectRadio(index, val);
db.UpdateDataBase();
}),
],
),
),
title: Center(child: Text(db.Students[index])),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: ((context) =>
InsideList(db.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(() {
db.Students.removeAt(index);
db.UpdateDataBase();
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);
db.UpdateDataBase();
_textController.clear();
},
),
],
);
},
);
if (result != null) {
result as String;
setState(() {
db.Students.add(result);
db.SelectedRadio.add(0);
});
}
},
),
],
),
);
}
}
From what i am understanding, You will need to connect the student list with every single class separately. To show a quick example.
List<ClassRoom> firstPageList = [];
class ClassRoom {
ClassRoom({required this.name});
final String name;
List<Student> students = [];
}
class Student {
Student({required this.studentName});
final String studentName;
}
Kinda like this. Then when a new ClassRoom class added to the list, Show the name on the list. And pass that ClassRoom to the next page.
And on the next page, Use that ClassRoom instance, and add all the students inside the list of that ClassRoom.
Now for every page you will have a different class and for every class, you will have it's seperate Student.
After that, all you need to do is, handle the states of every class properly. And setup a way to store them properly.

how to have two radio buttons in one row in each listtile item created by the user? in flutter

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.

TextField (in flutter) getting filled with previously filled values (unintentionally)

On click of floatingActionButton in main.dart file, I'm calling a dialog widget.
main.dart
late ShoppingListDialog dialog;
#override
void initState() {
dialog = ShoppingListDialog();
super.initState();
}
floatingActionButton: FloatingActionButton(
child: Icon(
Icons.add,
),
backgroundColor: Colors.pink,
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) => dialog.buildDialog(
context, ShoppingList(id: 0, name: '', priority: 0), true),
);
},
),
shopping_list_dialog.dart
class ShoppingListDialog {
final txtName = TextEditingController();
final txtPriority = TextEditingController();
Widget buildDialog(BuildContext context, ShoppingList list, bool isNew) {
DbHelper helper = DbHelper();
if (!isNew) {
txtName.text = list.name;
txtPriority.text = list.priority.toString();
}
return AlertDialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(30.0)),
title: Text((isNew) ? 'New shopping list' : 'Edit shopping list'),
content: SingleChildScrollView(
child: Column(
children: [
TextField(
controller: txtName,
onTap: () {},
decoration: InputDecoration(hintText: 'Shopping List Name')),
TextField(
controller: txtPriority,
keyboardType: TextInputType.number,
decoration:
InputDecoration(hintText: 'Shopping List Priority (1-3)'),
),
TextButton(
child: Text('Save Shopping List'),
onPressed: () {
list.name = txtName.text;
list.priority = int.parse(txtPriority.text);
helper.insertList(list);
Navigator.pop(context);
},
),
],
),
),
);
}
}
TextField is empty, the first time (showing the hint text). But the second time onwards, it gets filled with the last used values, while I intend them to be empty. Like in the image below, the second time when I hit on floatingActionButton to add something, it gets filled with "fruits"(values I had used previously).
TextField should start empty but it's getting filled with previous used values.
Here is a basic solution for you
TextButton(
child: Text('Save Shopping List'),
onPressed: () {
list.name = txtName.text;
list.priority = int.parse(txtPriority.text);
// Clear TE Controller
txtName.clear();
txtPriority.clear();
// Insert
helper.insertList(list);
Navigator.pop(context);
},
),

dynamically created checkbox dart/flutter

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((){});

Flutter:1 positional argument(s) expected, but 0 found

I am working on a flutter project, which separated the body: widget from the main.dart and placed it inside a new statefull widget with the file name todu_list.dart now i am trying to call it back to main.dart file body: SingleChildScrollView(child: Lists()), and am getting this error
1 positional argument(s) expected, but 0 found.
Try adding the missing arguments.
I have gone through alot of similar questions here on StackOverFlow and realised i am supposed to add an argument inside the brackets "()" but i don't know which of the function from my Lists widget that i am expected to call there
Below is the "Lists" widget code
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
import '../models/todus.dart';
import 'package:intl/intl.dart';
import 'package:sqflite/sqflite.dart';
import '../models/database_helper.dart';
class Lists extends StatefulWidget {
final Function addTx;
Lists(this.addTx);
#override
_ListsState createState() => _ListsState();
}
class _ListsState extends State<Lists> {
final dbHelper = DatabaseHelper.instance;
void _addNewTransaction(BuildContextcontext) {
showModalBottomSheet(
backgroundColor: Colors.white,
isScrollControlled: true,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(25.0))),
context: context,
builder: (_) {
return GestureDetector(
onTap: () {},
// Where i started the code pasting from
child: Padding(
padding: MediaQuery.of(context).viewInsets,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Card(
elevation: 0.000,
child: Container(
padding: EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
TextField(
decoration: InputDecoration(labelText: 'Title'),
controller: _titleController,
autofocus: true,
onSubmitted: null,
// onChanged: (val) {
// titleInput = val;
// },
),
TextField(
decoration: InputDecoration(labelText: 'Description'),
controller: _discriptionController,
onSubmitted: null,
// onChanged: (val) => amountInput = val,
),
Container(
height: 70,
child: Row(
children: [
Text(selectedDateAndTime == null
? 'No Date Choosen'
: DateFormat('MM/dd/yyyy HH:mm')
.format(selectedDateAndTime)
// : DateFormat.yMd()
// .format(_selectedDate),
),
FlatButton(
textColor: Theme.of(context).primaryColor,
child: Icon(Icons.calendar_today),
// onPressed: () async {
// var value = await _selectedTime();
// },
onPressed: () => _selectDayAndTimeL(context),
),
],
),
),
RaisedButton(
child: Text('Save Todo'),
color: Theme.of(context).primaryColor,
textColor: Theme.of(context).textTheme.button.color,
onPressed: _submitData,
),
],
),
),
),
),
),
);
},
);
}
final _titleController = TextEditingController();
final _discriptionController = TextEditingController();
var favorite;
// DateTime _selectedDate;
DateTime selectedDateAndTime;
#override
void dispose() {
super.dispose();
_discriptionController.dispose();
_titleController.dispose();
}
Future _selectDayAndTimeL(BuildContext context) async {
DateTime _selectedDay = await showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2021),
lastDate: DateTime(2030),
builder: (BuildContext context, Widget child) => child);
TimeOfDay _selectedTime = await showTimePicker(
context: context,
initialTime: TimeOfDay.now(),
);
if (_selectedDay != null && _selectedTime != null) {
//a little check
}
setState(() {
selectedDateAndTime = DateTime(
_selectedDay.year,
_selectedDay.month,
_selectedDay.day,
_selectedTime.hour,
_selectedTime.minute,
);
// _selectedDate = _selectedDay;
});
// print('...');
}
List<ItemLists> items = [
ItemLists(
title: 'Best Music of the Year',
description: 'Davido',
favorite: false,
),
ItemLists(
title: 'Best Album Cover design',
description: 'Brighter Press',
favorite: false,
),
void _submitData() {
// if (_amountController.text.isEmpty) {
// return;
// }
final enteredTitle = _titleController.text;
final enteredDescription = _discriptionController.text;
if (enteredTitle.isEmpty) {
return;
}
widget.addTx(
enteredTitle,
enteredDescription,
selectedDateAndTime,
);
Navigator.of(context).pop();
}
#override
Widget build(BuildContext context) {
return SizedBox(
child: Container(
child: ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemBuilder: (context, index) {
return Dismissible(
key: ObjectKey(items[index]),
background: Container(
color: Colors.red,
),
child: Card(
child: ListTile(
leading: new IconButton(
icon: Icon(
Icons.check,
color:
items[index].favorite ? Colors.green : Colors.grey,
),
tooltip: 'Add to Favorite',
onPressed: () {
setState(() {
items[index].favorite = !items[index].favorite;
});
}),
title: Text('${items[index].title}'),
subtitle: Text('${items[index].description}'),
trailing: IconButton(
icon: Icon(Icons.calendar_today),
onPressed: () => _selectDayAndTimeL(context),
),
)),
onDismissed: (direction) {
final String myTitle = items[index].title;
// Remove the item from the data source.
setState(() {
var deletedItems = items.removeAt(index);
Scaffold.of(context).showSnackBar(
SnackBar(
content: Text('$myTitle Deleted'),
action: SnackBarAction(
label: 'Undo',
onPressed: () => setState(
() => items.insert(index, deletedItems),
)),
),
);
});
});
},
itemCount: items.length,
),
),
);
floatingActionButton:
FloatingActionButton(
child: Icon(Icons.add),
onPressed: () => _addNewTransaction(context),
backgroundColor: Colors.redAccent,
);
}
}
You have to give a function as parameter in order to build your widget. This is not a function of your widget that you will be calling but the function addTx that you will be calling from within your Lists widget.
Either remove the parameter or pass a function to solve it.
Example: since your function is expected to have 3 parameters:
widget.addTx(
enteredTitle,
enteredDescription,
selectedDateAndTime,
);
you can create:
void addTitleDescDate(string title, string description, string date) { // NB you should probably use a Date object or epoch time.
print(title);
print(description);
print(date);
}
And you use this function Lists(addTitleDescDate)
As a side note I don't really see the point to have this function as a parameter shared to the Lists widget, but if you want to learn more about function as parameter that is still interesting.