I am implementing a ListView, where each widget in the children contains two buttons. These buttons are meant to add a new widget above or below the current one, as shown in the diagram below.

The issue I am facing is that when I click either button, the added widget is not positioned correctly, and the title is also incorrect, as shown in below.

I can’t figure out how to fix it. Here’s the full code:
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(
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> {
late final int index;
late final String title;
@override
void initState() {
super.initState();
index = widget.index;
title = widget.controller.items[index];
print('build row $title');
}
@override
Widget build(BuildContext context) => ListTile(
title: Text(title),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
TextButton(
child: const Text('Add above'),
onPressed: () => widget.controller.insertAbove(index),
),
TextButton(
child: const Text('Add below'),
onPressed: () => widget.controller.insertBelow(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();
}
}
>Solution :
The problem is that you defined index and title in _EntryState and initialize them in initState. Those entries won’t be reactive to the list changes and may also use wrong indices to get elements from the list. Move them to the build method so that they’re updated on every rebuild:
class _EntryState extends State<Entry> {
// Remove the late variables here
@override
Widget build(BuildContext context) {
final index = widget.index;
final title = widget.controller.items[index];