Flutter ListView删除异常:总是移除最后一项的问题求助
修复Flutter列表移除指定项时总是删除最后一项的问题
问题根源
- Key的错误使用:你用
ObjectKey(index)作为Widget的Key,当删除列表项后,后续项的索引会重新排列,Flutter的Widget复用机制会把旧Widget绑定到新的索引位置,导致删除操作的索引和实际数据不匹配。 - 状态未同步:
CustomInput的TextEditingController只在initState初始化,当父组件更新widget.text时,控制器内容没有同步更新,导致UI显示和实际数据脱节。
修复步骤
1. 使用稳定唯一的Key
替换基于索引的Key为基于列表项内容的ValueKey,确保每个Widget的Key和数据绑定而非位置:
// 在createNamesInput的itemBuilder中 return ListTile( title: CustomInput( key: ValueKey(names[index]), // 用内容作为唯一标识的Key labelText: "Nome", onChanged: (String changed) => changeName(index, changed), text: names[index], onRemoved: () => removeName(index), ), );
2. 同步CustomInput的控制器内容
在CustomInput的State类中重写didUpdateWidget方法,当父组件传递的widget.text变化时,更新控制器的文本:
@override void didUpdateWidget(covariant CustomInput oldWidget) { super.didUpdateWidget(oldWidget); if (oldWidget.text != widget.text) { inputController.text = widget.text; } }
3. 可选优化:使用实体类存储数据
如果列表项可能重复,建议创建带唯一ID的实体类,避免内容重复导致Key冲突:
class Player { final String id; String name; Player(this.id, this.name); }
后续用ValueKey(player.id)作为Key,确保Key的唯一性不受内容影响。
修改后的完整代码
create_game.dart
import 'package:flutter/material.dart'; import 'package:pontinho/components/custom_input.dart'; class CreateGame extends StatefulWidget { const CreateGame({super.key}); @override State<CreateGame> createState() => _CreateGameState(); } class _CreateGameState extends State<CreateGame> { List<String> names = ['']; void changeName(int nameIndex, String change) { setState(() { names[nameIndex] = change; }); } void removeName(int nameIndex) { print(names); print(nameIndex); setState(() { names.removeAt(nameIndex); }); } ListView createNamesInput() { return ListView.builder( itemCount: names.length, shrinkWrap: true, itemBuilder: (context, index) { return ListTile( title: CustomInput( key: ValueKey(names[index]), labelText: "Nome", onChanged: (String changed) => changeName(index, changed), text: names[index], onRemoved: () => removeName(index), ), ); }, ); } void addName() { setState(() { names.add(''); }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( leading: GestureDetector( onTap: (() => Navigator.pop(context)), child: const Icon( Icons.arrow_back, color: Colors.black, size: 40, ), ), backgroundColor: Colors.white, titleTextStyle: const TextStyle( color: Colors.black, fontSize: 20, ), title: const Text("CRIE SEU JOGO"), ), body: Padding( padding: const EdgeInsets.symmetric( vertical: 8, horizontal: 16, ), child: Column( children: [ createNamesInput(), Padding( padding: const EdgeInsets.symmetric(vertical: 10), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ TextButton( onPressed: addName, child: Row( children: const [ Icon(Icons.add), Text('Adicionar Jogador'), ], ), ), ], ), ), SizedBox( width: double.infinity, height: 50, child: ElevatedButton( onPressed: () => print('Iniciar!'), child: const Text('Iniciar!'), ), ) ], ), ), ); } }
custom_input.dart
import 'package:flutter/material.dart'; typedef OneArgumentCallback = void Function(String changed); class CustomInput extends StatefulWidget { final OneArgumentCallback onChanged; final VoidCallback onRemoved; final String labelText; final String text; const CustomInput({ super.key, required this.onChanged, required this.labelText, required this.text, required this.onRemoved, }); @override State<CustomInput> createState() => _CustomInputState(); } class _CustomInputState extends State<CustomInput> { late final TextEditingController inputController; @override void initState() { super.initState(); inputController = TextEditingController(text: widget.text); } @override void didUpdateWidget(covariant CustomInput oldWidget) { super.didUpdateWidget(oldWidget); // 同步父组件更新后的文本内容 if (oldWidget.text != widget.text) { inputController.text = widget.text; } } void changeContent(String value) { widget.onChanged(value); } @override Widget build(BuildContext context) { return TextFormField( controller: inputController, textDirection: TextDirection.ltr, decoration: InputDecoration( border: const UnderlineInputBorder(), labelText: widget.labelText, suffixIcon: IconButton( onPressed: widget.onRemoved, icon: const Icon( Icons.close, color: Colors.red, ), ), ), autocorrect: false, onChanged: changeContent, ); } }
内容的提问来源于stack exchange,提问作者EnzoBtv
相关产品推荐
相关产品推荐

