首页 > 解决方案 > ListView.build 过滤后渲染错误的元素

问题描述

我对 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:如果我有一个列表,例如[香蕉,芒果,鳄梨,西瓜,甜瓜],如果我按“甜瓜”过滤,则显示香蕉和芒果!

示例 2:在同一个列表中,如果我按“芒果”过滤,则会显示香蕉。

我的 filtra 方法工作正常,预期结果正在被过滤并显示在控制台上。我的 Flutter 版本是 2.5.1。

标签: fluttermobx

解决方案


我的建议是你需要在构建时使用键LinhaItemWidget(将键添加到它的构造函数并传递ValueKey项目的 id 或类似的东西)。

问题是颤振试图优化渲染过程,它重用旧的小部件,而不是从头开始重建所有东西(这正是基于你的水果示例的样子)。为了更好地了解发生了什么,请查看此视频


推荐阅读