-1

I am building an application using Flutter and Dart programming language. I am using SQLite as the local database. Now I am developing a feature where I pass the data to a new form page when one of the items of the ListView is tapped. But updating the data is not working.

Following is my database helper class. Pay attention to the updateNote method

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> updateNote(Note note) async {
    var db = await this.database;

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

  //there are other methods hidden here
}

I have a widget class that displays the ListView with the following code

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

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

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

      setState(() {
        _notes = notes;
        _count = notes.length;
      });
    });
  }
}

As you can see I am passing the data to the next widget, NoteForm when one of the ListView's items is clicked.

This is my NoteForm class's implementation.

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(this._title, this._note);
  }
}

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;
  String _title;
  Note _note;

  _NoteFormState(String title, [Note note]) {
    this._title = title;
    this._note = note;
    if (this._note != null && this._note.id != null) {
      _titleController.text = this._note.title;
      _descriptionController.text = this._note.description;
      _dateController.text = this._note.date;
      _selectedPriority = this._note.priority;
    }
  }

  @override
  Widget build(BuildContext context) {

    return Scaffold(
      appBar: AppBar(title: Text(this._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);
      debugPrint("Note title is ${note.title}");
      debugPrint("Note description is ${note.description}");
      debugPrint("Note date is ${note.date}");
      debugPrint("Note priority is ${note.priority}");
      debugPrint("Note has been updated.");
      this.showSnackBar(context, "Note has been updated.");
    } else {
      //create
      _databaseHelper.insertNote(note);
      debugPrint("Note has been created.");
      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);
  }
}

When I update the note, it does not update it. The list view is still displaying the same data. What is wrong with my code and how can I fix it?

halfer
  • 19,824
  • 17
  • 99
  • 186
Wai Yan Hein
  • 13,651
  • 35
  • 180
  • 372

3 Answers3

0

When you are making your call to _databaseHelper.updateNote(note); I believe it you should be awaiting this so await _databaseHelper.updateNote(note); because it returns a future so right now you are not waiting for your data to return. You have a couple other functions that return futures too.

wcyankees424
  • 2,554
  • 2
  • 12
  • 24
0

maybe you need a await or future then, where you call

also give a print in the _databaseHelper.updateNote you can see which one comes first

  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
      await _databaseHelper.updateNote(note);
      debugPrint("Note title is ${note.title}");
      debugPrint("Note description is ${note.description}");
      debugPrint("Note date is ${note.date}");
      debugPrint("Note priority is ${note.priority}");
      debugPrint("Note has been updated.");
      this.showSnackBar(context, "Note has been updated.");
    } else {
      //create
      await _databaseHelper.insertNote(note);
      debugPrint("Note has been created.");
      this.showSnackBar(context, "Note has been added.");
    }

    closeForm(context);
  }
FloW
  • 1,211
  • 6
  • 13
0

I found the issue. It is with my code. Have a look at the following snippet.

  Note note = Note();
    note.title = _titleController.text;
    note.description = _descriptionController.text;
    note.date = _dateController.text;
    note.priority = _selectedPriority;

I am not assigning the id value. So that id is always null for updating. Thanks for all your help.

Wai Yan Hein
  • 13,651
  • 35
  • 180
  • 372