自定义Controller的ListView插入元素时行为异常求助
问题原因
你的代码存在两个核心问题,导致新增Widget的位置和标题不符合预期:
- Entry的State缓存了旧数据:
_EntryState里的index和title是在initState中初始化的late final变量,而initState仅在Widget首次创建时执行一次。当列表数据变化后,widget.index会更新,但State里的缓存值不会同步更新,导致点击按钮时使用旧索引,新增位置错误,标题也显示旧内容。 - ListView子项缺少唯一Key:
ListView.builder生成的Entry没有设置唯一key,Flutter会复用现有Widget,导致数据更新时Widget与数据的对应关系混乱,进一步加剧位置和标题的错误。
修复方案
1. 移除State中的缓存变量,直接使用最新参数
修改_EntryState,删除缓存的index和title,在build方法中直接获取最新的索引和标题:
class _EntryState extends State<Entry> { @override Widget build(BuildContext context) => ListTile( title: Text(widget.controller.items[widget.index]), trailing: Row( mainAxisSize: MainAxisSize.min, children: [ TextButton( child: const Text('Add above'), onPressed: () => widget.controller.insertAbove(widget.index), ), TextButton( child: const Text('Add below'), onPressed: () => widget.controller.insertBelow(widget.index), ), ], ), ); }
2. 给ListView子项添加唯一Key
在CustomListView的itemBuilder中,给Entry添加key,确保Flutter能正确识别每个列表项:
itemBuilder: (context, index) => Entry( key: ValueKey(controller.items[index]), // 使用数据项作为唯一标识 index: index, controller: controller, ),
如果后续会出现重复的items值,可以改用包含索引的唯一Key,比如:key: Key('entry-$index-${controller.items[index]}'),确保每个Key绝对唯一。
完整修复后的代码
import 'package:flutter/material.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), useMaterial3: true, ), home: const MyHomePage(title: 'Flutter Demo Home Page'), ); } } class MyHomePage extends StatefulWidget { const MyHomePage({super.key, required this.title}); final String title; @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { @override Widget build(BuildContext context) { return Scaffold( body: Padding( padding: const EdgeInsets.symmetric(horizontal: 40), child: CustomListView( controller: ListController( items: ['a', 'b', 'c'], ), ), ), ); } } class CustomListView extends StatefulWidget { const CustomListView({ super.key, required this.controller, }); final ListController controller; @override State<CustomListView> createState() => _CustomListViewState(); } class _CustomListViewState extends State<CustomListView> { late final ListController controller; @override void initState() { super.initState(); controller = widget.controller..addListener(() => setState(() {})); } @override Widget build(BuildContext context) { return ListView.builder( shrinkWrap: true, itemCount: controller.items.length, itemBuilder: (context, index) => Entry( key: ValueKey(controller.items[index]), index: index, controller: controller, ), ); } } class Entry extends StatefulWidget { const Entry({ super.key, required this.index, required this.controller, }); final int index; final ListController controller; @override State<Entry> createState() => _EntryState(); } class _EntryState extends State<Entry> { @override Widget build(BuildContext context) => ListTile( title: Text(widget.controller.items[widget.index]), trailing: Row( mainAxisSize: MainAxisSize.min, children: [ TextButton( child: const Text('Add above'), onPressed: () => widget.controller.insertAbove(widget.index), ), TextButton( child: const Text('Add below'), onPressed: () => widget.controller.insertBelow(widget.index), ), ], ), ); } class ListController extends ChangeNotifier { List<String> items; ListController({required this.items}); void insertAbove(int index) { items.insert(index, 'above-${items[index]}'); notifyListeners(); } void insertBelow(int index) { items.insert(index + 1, 'below-${items[index]}'); notifyListeners(); } }
修复后的效果
点击"Add above"或"Add below"时,会在当前项的正确位置新增子项,标题也会正确显示为above-原标题或below-原标题,不会出现位置偏移或标题错误的问题。
内容的提问来源于stack exchange,提问作者TENX
相关产品推荐
相关产品推荐

