Flutter:删除项目时,ListView 内的小部件值不会更新
Flutter: The widget values inside the ListView are not updated when an item is removed
你好,我是 flutter 的新手,我在一个名为 split row 的小部件中工作,我使用一个 ListView 和三个孩子:Text Widget、ListView Builder 小部件和一个 Container Widget。
我使用 Text 作为标题,使用 Map 填充 ListView Builder,使用容器将小部件添加到 ListView Builder,一切正常,但是当我删除一个项目时,该项目从地图中删除,从ListView Builder 也是,但不更新值。
为了更好地解释我自己,我用一个 textField 和一个 Text 做了一个例子,在第一次我输入了 map key 的值三次,在第二次文本是由 word data 和 map key 组成的。事情是这样的
如您所见,当我删除数据 1 时,它消失了,只留下数据 0、数据 2 和数据 3,但值 111 仍然存在,值 333 消失了。显然值不对应地图元素除了000 => data 0
我已经在论坛上进行了研究,在网站上搜索了 Whosebug,但没有找到任何对我有帮助的东西
这是代码:
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,
),
);
}
}
如果您能给我任何帮助或建议,我将永远感激不已,我已经很久没有解决方案了
ListView.builder 构建要显示的小部件列表,当您删除一个小部件并更新状态时 ListView.builder 只需重新运行要构建的小部件并查看 TextField 已经存在,比较元素看起来它们是同一种小部件,所以它不会改变任何东西,文本字段也不会保存任何已保存的状态,所以即使重建它也只会返回 'Put direction here...'。即使您认为您将小部件的状态保存在不会更改任何内容的 Map 中,要构建的小部件也会保存但没有状态。您可以添加一个 Key 使它们看起来不同,或者向 TextField 添加一个 Textcontroller(它不会保存状态,但您可以向其添加一个初始文本值)
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
],
);
}
}
在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),
);
}
一些建议,更喜欢使用一种Class而不是一种方法来构建Widgets。您的 _HorizontalDivider 和 _VerticalDivider 可以在不将分隔线包装在容器中的情况下构建
你好,我是 flutter 的新手,我在一个名为 split row 的小部件中工作,我使用一个 ListView 和三个孩子:Text Widget、ListView Builder 小部件和一个 Container Widget。
我使用 Text 作为标题,使用 Map 填充 ListView Builder,使用容器将小部件添加到 ListView Builder,一切正常,但是当我删除一个项目时,该项目从地图中删除,从ListView Builder 也是,但不更新值。
为了更好地解释我自己,我用一个 textField 和一个 Text 做了一个例子,在第一次我输入了 map key 的值三次,在第二次文本是由 word data 和 map key 组成的。事情是这样的
如您所见,当我删除数据 1 时,它消失了,只留下数据 0、数据 2 和数据 3,但值 111 仍然存在,值 333 消失了。显然值不对应地图元素除了000 => data 0
我已经在论坛上进行了研究,在网站上搜索了 Whosebug,但没有找到任何对我有帮助的东西
这是代码:
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,
),
);
}
}
如果您能给我任何帮助或建议,我将永远感激不已,我已经很久没有解决方案了
ListView.builder 构建要显示的小部件列表,当您删除一个小部件并更新状态时 ListView.builder 只需重新运行要构建的小部件并查看 TextField 已经存在,比较元素看起来它们是同一种小部件,所以它不会改变任何东西,文本字段也不会保存任何已保存的状态,所以即使重建它也只会返回 'Put direction here...'。即使您认为您将小部件的状态保存在不会更改任何内容的 Map 中,要构建的小部件也会保存但没有状态。您可以添加一个 Key 使它们看起来不同,或者向 TextField 添加一个 Textcontroller(它不会保存状态,但您可以向其添加一个初始文本值)
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
],
);
}
}
在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),
);
}
一些建议,更喜欢使用一种Class而不是一种方法来构建Widgets。您的 _HorizontalDivider 和 _VerticalDivider 可以在不将分隔线包装在容器中的情况下构建