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.