for ease, I’ve created a DartPad for you to try out.
I would like a simple list view with two text fields where users can add or delete items. The entered data is stored as a Map.
When I click on the ‘Add New’ button, it should create a new list tile. However, when I attempt to delete any item, it seems to delete a different list item. Another issue is that when I add a new item for the second time, the first item I created gets overridden with a blank text field. Could you please check this?
This is the code
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Shift List'),
),
body: ShiftList(),
),
);
}
}
class ShiftList extends StatefulWidget {
@override
_ShiftListState createState() => _ShiftListState();
}
class _ShiftListState extends State<ShiftList> {
Map<String, String> shifts = {
'1': 'Morning',
'2': 'Afternoon',
'3': 'Night',
};
void addShift() {
setState(() {
String newKey = '';
shifts[newKey] = '';
});
}
void deleteShift(String key) {
setState(() {
shifts.remove(key);
});
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Expanded(
child: ListView.builder(
itemCount: shifts.keys.length,
itemBuilder: (context, index) {
String key = shifts.keys.elementAt(index);
String value = shifts[key]!;
final keyController = TextEditingController(text: key);
final valueController = TextEditingController(text: value);
return ListTile(
title: Row(
children: [
Expanded(
child: TextFormField(
controller: keyController,
),
),
SizedBox(width: 10),
Expanded(
child: TextFormField(
controller: valueController,
),
),
],
),
trailing: IconButton(
icon: Icon(Icons.delete),
onPressed: () {
deleteShift(keyController.text);
},
),
);
},
),
),
ElevatedButton(
onPressed: () {
addShift();
},
child: Text('Add Shift'),
),
],
);
}
}
2
Answers
The Issue
The main issue here is the code does not have the capability to transform/change the value assigned to the blank string key into a non-blank string.
Every time you call
void addShift
you are assuming that the last time you called it the "temporary" blank-string entry content/data was already move/update to something likeshifts['4'] = '';
(4 here because you start with 3 items) BUT your code never move/update the blank string entry.The Solution
To resolve this issue you would need to extend the
TextEditingController keyController
to being able to perform a a key swap onshifts
:That will fix the insertion issue and the deletion issue as well.
Notes
I would advice you to change
shifts
fromMap<String, String>
toList<Set<String>>. The
List<>will automatically make your code avoid this extra work of swapping data between "indexes" with the
.add` function.If you’re worried about duplicated "number labels" for the shifts you might as well change to
List<String>
and use the built-in index. To rearrange the indexes you would replaceListView
withReorderableListView
.https://api.flutter.dev/flutter/material/ReorderableListView-class.html
The issue lies in your addShift() function. You are creating an empty key each time with an empty value but you are not updating the hashmap with the new values from the textControllers. Since the map cannot have duplicate keys, whenever u press the add button again, it sees that the previously added key was empty as well so it will just override it as you are again creating just an empty key again instead of creating a unique key. On your UI you are adding the values to your tile widget but the inside the map it has that same empty entry.
You can do a few things to fix this.
Either you can generate keys in order whenever a tile is created so you can first pass them and just update the values.
Create a popup whenever u press to add a button that takes the key and actual value, and then update your hashmap with those so the ListView builder can update and show it. You should not create unnecessary empty entries in the hashmap when the user has not finished adding the data first.
Something like this:
Also, your delete functions seem to be working fine.