Flutter嵌套模型列表所有元素均存最后添加项的原因排查
问题原因及解决方案
问题现象
循环加载队伍数据并添加到listaEquipesGeral后,列表中所有元素的equipe属性都指向最后一次加载的listaAtletaEquipe数据,所有项的equipe内容完全一致。
核心原因
Dart中列表属于引用类型,你的代码存在两个关键问题:
- 全局的
listaAtletaEquipe是同一个列表实例,每次调用_loadEquipe时只是执行clear()清空原有内容,再添加新数据,内存地址始终不变。 - 循环中
_reg.equipe = listaAtletaEquipe是把这个全局列表的引用赋值给每个TabelaListaEquipes对象,所有_reg的equipe属性都指向同一个内存地址的列表。后续每次修改listaAtletaEquipe的内容,所有引用它的对象都会同步更新,最终所有项都显示最后一次加载的数据。
修复方案
方案1:重构_loadEquipe,返回新的列表实例
不再依赖全局的listaAtletaEquipe,每次加载时创建新列表,从根源避免引用复用:
// 移除全局的listaAtletaEquipe定义 Future<List<TabelaInscricoes>> _loadEquipe(String equipId) async { setState(() { carregandoEquipe = true; }); List<TabelaInscricoes> listaAtletaEquipe = []; // 每次调用创建新列表 TabelaInscricoes _result = TabelaInscricoes(); CollectionReference _dbCollection = FirebaseFirestore.instance .collection('campeonatos') .doc(resultSelect.campId) .collection('divisoes') .doc(resultSelect.divId) .collection('equipes') .doc(equipId) .collection('atletas'); await _dbCollection.orderBy('pos2', descending: false).get().then((QuerySnapshot querySnapshot) async { if (querySnapshot.docs.isNotEmpty) { querySnapshot.docs.forEach((element) { _result = TabelaInscricoes.fromJson(element.data()! as Map<String, dynamic>); if (_result.campId == resultSelect.campId && _result.divId == resultSelect.divId) { _result.id = element.id; _result.filePath = ""; listaAtletaEquipe.add(_result); } }); // 填充运动员信息 for (int x = 0; x < listaAtletaEquipe.length; x++) { for (int y = 0; y < listaAtletas.length; y++) { if (listaAtletaEquipe[x].atletaId.toString() == listaAtletas[y].id.toString()) { listaAtletaEquipe[x].nome = listaAtletas[y].nome; listaAtletaEquipe[x].fotoNome = listaAtletas[y].fotoNome; listaAtletaEquipe[x].filePath = listaAtletas[y].filePath; listaAtletaEquipe[x].dataN = listaAtletas[y].dataN; listaAtletaEquipe[x].fone1 = listaAtletas[y].fone1; listaAtletaEquipe[x].fone2 = listaAtletas[y].fone2; listaAtletaEquipe[x].nTitulo = listaAtletas[y].nTitulo; listaAtletaEquipe[x].info = listaAtletas[y].info; listaAtletaEquipe[x].email = listaAtletas[y].email; } } } // 获取图片URL for (int x = 0; x < listaAtletaEquipe.length; x++) { if (listaAtletaEquipe[x].fotoNome.toString().isNotEmpty) { String url = await MyStorage.getUrl(context, "atletas/${listaAtletaEquipe[x].fotoNome.toString()}"); listaAtletaEquipe[x].filePath = url; } } } setState(() { carregandoEquipe = false; }); }); return listaAtletaEquipe; }
然后修改循环代码:
for (int x=0; x<listaEquipes.length; x++) { List<TabelaInscricoes> equipeData = await _loadEquipe(listaEquipes[x].id.toString()); TabelaListaEquipes _reg = TabelaListaEquipes(); _reg.equipeId = listaEquipes[x].id.toString(); _reg.equipe = equipeData; // 赋值新创建的列表实例 listaEquipesGeral.add(_reg); }
方案2:保留全局列表,赋值时创建副本
如果不想修改_loadEquipe的返回值,可在循环中给_reg.equipe赋值时,创建当前listaAtletaEquipe的副本,避免引用同一实例:
for (int x=0; x<listaEquipes.length; x++) { await _loadEquipe(listaEquipes[x].id.toString()); TabelaListaEquipes _reg = TabelaListaEquipes(); _reg.equipeId = listaEquipes[x].id.toString(); _reg.equipe = List.from(listaAtletaEquipe); // 创建列表副本 listaEquipesGeral.add(_reg); }
注意:方案2仅当
TabelaInscricoes是不可变对象时完全安全,如果TabelaInscricoes后续会被修改,建议给TabelaInscricoes添加copyWith方法实现深拷贝,逐个复制属性。
内容的提问来源于stack exchange,提问作者Rodrigo Costa
相关产品推荐
相关产品推荐

