Reputation: 357
Hello I am new on flutter, I am work in a widget named split row, i use a ListView with three childrens: Text Widget, ListView Builder widget and a Container Widget.
I use the Text for a Tittle, a Map to fill the ListView Builder, and the container to add widgets to ListView Builder, every thing works fine but when i remove an item this item is removed from the map, from the ListView Builder too but not update the values.
To explain myself better I made an example with a textField and a Text, in the first I entered the value of the map key three times and in the second the text is formed with the word data and the map key. This is what happened
As you can see when i delete data1 the item it disappears leaving only data 0, data 2 and data 3, but value 111 it remains and value 333 disappears. obviously values do not correspond to map elements except 000 => data 0
I have already been researching in forums, I have searched stackoverflow, on websites and I have not found anything that helps me
This is the code:
import 'package:flutter/material.dart';
class SplitRow extends StatefulWidget {
@override
_SplitRowState createState() => _SplitRowState();
}
class _SplitRowState extends State<SplitRow> {
int _index = 0;
Map<String, Widget> splitRows = {};
Map records = {};
Map col1 = {};
// Crete Widget Split Row
Widget _splitRow(index) {
return Column(
children: <Widget>[
Container(
color: Colors.white,
height: 40.0,
child: Row(
children: <Widget>[
IconButton(
icon: Icon(Icons.remove_circle),
iconSize: 24,
color: Color(0xFFFF3B30),
onPressed: () {
removeItem(index);
},
),
Container(
width: 100,
child: TextField(
keyboardType: TextInputType.numberWithOptions(
signed: false,
decimal: true,
),
decoration: InputDecoration(
hintText: 'Put direction here...',
border: InputBorder.none,
),
style: TextStyle(fontFamily: 'Lekton'),
onChanged: (value) {
col1["$index"] = value;
},
),
),
_VerticalDivider(),
Text('data $index'),
],
),
),
_HorizontalDivider(),
],
);
}
// Create Widget Add Row
Widget _addRow() {
return Container(
height: 40,
color: Colors.white,
child: Row(
children: <Widget>[
Container(
height: 40,
color: Colors.white,
child: IconButton(
icon: Icon(Icons.add_circle),
iconSize: 24,
color: Color(0xFF34C759),
onPressed: () {
addItem();
},
),
),
Text('New Item')
],
),
);
}
// Add splitRow
void addItem() {
int key = _index;
print('Index antes $_index');
setState(() {
splitRows['$key'] = _splitRow(key);
++_index;
});
print(splitRows);
}
// Remove splitRow
void removeItem(key) {
setState(() {
splitRows.remove('$key');
col1.remove('$key');
});
}
// Save Values
void saveItems() {
records = {'qty': col1};
print(records);
}
@override
void initState() {
super.initState();
splitRows['$_index'] = _splitRow(_index);
++_index;
// splitRows['$_index'] = _addRow(_index);
// ++_index;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Directions'),
actions: <Widget>[
FlatButton(
child: Text(
'Save',
style: TextStyle(color: Colors.white),
),
onPressed: () {
saveItems();
},
)
],
),
backgroundColor: Color(0xFFE5E5EA),
body: Column(
children: <Widget>[
Container(
constraints: BoxConstraints(
minHeight: 40.0,
maxHeight: 500.0,
minWidth: double.infinity,
maxWidth: double.infinity,
),
child: ListView(
children: <Widget>[
Padding(padding: EdgeInsets.only(top: 10.0)),
Text('DIRECTIONS'),
ListView.builder(
shrinkWrap: true,
itemCount: splitRows.length,
itemBuilder: (BuildContext context, int index) {
String key = splitRows.keys.elementAt(index);
return splitRows[key];
},
physics: NeverScrollableScrollPhysics(),
),
_addRow()
],
))
],
));
}
}
class _HorizontalDivider extends StatelessWidget {
const _HorizontalDivider({
Key key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: Divider(
height: 2.0,
),
);
}
}
class _VerticalDivider extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
height: 20,
width: 2,
margin: EdgeInsets.symmetric(horizontal: 5.0, vertical: 5.0),
child: VerticalDivider(
width: 2.0,
),
);
}
}
I will be eternally grateful for any help or advice you can give me, I have been without a solution for a long time
Upvotes: 1
Views: 2520
Reputation: 5601
ListView.builder build a list of widgets to display, when you delete one widget and update the state ListView.builder just rerun the widgets to build and see that a TextField is already there, comparing the elements it looks like they're the same kind of widget, so it doesnt change anything, also the Textfield doesn't save any state saved so even if there were to rebuild it will just go back 'Put direction here...'. Even if you think you saved the state of a widget inside a Map that doesnt change anything, the widget to build is saved but there is no state. You either add a Key to make them look different or add a Textcontroller to the TextField (it wont save the state, but you can add an initial text value to it)
class MySplitRow extends StatelessWidget{
final Map col1;
final String index;
final VoidCallback onPressed;
MySplitRow({this.col1, this.index, this.onPressed, Key key}) : super(key: key);
@override
Widget build(BuildContext context){
return Column(
children: <Widget>[
Container(
color: Colors.white,
height: 40.0,
child: Row(
children: <Widget>[
IconButton(
icon: Icon(Icons.remove_circle),
iconSize: 24,
color: Color(0xFFFF3B30),
onPressed: onPressed,
),
Container(
width: 100,
child: TextField(
controller: TextEditingController(text: col1["$index"]), //it will check the map col1 and if its not null it will give you the value that was saved at that key
keyboardType: TextInputType.numberWithOptions(
signed: false,
decimal: true,
),
decoration: InputDecoration(
hintText: 'Put direction here...',
border: InputBorder.none,
),
style: TextStyle(fontFamily: 'Lekton'),
onChanged: (value) {
col1["$index"] = value;
},
),
),
const VerticalDivider(width: 10, endIndent: 10, indent: 10), //Works exactly as _VerticalDivider with less code
Text('data $index')
],
),
),
const Divider(color: Colors.white, height: 2) //Works exactly as _HorizontalDivider with less code
],
);
}
}
and inside Listview.builder
(BuildContext context, int index) {
String key = splitRows.keys.elementAt(index);
return MySplitRow(
key: ValueKey<String>(key),
col1: col1,
index: key,
onPressed: () => removeItem(key),
);
}
A few recommendations, prefer to use a Class nstead of a method to build Widgets. Your _HorizontalDivider and _VerticalDivider could be build without wrapping a Divider in a Container
Upvotes: 3