I have an issue with ListView.builder on Flutter. Whenever I filter elements by a string pattern, I'm always getting the wrong items rendering on the list, although the expected length is correct. Here is my code:
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);
},
);
},
);
}
And now the controller (listaLinhas is an ObservableList):
@action
filtra(String v) {
isFiltroFavorito = false;
listaLinhas = listaLinhasAux
.where((i) => '${i.codLinha} ${i.nomeLinha}'
.toLowerCase()
.contains(v.toLowerCase()))
.toList()
.asObservable();
}
Example 1: if I have a list such as [Banana, Mango, Avocado, Watermelon, Melon], if I filter by "melon", Banana and Mango are shown!
Example 2: on the same list, if I filter by "mango", Banana is shown.
My filtra method is working properly and the expected results are being filtered and being showed on the console. My Flutter version is 2.5.1.
CodePudding user response:
My suggestion is that you need to use keys when building LinhaItemWidget
(add key to its' constructor and pass ValueKey
with item's id or something like that into it).
Thing is that flutter tries to optimize rendering process and it reuses old widgets instead of rebuilding everything from scratch (this is exactly what it looks like based on your examples with fruits). To better understand what's going on check out this video.