Updating field in SQLite (Flutter)

3,878

Solution 1

The example in the documentation looks like this:

// Update Fido's age and save it to the database.
  fido = Dog(
    id: fido.id,
    name: fido.name,
    age: fido.age + 7,
  );
  await updateDog(fido);

You either approach it with the raw SQL query like in chunhunghan's answer, or query the Dog with the id, then override the fields, then update.

Why?

Lets look at your update code:

await updateDog(Dog(id: 0, age: 35));

When the update line is called, Dog.toMap() will be called and it will look like you are updating the name to null.

For you to do what you want here is the code:

 Future<Dog> getDog(int id) async {
    List<Map> result = await database.query(..... whereArgs: [id]);
    if (result.length > 0) {
      return new Dog.fromMap(result.first);
    }
    return null;
 }

// Now in code
fido = await getDog(id);
// Update Fido's age and save it to the database.
fido = Dog(
 id: fido.id,
 name: fido.name,
 age: 35, //<--
);
await updateDog(fido);

Solution 2

You can copy paste run full code below
Example code has two records to demo update effect
Solution 1 : You can use rawUpdate
code snippet

int count = await db.rawUpdate('UPDATE dogs SET age = ? WHERE id = ?', [35, 0]);

Solution 2 : You can revise toMap to only return id and age

Future<void> updateDog1(Dog dog) async {
    // Get a reference to the database.
    final db = await database;

    // Update the given Dog.
    await db.update(
      'dogs',
      dog.toMap1(),
 ...
Map<String, dynamic> toMap1() {
    return {
      'id': id,
      'age': age,
    };
  }

fido = Dog(
    id: fido.id,
    name: "not care",
    age: 35,
  );
 await updateDog1(fido);

output

I/flutter ( 6570): [Dog{id: 0, name: Fido, age: 42}, Dog{id: 1, name: abc, age: 10}]
I/flutter ( 6570): updated: 1
I/flutter ( 6570): [Dog{id: 0, name: Fido, age: 35}, Dog{id: 1, name: abc, age: 10}]

full code solution 1

import 'dart:async';

import 'package:flutter/widgets.dart';

import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';

void main() async {
  // Avoid errors caused by flutter upgrade.
  // Importing 'package:flutter/widgets.dart' is required.
  WidgetsFlutterBinding.ensureInitialized();
  final database = openDatabase(
    // Set the path to the database. Note: Using the `join` function from the
    // `path` package is best practice to ensure the path is correctly
    // constructed for each platform.
    join(await getDatabasesPath(), 'doggie_database.db'),
    // When the database is first created, create a table to store dogs.
    onCreate: (db, version) {
      return db.execute(
        "CREATE TABLE dogs(id INTEGER PRIMARY KEY, name TEXT, age INTEGER)",
      );
    },
    // Set the version. This executes the onCreate function and provides a
    // path to perform database upgrades and downgrades.
    version: 1,
  );

  Future<void> insertDog(Dog dog) async {
    // Get a reference to the database.
    final Database db = await database;

    // Insert the Dog into the correct table. Also specify the
    // `conflictAlgorithm`. In this case, if the same dog is inserted
    // multiple times, it replaces the previous data.
    await db.insert(
      'dogs',
      dog.toMap(),
      conflictAlgorithm: ConflictAlgorithm.replace,
    );
  }

  Future<List<Dog>> dogs() async {
    // Get a reference to the database.
    final Database db = await database;

    // Query the table for all The Dogs.
    final List<Map<String, dynamic>> maps = await db.query('dogs');

    // Convert the List<Map<String, dynamic> into a List<Dog>.
    return List.generate(maps.length, (i) {
      return Dog(
        id: maps[i]['id'],
        name: maps[i]['name'],
        age: maps[i]['age'],
      );
    });
  }

  Future<void> updateDog(Dog dog) async {
    // Get a reference to the database.
    final db = await database;

    // Update the given Dog.
    await db.update(
      'dogs',
      dog.toMap(),
      // Ensure that the Dog has a matching id.
      where: "id = ?",
      // Pass the Dog's id as a whereArg to prevent SQL injection.
      whereArgs: [dog.id],
    );
  }

  Future<void> deleteDog(int id) async {
    // Get a reference to the database.
    final db = await database;

    // Remove the Dog from the database.
    await db.delete(
      'dogs',
      // Use a `where` clause to delete a specific dog.
      where: "id = ?",
      // Pass the Dog's id as a whereArg to prevent SQL injection.
      whereArgs: [id],
    );
  }

  var fido = Dog(
    id: 0,
    name: 'Fido',
    age: 42,
  );

  var fido1 = Dog(
    id: 1,
    name: 'abc',
    age: 10,
  );

  // Insert a dog into the database.
  await insertDog(fido);
  await insertDog(fido1);

  // Print the list of dogs (only Fido for now).
  print(await dogs());
/*
  // Update Fido's age and save it to the database.
  fido = Dog(
    id: fido.id,
    name: fido.name,
    age: fido.age + 7,
  );
  await updateDog(fido);

  // Print Fido's updated information.
  print(await dogs());*/

  final Database db = await database;
  int count =
      await db.rawUpdate('UPDATE dogs SET age = ? WHERE id = ?', [35, 0]);
  print('updated: $count');
  print(await dogs());

  /*// Delete Fido from the database.
  await deleteDog(fido.id);

  // Print the list of dogs (empty).
  print(await dogs());*/
}

class Dog {
  final int id;
  final String name;
  final int age;

  Dog({this.id, this.name, this.age});

  Map<String, dynamic> toMap() {
    return {
      'id': id,
      'name': name,
      'age': age,
    };
  }

  // Implement toString to make it easier to see information about
  // each dog when using the print statement.
  @override
  String toString() {
    return 'Dog{id: $id, name: $name, age: $age}';
  }
}

full code solution 2

import 'dart:async';

import 'package:flutter/widgets.dart';

import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';

void main() async {
  // Avoid errors caused by flutter upgrade.
  // Importing 'package:flutter/widgets.dart' is required.
  WidgetsFlutterBinding.ensureInitialized();
  final database = openDatabase(
    // Set the path to the database. Note: Using the `join` function from the
    // `path` package is best practice to ensure the path is correctly
    // constructed for each platform.
    join(await getDatabasesPath(), 'doggie_database.db'),
    // When the database is first created, create a table to store dogs.
    onCreate: (db, version) {
      return db.execute(
        "CREATE TABLE dogs(id INTEGER PRIMARY KEY, name TEXT, age INTEGER)",
      );
    },
    // Set the version. This executes the onCreate function and provides a
    // path to perform database upgrades and downgrades.
    version: 1,
  );

  Future<void> insertDog(Dog dog) async {
    // Get a reference to the database.
    final Database db = await database;

    // Insert the Dog into the correct table. Also specify the
    // `conflictAlgorithm`. In this case, if the same dog is inserted
    // multiple times, it replaces the previous data.
    await db.insert(
      'dogs',
      dog.toMap(),
      conflictAlgorithm: ConflictAlgorithm.replace,
    );
  }

  Future<List<Dog>> dogs() async {
    // Get a reference to the database.
    final Database db = await database;

    // Query the table for all The Dogs.
    final List<Map<String, dynamic>> maps = await db.query('dogs');

    // Convert the List<Map<String, dynamic> into a List<Dog>.
    return List.generate(maps.length, (i) {
      return Dog(
        id: maps[i]['id'],
        name: maps[i]['name'],
        age: maps[i]['age'],
      );
    });
  }

  Future<void> updateDog(Dog dog) async {
    // Get a reference to the database.
    final db = await database;

    // Update the given Dog.
    await db.update(
      'dogs',
      dog.toMap(),
      // Ensure that the Dog has a matching id.
      where: "id = ?",
      // Pass the Dog's id as a whereArg to prevent SQL injection.
      whereArgs: [dog.id],
    );
  }

  Future<void> updateDog1(Dog dog) async {
    // Get a reference to the database.
    final db = await database;

    // Update the given Dog.
    await db.update(
      'dogs',
      dog.toMap1(),
      // Ensure that the Dog has a matching id.
      where: "id = ?",
      // Pass the Dog's id as a whereArg to prevent SQL injection.
      whereArgs: [dog.id],
    );
  }

  Future<void> deleteDog(int id) async {
    // Get a reference to the database.
    final db = await database;

    // Remove the Dog from the database.
    await db.delete(
      'dogs',
      // Use a `where` clause to delete a specific dog.
      where: "id = ?",
      // Pass the Dog's id as a whereArg to prevent SQL injection.
      whereArgs: [id],
    );
  }

  var fido = Dog(
    id: 0,
    name: 'Fido',
    age: 42,
  );

  var fido1 = Dog(
    id: 1,
    name: 'abc',
    age: 10,
  );

  // Insert a dog into the database.
  await insertDog(fido);
  await insertDog(fido1);

  // Print the list of dogs (only Fido for now).
  print(await dogs());

  // Update Fido's age and save it to the database.
  fido = Dog(
    id: fido.id,
    name: "not care",
    age: 35,
  );
  await updateDog1(fido);

  // Print Fido's updated information.
  print(await dogs());

  /*final Database db = await database;
  int count =
      await db.rawUpdate('UPDATE dogs SET age = ? WHERE id = ?', [35, 0]);
  print('updated: $count');
  print(await dogs());*/

  /*// Delete Fido from the database.
  await deleteDog(fido.id);

  // Print the list of dogs (empty).
  print(await dogs());*/
}

class Dog {
  final int id;
  final String name;
  final int age;

  Dog({this.id, this.name, this.age});

  Map<String, dynamic> toMap() {
    return {
      'id': id,
      'name': name,
      'age': age,
    };
  }

  Map<String, dynamic> toMap1() {
    return {
      'id': id,
      'age': age,
    };
  }

  // Implement toString to make it easier to see information about
  // each dog when using the print statement.
  @override
  String toString() {
    return 'Dog{id: $id, name: $name, age: $age}';
  }
}
Share:
3,878
Bassam
Author by

Bassam

Updated on December 21, 2022

Comments

  • Bassam
    Bassam over 1 year

    Let's use this code snippet as an example. The data model is very simple:

    class Dog {
      final int id;
      final String name;
      final int age;
    
      Dog({this.id, this.name, this.age});
    }
    

    To update the information, I'm using this function:

    Future<void> updateDog(Dog dog) async {
      // Get a reference to the database.
      final db = await database;
    
      // Update the given Dog.
      await db.update(
        'dogs',
        dog.toMap(),
        // Ensure that the Dog has a matching id.
        where: "id = ?",
        // Pass the Dog's id as a whereArg to prevent SQL injection.
        whereArgs: [dog.id],
      );
    }
    
    await updateDog(Dog(id: 0, name: 'Fido', age: 42));
    
    

    It works really fine and there aren't any problems. Now the question is, how to update only the field age without using name? So basically I want to do something like this

    await updateDog(Dog(id: 0, age: 35));
    

    and expect as a result "name: Figo, age: 35". But instead it removes Fido in null. So I get this as a result: "name: null, age: 35".