Home > OS >  Deleting Item out of List, Listview.build shows wrong data
Deleting Item out of List, Listview.build shows wrong data

Time:11-04

I have a Stateful widget that i pass a list to (for example 2 items). After I delete an item, the widget should rebuild itself. Unfortunately, the deleted item is still displayed and the other one is not. When I re-enter the widget, the correct item is loaded. There is a similar problem List not updating on deleting item but maybe someone can explain me what i did wrong and why provider is helping me here instead of setState?

My code is:

import 'package:firebase_database/firebase_database.dart';
import 'package:flutter/material.dart';
import 'package:trip_planner/util/dialog_box.dart';
import 'package:trip_planner/util/previewUrl.dart';

class BookingPage extends StatefulWidget {
  final List toDoList;

  BookingPage({
    super.key,
    required this.toDoList,
  });

  @override
  State<BookingPage> createState() => _BookingPageState();
}

class _BookingPageState extends State<BookingPage> {
  //text controller
  final _controller = TextEditingController();
  final _database = FirebaseDatabase.instance.ref();

  //Liste is an example what i have in my list
  List toDoList2 = [
    ["https://www.booking.com/Share-Rnv2Kf", true],
    ["https://www.booking.com/Share-3hKQ0r", true],
  ];

  void initState(){
    super.initState();
  }

  void deleteTask(int index){
    setState(() {
      widget.toDoList.removeAt(index);
    });
    //DatabaseReference _testRef = _database.child("Hotel:");
    //_testRef.set(widget.toDoList.toString());
  }

  //save new Item
  void saveNewItem(){
    setState(() {
      widget.toDoList.add([_controller.text, false]);
      //DatabaseReference _testRef = _database.child("Hotel:");
      //_testRef.set(widget.toDoList.toString());
      _controller.clear();
    });
    Navigator.of(context).pop();
  }

  void createNewItem(){
    showDialog(
      context: context,
      builder: (context){
        return DialogBox(
          controller: _controller,
          onSave: saveNewItem,
          onCancel: () => Navigator.of(context).pop(),
        );
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Booking Seiten'),
        elevation: 0,
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: createNewItem,
        child: Icon(Icons.add),
      ),
      body: ListView.builder(
        itemCount: widget.toDoList.length,
        itemBuilder: (context, index){
          return PreviewUrl(
            url2: widget.toDoList[index][0],
            deleteFunction: (context) => setState(() => deleteTask(index)),
          );
        },
      ),
    );
  }
}

i thought setState does the same thing as when i re-enter the widget, but it doesn't.

CodePudding user response:

If you run the simplified version of your code in DartPad - it will work:

import 'package:flutter/material.dart';

const Color darkBlue = Color.fromARGB(255, 18, 32, 47);

List toDoList = [
    ["Button 1", true],
    ["Button 2", true],
  ];

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.dark().copyWith(
        scaffoldBackgroundColor: darkBlue,
      ),
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        body: Center(
          child: BookingPage(toDoList: toDoList),
        ),
      ),
    );
  }
}

class BookingPage extends StatefulWidget {
  final List toDoList;

  const BookingPage({
    super.key,
    required this.toDoList,
  });

  @override
  State<BookingPage> createState() => _BookingPageState();
}

class _BookingPageState extends State<BookingPage> {
  //Liste is an example what i have in my list
  List toDoList2 = [
    ["Button 1", true],
    ["Button 2", true],
  ];

  @override
  void initState() {
    super.initState();
  }

  void deleteTask(int index) {
    setState(() {
      widget.toDoList.removeAt(index);
    });
  }


  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Booking Seiten'),
        elevation: 0,
      ),
      body: ListView.builder(
        itemCount: widget.toDoList.length,
        itemBuilder: (context, index) {
          return ElevatedButton(
            style: ElevatedButton.styleFrom(
              backgroundColor: Colors.lightBlue,
              padding: const EdgeInsets.all(12),
              textStyle: const TextStyle(fontSize: 22),
            ),
            child: Text(widget.toDoList[index][0]!),
            onPressed: () => setState(() => deleteTask(index)),
          );
        },
      ),
    );
  }
}

Which tells me that the problem is your PreviewUrl. My guess is - it is a statful widget, and when the tree rebuilds - it will link the old State object to the first item.

Using Keys might help, something like:

return PreviewUrl(
            key: ObjectKey(widget.toDoList[index]),
            url2: widget.toDoList[index][0],
            deleteFunction: (context) => setState(() => deleteTask(index)),
          );

  • Related