ListView.build 过滤后呈现错误的元素
ListView.build rendering wrong elements after filter
我对 Flutter 上的 ListView.builder 有疑问。每当我按字符串模式过滤元素时,尽管预期的长度是正确的,但我总是在列表中呈现错误的项目。这是我的代码:
Widget build(BuildContext context) {
return Scaffold(
appBar: PreferredSize(
preferredSize: const Size.fromHeight(105.0),
child: Observer(builder: (_) {
return Column(
children: <Widget>[
searchBar(), // StreamBuilder
filtroFavorito(), // StreamBuilder
],
);
})),
body: SafeArea(
child: AppDefaultPadding(
child: Observer(builder: (_) => buildListView()),
),
),
);
}
searchBar() {
return AppBar(
title: !controller.isSearching
? Text(widget.title)
: Container(
height: 40.0,
child: TextField(
onChanged: (String? text) {
controller.filtra(text != null ? text : '');
print("First text field: $text");
},
decoration: new InputDecoration(
filled: true,
fillColor: Colors.white.withOpacity(0.5),
prefixIcon: new Icon(
Icons.search,
color: Colors.white,
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: const BorderSide(
width: 0, style: BorderStyle.none)),
enabledBorder: UnderlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide:
BorderSide(color: Colors.transparent, width: 0),
),
focusedBorder: UnderlineInputBorder(
borderSide:
BorderSide(color: Colors.transparent, width: 0),
),
contentPadding:
EdgeInsets.only(left: 15, bottom: 0, top: 7, right: 15),
hintText: 'Pesquisa...',
hintStyle: TextStyle(fontSize: 17, color: Colors.white)),
)),
actions: <Widget>[
IconButton(
icon: Icon(
!controller.isSearching ? Icons.search : Icons.close,
color: Colors.white,
),
onPressed: () {
controller.isSearching
? controller.isSearching = false
: controller.isSearching = true;
controller.filtra('');
//_searchPressed();
},
)
],
);
}
buildListView() {
return ListView.builder(
itemCount: controller.listaLinhas.length,
itemBuilder: (context, i) {
var item = controller.listaLinhas[i];
return LinhaItemWidget(
linha: item,
onChange: (bool val) {
print('novo valor para o elemento ${item.codLinha} : $val');
controller.setaFavorito(i, val);
},
onClick: (LinhaModel val) {
if (item.sentido!.length > 1) {
controller.sentidoSelecionado = null;
popup(item);
} else
controller.abreFormulario(item);
},
);
},
);
}
现在是控制器(listaLinhas 是一个 ObservableList):
@action
filtra(String v) {
isFiltroFavorito = false;
listaLinhas = listaLinhasAux
.where((i) => '${i.codLinha} ${i.nomeLinha}'
.toLowerCase()
.contains(v.toLowerCase()))
.toList()
.asObservable();
}
示例 1:如果我有一个列表,例如 [Banana, Mango, Avocado, Watermelon, Melon],如果我按“melon”过滤,则会显示 Banana 和 Mango!
示例 2:在同一个列表中,如果我按“芒果”过滤,则会显示香蕉。
我的 filtra 方法运行正常,预期的结果正在过滤并显示在控制台上。我的 Flutter 版本是 2.5.1.
我的建议是在构建 LinhaItemWidget
时需要使用键(将键添加到它的构造函数中并将 ValueKey
与项目的 ID 或类似的东西传递给它)。
事实是 flutter 试图优化渲染过程,它重用旧的小部件而不是从头开始重建所有东西(这正是基于你的水果示例的样子)。
要更好地了解发生了什么,请查看 this video.
我对 Flutter 上的 ListView.builder 有疑问。每当我按字符串模式过滤元素时,尽管预期的长度是正确的,但我总是在列表中呈现错误的项目。这是我的代码:
Widget build(BuildContext context) {
return Scaffold(
appBar: PreferredSize(
preferredSize: const Size.fromHeight(105.0),
child: Observer(builder: (_) {
return Column(
children: <Widget>[
searchBar(), // StreamBuilder
filtroFavorito(), // StreamBuilder
],
);
})),
body: SafeArea(
child: AppDefaultPadding(
child: Observer(builder: (_) => buildListView()),
),
),
);
}
searchBar() {
return AppBar(
title: !controller.isSearching
? Text(widget.title)
: Container(
height: 40.0,
child: TextField(
onChanged: (String? text) {
controller.filtra(text != null ? text : '');
print("First text field: $text");
},
decoration: new InputDecoration(
filled: true,
fillColor: Colors.white.withOpacity(0.5),
prefixIcon: new Icon(
Icons.search,
color: Colors.white,
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: const BorderSide(
width: 0, style: BorderStyle.none)),
enabledBorder: UnderlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide:
BorderSide(color: Colors.transparent, width: 0),
),
focusedBorder: UnderlineInputBorder(
borderSide:
BorderSide(color: Colors.transparent, width: 0),
),
contentPadding:
EdgeInsets.only(left: 15, bottom: 0, top: 7, right: 15),
hintText: 'Pesquisa...',
hintStyle: TextStyle(fontSize: 17, color: Colors.white)),
)),
actions: <Widget>[
IconButton(
icon: Icon(
!controller.isSearching ? Icons.search : Icons.close,
color: Colors.white,
),
onPressed: () {
controller.isSearching
? controller.isSearching = false
: controller.isSearching = true;
controller.filtra('');
//_searchPressed();
},
)
],
);
}
buildListView() {
return ListView.builder(
itemCount: controller.listaLinhas.length,
itemBuilder: (context, i) {
var item = controller.listaLinhas[i];
return LinhaItemWidget(
linha: item,
onChange: (bool val) {
print('novo valor para o elemento ${item.codLinha} : $val');
controller.setaFavorito(i, val);
},
onClick: (LinhaModel val) {
if (item.sentido!.length > 1) {
controller.sentidoSelecionado = null;
popup(item);
} else
controller.abreFormulario(item);
},
);
},
);
}
现在是控制器(listaLinhas 是一个 ObservableList):
@action
filtra(String v) {
isFiltroFavorito = false;
listaLinhas = listaLinhasAux
.where((i) => '${i.codLinha} ${i.nomeLinha}'
.toLowerCase()
.contains(v.toLowerCase()))
.toList()
.asObservable();
}
示例 1:如果我有一个列表,例如 [Banana, Mango, Avocado, Watermelon, Melon],如果我按“melon”过滤,则会显示 Banana 和 Mango!
示例 2:在同一个列表中,如果我按“芒果”过滤,则会显示香蕉。
我的 filtra 方法运行正常,预期的结果正在过滤并显示在控制台上。我的 Flutter 版本是 2.5.1.
我的建议是在构建 LinhaItemWidget
时需要使用键(将键添加到它的构造函数中并将 ValueKey
与项目的 ID 或类似的东西传递给它)。
事实是 flutter 试图优化渲染过程,它重用旧的小部件而不是从头开始重建所有东西(这正是基于你的水果示例的样子)。 要更好地了解发生了什么,请查看 this video.