Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Incorrect behaviour when inserting an item into the ListView with a custom controller

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.
enter image description here
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.
enter image description here

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();
  }
}

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

>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];
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading