Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flutter ListView displays wrong items

Tags:

flutter

dart

The ListView shows Item1, Item2 and Item3. When I try to delete Item2, the ListView incorrectly shows Item1 and Item2. The console shows the correct items in the list: Item1 and Item3.

HomeScreen:

    class HomeScreen extends StatefulWidget {
  @override
  HomeScreenState createState() => new HomeScreenState();
}

class HomeScreenState extends State<HomeScreen> {
  List<Todo> todos = new List();

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

  void populateTodos() async {
    TodoDatabase db = new TodoDatabase();
    db.getAllTodos().then((newTodos) {
      for (var todo in newTodos) {
        print(todo.title + ", " + todo.id);
      }
      setState(() => todos = newTodos);
    });
  }

  void openAddTodoScreen() async {
    Navigator
        .push(context,
            new MaterialPageRoute(builder: (context) => new AddTodoScreen()))
        .then((b) {
      populateTodos();
    });
  }

  void clearDb() async {
    TodoDatabase db = new TodoDatabase();
    db.clearDb();
    populateTodos();
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("Todo App"),
        actions: <Widget>[
          new IconButton(
            icon: new Icon(Icons.delete),
            onPressed: () => clearDb(),
          ),
          new IconButton(
            icon: new Icon(Icons.refresh),
            onPressed: () => populateTodos(),
          )
        ],
      ),
      body: new Center(
        child: new Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            new Expanded(
              child: new ListView.builder(
                padding: new EdgeInsets.all(10.0),
                itemCount: todos.length,
                itemBuilder: (BuildContext context, int index) {
                  return new TodoItem(todos[index], onDelete: (id) {
                    TodoDatabase db = new TodoDatabase();
                    print("ID: " + id);
                    db.deleteTodo(id).then((b) {
                      populateTodos();
                    });
                  });
                },
              ),
            )
          ],
        ),
      ),
      floatingActionButton: new FloatingActionButton(
          child: new Icon(Icons.add), onPressed: () => openAddTodoScreen()),
    );
  }
}

DataBase Code:

    class TodoDatabase {
  TodoDatabase();
  static Database _db;

  Future<Database> get db async {
    if (_db != null) {
      return _db;
    }
    _db = await initDB();
    return _db;
  }

  Future<Database> initDB() async {
    Directory documentsDirectory = await getApplicationDocumentsDirectory();
    String path = join(documentsDirectory.path, "main.db");
    var theDb = await openDatabase(path, version: 1, onCreate: createDatabase);
    return theDb;
  }

  void createDatabase(Database db, int version) async {
    await db.execute("CREATE TABLE Todos(id STRING PRIMARY KEY, title TEXT, description TEXT)");

    print("Database was Created!");
  }

  Future<List<Todo>> getAllTodos() async {
    var dbClient = await db;
    List<Map> res = await dbClient.query("Todos");
    print(res);
    return res.map((map) => new Todo(title: map["title"], description: map["description"], id: map["id"])).toList();
  }

  Future<Todo> getTodo(String id) async {
    var dbClient = await db;
    var res = await dbClient.query("Todos", where: "id = ?", whereArgs: [id]);
    if (res.length == 0) return null;
    return new Todo.fromDb(res[0]);
  }

  Future<int> addTodo(Todo todo) async {
    var dbClient = await db;
    int res = await dbClient.insert("Todos", todo.toMap());
    return res;
  }

  Future<int> updateTodo(Todo todo) async {
    var dbClient = await db;
    int res = await dbClient.update(
      "Todos",
      todo.toMap(),
      where: "id = ?",
      whereArgs: [todo.id]);
      return res;
  }

  Future<int> deleteTodo(String id) async {
    var dbClient = await db;
    var res = await dbClient.delete(
      "Todos",
      where: "id = ?",
      whereArgs: [id]);
      print("Deleted item");
      return res;
  }

  Future<int> clearDb() async {
    var dbClient = await db;
    var res = await dbClient.execute("DELETE from Todos");
      print("Deleted db contents");
      return res;
  }
}

This almost seems like a bug with Flutter. I have to add some more text because otherwise it won't let me post. I don't know how to illustrate the issue further. Thanks for your help!

like image 906
Necrophades Avatar asked Apr 07 '18 17:04

Necrophades


1 Answers

I was missing a key. Here's the correct ListView.builder:

    new ListView.builder(
    key: new Key(randomString(20)),
    padding: new EdgeInsets.all(10.0),
    itemCount: todos.length,
    itemBuilder: (BuildContext context, int index) {
        return new TodoItem(todos[index], onDelete: (id) {
            TodoDatabase db = new TodoDatabase();
            db.deleteTodo(id).then((b) {
            populateTodos();
            });
        });
    },
),
like image 125
Necrophades Avatar answered Nov 10 '22 08:11

Necrophades