Flutter ListView не обновляется при изменении элементов списка - PullRequest
0 голосов
/ 04 апреля 2020

Я начал изучать Флаттер. Я разрабатываю простое приложение, используя его. Сейчас я разрабатываю функцию, в которой мое приложение будет отображать записи из базы данных SQLite и где пользователь добавляет новые записи в базу данных SQLite. Но мой ListView отображает пустой экран.

У меня есть класс DatabaseHelper со следующим кодом.

class DatabaseHelper {
  static DatabaseHelper _databaseHelper;
  Database _database;

  String noteTable = 'note_table';
  String colId = 'id';
  String colTitle = 'title';
  String colDescription = 'description';
  String colPriority = 'priority';
  String colDate = 'date';

  DatabaseHelper._createInstance();

  factory DatabaseHelper() {
    if (_databaseHelper == null) {
      _databaseHelper = DatabaseHelper._createInstance();
    }

    return _databaseHelper;
  }

  Future<Database> get database async {
    if (_database == null) {
      _database = await initializeDatabase();
    }

    return _database;
  }

  Future<Database> initializeDatabase() async {
    Directory directory = await getApplicationDocumentsDirectory();
    String path = directory.path + 'notes.db';
    var notesDatabase = await openDatabase(path, version: 1, onCreate: _createDB);

    return notesDatabase;
  }

  void _createDB(Database db, int newVersion) async {
    await db.execute('CREATE TABLE $noteTable($colId INTEGER PRIMARY KEY AUTOINCREMENT, $colTitle TEXT, $colDescription TEXT, $colPriority INTEGER, $colDate TEXT)');
  }

  Future<List<Map<String, dynamic>>> getNoteMapList() async {
    Database db = await this.database;

    return await db.query(noteTable, orderBy: '$colPriority ASC');
  }

  Future<int> insertNote(Note note) async {
    Database db = await this.database;

    return await db.insert(noteTable, note.toMap());
  }

  Future<int> updateNote(Note note) async {
    var db = await this.database;

    return await db.update(noteTable, note.toMap(), where: '$colId = ?', whereArgs: [note.id]);
  }

  Future<int> deleteNote(int id) async {
    var db = await this.database;

    return await db.rawDelete('DELETE FROM $noteTable WHERE $colId = $id');
  }

  Future<int> getCount() async {
    Database db = await this.database;
    List<Map<String, dynamic>> x = await db.rawQuery('SELECT COUNT(*) FROM $noteTable');

    return Sqflite.firstIntValue(x);
  }
}

Затем у меня есть виджет NoteList со следующим кодом, в котором отображается список элементов.

    class NoteList extends StatefulWidget {
      @override
      State<StatefulWidget> createState() {
        return _NoteListState();
      }
    }

    class _NoteListState extends State<NoteList> {
      List<Note> _notes = [];
      int _count = 0;
      DatabaseHelper _databaseHelper = DatabaseHelper();

      _NoteListState() {
        this._notes = getNotes();
        this._count = _notes.length;
      }

      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(title: Text("Notes"),),
          body: Container(
            child: getListView(context),
          ),
          floatingActionButton: FloatingActionButton(
            child: Icon(Icons.add),
            onPressed: () {
              navigateToNoteForm("Add Note");
            },
          ),
        );
      }

      Widget getListView(BuildContext context) {
        return ListView.builder(
            itemCount: _count,
            itemBuilder: (context, index) {
              return ListTile(
                leading: CircleAvatar(
                  backgroundColor: _notes[index].priority == 1? Colors.yellow: Colors.red,
                  child: Icon(_notes[index].priority == 1 ? Icons.arrow_right : Icons.add),
                ),
                title: Text(_notes[index].title),
                subtitle: Text(_notes[index].date),
                trailing: Icon(Icons.delete),
                onTap: () {
                  navigateToNoteForm("Edit Note", _notes[index]);
                },
              );
            });
      }

      void navigateToNoteForm(String pageTitle, [Note note]) async {
        bool result = await Navigator.push(context, MaterialPageRoute(builder: (context) {
          return NoteForm(pageTitle, note);
        }));

        if (result) {
          setState(() {
            debugPrint("Updating list");
            _notes = getNotes();
            _count = _notes.length;
          });
        }
      }

      List<Note> getNotes() {
        List<Note> notes = List<Note>();
        Future<List<Map<String, dynamic>>> notesFuture = _databaseHelper.getNoteMapList();
        notesFuture.then((notesMap) {
          debugPrint("Total notes found in the database ${notesMap.length}");
          notesMap.forEach((map) {
            notes.add(Note.fromMapObject(map));
          });
        });

        return notes;
      }
    }


Then I also have another widget class called NoteForm with the following code.


class NoteForm extends StatefulWidget {
  String _title = "";
  Note _note = null;

  NoteForm(String title, [Note note]) {
    this._title = title;
    this._note = note;
  }

  @override
  State<StatefulWidget> createState() {
    return _NoteFormState();
  }
}

class _NoteFormState extends State<NoteForm> {

  double _minimumPadding = 15.0;
  var _priorities = [ 1, 2 ];
  var _titleController = TextEditingController();
  var _descriptionController = TextEditingController();
  var _dateController = TextEditingController();
  DatabaseHelper _databaseHelper = DatabaseHelper();
  var _selectedPriority = 1;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text(widget._title),),
      body: Builder(
        builder: (scaffoldContext) => Form(
          child: Column(
            children: <Widget>[
              Container(
                child: Padding(
                  padding: EdgeInsets.all(_minimumPadding),
                  child: TextFormField(
                    controller: _titleController,
                    decoration: InputDecoration(
                        labelText: "Title",
                        hintText: "Enter title"
                    ),
                  ),
                ),
              ),
              Container(
                  child: Padding(
                    padding: EdgeInsets.all(_minimumPadding),
                    child: TextFormField(
                      controller: _descriptionController,
                      decoration: InputDecoration(
                          labelText: "Description",
                          hintText: "Enter description"
                      ),
                    ),
                  )
              ),
              Container(
                child: Padding(
                  padding: EdgeInsets.all(_minimumPadding),
                  child: TextFormField(
                    controller: _dateController,
                    decoration: InputDecoration(
                        labelText: "Date",
                        hintText: "Enter date"
                    ),
                  ),
                ),
              ),
              Container(
                child: Padding(
                  padding: EdgeInsets.all(_minimumPadding),
                  child: DropdownButton<int>(
                    value: _selectedPriority,
                    items: _priorities.map((dropdownItem) {
                      return DropdownMenuItem<int>(
                        value: dropdownItem,
                        child: Text(dropdownItem == 1? "Low": "High"),
                      );
                    }).toList(),
                    onChanged: (int newSelectedValue) {
                      setState(() {
                        _selectedPriority = newSelectedValue;
                      });
                    },
                  ),
                ),
              ),
              Container(
                child: Padding(
                  padding: EdgeInsets.all(_minimumPadding),
                  child: RaisedButton(
                    child: Text(
                        "Save"
                    ),
                    onPressed: () {
                      _save(scaffoldContext);
                    },
                  ),
                ),
              )
            ],
          ),
        ),
      )
    );
  }

  void _save(BuildContext context) async {
    Note note = Note();
    note.title = _titleController.text;
    note.description = _descriptionController.text;
    note.date = _dateController.text;
    note.priority = _selectedPriority;

    if (widget._note != null && widget._note.id!=null) {
      //update
      _databaseHelper.updateNote(note);
      this.showSnackBar(context, "Note has been updated.");
    } else {
      //create
      _databaseHelper.insertNote(note);
      this.showSnackBar(context, "Note has been added.");
    }

    closeForm(context);
  }

  void showSnackBar(BuildContext context, String message) {
    var snackBar = SnackBar(
      content: Text(message),
      action: SnackBarAction(
        label: "UNDO",
        onPressed: () {

        },
      ),
    );

    Scaffold.of(context).showSnackBar(snackBar);
  }

  void closeForm(BuildContext context) {
    Navigator.pop(context, true);
  }
}

Когда я запускаю свое приложение, оно просто отображает пустой экран следующим образом.

enter image description here

Как видите, я выхожу из системы. количество записей, возвращаемых из базы данных методом debugPrint. Это говорит о том, что в базе данных есть 6 записей. Это просто не отображение записей. Что не так с моим кодом и как я могу это исправить?

1 Ответ

1 голос
/ 04 апреля 2020

Как я уже упоминал в комментарии, что происходило из-за асинхронной c, выполнение задачи занимает некоторое время, и если вы не оставляете ее асинхронной c, то функция setState выполняется до фактической загрузки или установки данных.

Итак, следующие изменения решат вашу проблему.

make getNotes async method And

getNotes().then((noteresponce){ setState((){ _notes=noteresponce; _count = _notes.length;} });
...