Dynamic adding and removing elements from Widget list - flutter

I want to create a list of widgets(TextFormField) in which I can add a new element with button Add, and remove any element with the button next to that element. So I would have unknown number of TextFormFields in array and would be able to add a new one, and destroy any one TextFormField.
I was able to make adding of new TextFormFields but removing only works if I want to remove last one.
Is there any way to determine the index of removeButton that was clicked?
List<Widget> proba = new List<Widget>();
List<TextEditingController> _controllers = new List<TextEditingController>();
...
IconButton(
icon: Icon(Icons.add_circle_outline),
onPressed: () {
setState(() {
_controllers.add(new TextEditingController());
});
setState(() {
proba.add(Row(
children: [
Icon(Icons.radio_button_unchecked),
Expanded(
child: TextFormField(
controller: _controllers[_controllers.length - 1],
decoration:
InputDecoration(hintText: "Add text..."),
),
),
IconButton(
icon: Icon(Icons.delete),
onPressed: () {
setState(() {
_controllers.removeAt(_controllers.length - 1);
proba.removeAt(proba.length - 1);
});
},
)
],
));
});
},
),
Adding works fine. The code removes last element but I would like to remove the element whose button was clicked.

I think you could use a ListView (for example with the builder constructor), so that each Row is a ListTile. The itemBuilder builds the item and you have access to the index. It would look something like this:
int itemCount = 3;
ListView.builder(
itemCount: _counter,
itemBuilder: (context, index) {
return ListTile(
leading: Icon(Icons.radio_button_unchecked),
title: TextFormField(),
trailing: IconButton(
onPressed: () {
setState(() {
_counter--;
});
},
icon: Icon(Icons.delete),
),
);
},
),
In the setState Method in the onPressed property you have access to the index. In the example the ListView takes care to create the ListTiles based on the itemCount. You might want to create a list of objects instead of just the int itemCount to store data (maybe the text in the TextFormField). But you can still delete the item based on the index from the itemBuilder: values.deleteAt(index).
Have a look at the docs for the ListView and the ListTile classes:
https://api.flutter.dev/flutter/widgets/ListView-class.html
https://api.flutter.dev/flutter/material/ListTile-class.html

Related

ListView.builder and Hive database, misaligned index when deleted an entry

I have a listView.builder building a list for me using information from Hive database.
If I create 3 entries (0, 1, 2), the builder will be like this (0,1,2). If Idelete the (1) hive database keys become this (0,2), but the index generated by the listviewBuilder becomes this (0,1). misaligning the data.
Each entry is an instance of a Person object, just so you guys know
What is the solution? There is a function to recreate the Keys without holes?
Created DB and ListView Builder
Deleted the second card
return ListView.builder(
itemCount: peopleList.length,
itemBuilder: (context, index) {
return Card(
child: Container(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ListTile(
leading: Icon(
(Icons.person),
color: Colors.teal,
),
title: Text(
" ${peopleList[index].name} (${peopleList[index].vehicleType} ${peopleList[index].partieRole} ) ${isDriver(index)}"),
subtitle: Text(
'Injured: ${parseBool(peopleList[index].isInjured)}, transported to hospital: ${parseBool(peopleList[index].isTransported)} ${isCitated(index)} '),
trailing: IconButton(
icon: buildPopMenu(index, peopleList),
onPressed: () {},
),
onTap: () {
print(index);
},
)
],
),
),
);
},
);
the floating button creates showDialog that adds an object to the Hive db
box.add(Person);
so i understand you , you need first to give each item a keyValue based on the last item in your HiveBox and when you want to delete or update some items you will do that based on your keyValue and not based on the index of your ListViewBuilder
this code for adding items
void addNewOrder(NewOrder order) {
final ordersBox = Hive.box('order');
var lengthBox = Hive.box('order').values;
if (lengthBox.isEmpty) {
ordersBox.put(1, order);
print('the box is empty');
} else {
lengthBox.forEach((item) => allorder.add(item.key));
int lastElement = allorder.last;
int newElement = lastElement + 1;
ordersBox.put(newElement, order);
allorder.clear();
}
}
and this code is for retrieving the order of items
List<int> productkey = [];
List<dynamic> items = [];
items = Hive.box('order').values.toList();
items.forEach((item) {
productkey.add(item.key);
});
and this line is for deleting put it in the onPressed
Hive.box('order').delete(productkey[index]);
i hope i was clear

How to make an ListView.builder or List index static in Dart Flutter

I am building a flutter app that increment a index value on a list as a result of a user's interaction.
class _CheckoutScreenState extends State<CheckoutScreen>{
List<int> quantity = [1, 2, 1, 6];
void increment(int index){
setState((){
quantity[index] +=1;
});
}
void decrement(int index){
if(quantity[index] != 0){
setState((){
quantity[index] -=1;
});
}
}
#override
Widget build(BuildContext context){
return ListView.builder(
itemBuilder: (BuildContext context, index) {
return quantity[index] != 0 ?
ListTile(
leading: IconButton(
icon: Icon(Icons.remove),
onPressed: (){
decrement(index);
}
),
trailing: IconButton(
icon: Icon(Icons.add),
onPressed: (){
increment(index);
}
),
title: Text('${quantity[index].toString()} products')
)
}
)
}
}
When I run this code, and then remove an item (once an item is 0, it is not displayed on the screen), the index is shifted as ListView.Builder rebuilds.
For example, if I removed the item (making its quantity 0) at index 2, the index should look like
[1,2,0,6]
but as a result of the list view builder, the index shifts, and the listview builder only has
[1,2,6]
which is discontinous from the main list. How do I make this index static in listView builder so it won't be shifted whenever it needs to be rebuilt
Replace List with a Map and display the current index quantity as a Map, that way each map key represents a unique identifier

Flutter display sql data in ListTile instead of DataCell

I am displaying a list of data fetched from my sql database using DataCell, but I don't really like how it looks and want to switch it to display it using ListTile, this is the code that I am using to display it using DataCell:
return SingleChildScrollView(
scrollDirection: Axis.vertical,
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: DataTable(
columns: [
DataColumn(
label: Text(''),
)
],
rows: _chatUsers
.map(
(user) => DataRow(cells: [
DataCell(
Text(user.firstNameUser),
// Add tap in the row and populate the
// textfields with the corresponding values to update
onTap: () {
// Set the Selected employee to Update
_selectedUser = user;
setState(() {
});
},
),
]),
)
.toList(),
),
),
);
You need to use the ListView widget for this.
There is a lot explained in that API reference section, I think you will be able to rework you app after reading.
So you will have a ListView with the childrenproperty set to smth like
_chatUsers
.map(
(user) =>
ListTile(
title: Text(user.firstNameUser),
// Add tap in the row and populate the
// textfields with the corresponding values to update
onTap: () {
// Set the Selected employee to Update
_selectedUser = user;
setState(() {
});
},
),
)
.toList()

Remove item from PopupMenuButton while it is open

I have a PopupMenuButton that displays some PopupMenuItem<String>'s generated from a List<String>. Each item has a delete button, which removes the String from the list. The problem is that the popup menu doesn't get rebuilt after deleting an item, until it's closed and opened again.
It seems that no matter what I do, even using a GlobalKey and calling key.currentState.setState(), it doesn't cause the popup menu to be rebuilt until it's closed and opened again.
GlobalKey _favoritesKey = new GlobalKey();
PopupMenuButton<String>(
key: _favoritesKey,
icon: Icon(Icons.bookmark_border),
itemBuilder: (context){
List<PopupMenuItem<String>> result = [];
model.favorites.forEach((x){
result.add(PopupMenuItem<String>(value: x, child: Row(
children: [
IconButton(icon: Icon(Icons.delete_outline), onPressed: (){
model.removeFavorite(x);
_favoritesKey.currentState?.setState((){});
setState(() {});
}),
Text(x)
]
)));
});
return result;
},
onSelected: (x){
// Do something with the selected value
},
)
How can I make the popup menu rebuild itself while it is opened?

Can the error "Cannot use local variable before it is declared" be resolved using async functions?

I have an app which adds a Card widget when the user clicks an FAB. There is a list of type Card which is displayed in a SliverGrid which stores a Card added by the user. I wanted to implement a delete function but when i tried that i get the error "Cannot use local variable before it is declared". The Card is added to the list using this code :
int _count = 0;
cardList = List.generate(_count, (int i) =>
new Card(
child: ListTile(
title: Text("project 1"),
trailing: new Listener(
key: new Key(UniqueKey().toString()),
child: new Icon(Icons.remove_circle,
color: Colors.redAccent,),
onPointerDown: (pointerEvent) {}
// deleteNoDo(), //this is the delete function
),
),
)
);
the Card is generated using this code (this button is defined under a Scaffold):
floatingActionButton: FloatingActionButton(
onPressed: () async {
setState(() {
_count += 1;
});
},
heroTag: "btn2",
child: Icon(Icons.add, color: Color(whitecolor),), // this is just a custom color
backgroundColor: Color(redcolor),), // this is just a custom color
this is the delete function:
deleteNoDo(int index) {
debugPrint("Deleted Item!");
setState(() {
cardList.removeAt(index);
});
}
this is the SliverGrid where the cardList is displayed:
SliverGrid(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2
),
delegate: new SliverChildBuilderDelegate((context,index) {
return cardList[index]; // this is where the cards are displayed in a list
},
childCount: cardList.length
)
),
question : can the error "Cannot use local variable before it is declared" be resolved if i use a database since database (CRUD) functions are in async?
The problem is that your cardList variable is local and you are trying to access it inside of an async callback that does not have the scope to access it. Declare cardList inside the initState and initialise it to an empty List and inside build or where ever you want assign that variable actual values and then upon delete button action you can access it.