If you write an app that needs to persist and query larger amounts of data onthe local device, consider using a database instead of a local file or key-valuestore. In general, databases provide faster inserts, updates, and queriescompared to other local persistence solutions.

Flutter apps can make use of the SQLite databases via thesqflite plugin available on pub.This recipe demonstrates the basics of using sqflite to insert, read, update,and remove data about various Dogs!

If you are new to SQLite and SQL statements, please review the SQLite Tutorialsite to learn the basics before completingthis recipe.

Directions

  • Add the dependencies
  • Define the Dog data model
  • Open the Database
  • Create the dogs table
  • Insert a Dog into the database
  • Retrieve the list of Dogs
  • Update a Dog in the database
  • Delete a Dog from the database

1. Add the dependencies

To work with SQLite databases, import the sqflite and path packages.

  • The sqflite package provides classes and functions that allow you to interact with a SQLite database.
  • The path package provides functions that allow you to correctly define the location to store the database on disk.
  1. dependencies:
  2. flutter:
  3. sdk: flutter
  4. sqflite:
  5. path:

2. Define the Dog data model

Before you create the table to store information on Dogs, take a few moments todefine the data that needs to be stored. For this example, define a Dog classthat contains three pieces of data: A unique id, the name, and the age ofeach dog.

  1. class Dog {
  2. final int id;
  3. final String name;
  4. final int age;
  5. Dog({this.id, this.name, this.age});
  6. }

3. Open the Database

Before you read and write data to the database, you need to open a connection to the database. This involves two steps:

  • Define the path to the database file using the getDatabasesPath from the sqflite package combined with the path function from the path package
  • Open the database with the openDatabase function from sqflite
  1. // Open the database and store the reference
  2. final Future<Database> database = openDatabase(
  3. // Set the path to the database. Note: Using the `join` function from the
  4. // `path` package is best practice to ensure the path is correctly
  5. // constructed for each platform.
  6. join(await getDatabasesPath(), 'doggie_database.db'),
  7. );

4. Create the dogs table

Next, you need to create a table to store information about various Dogs. Forthis example, create a table called dogs that defines the data that can bestored. In this case, each Dog contains an id, name, and age. Therefore,these will be represented as three columns in the dogs table.

  • The id is a Dart int, and will be stored as an INTEGER SQLite Datatype. It is also good practice to use an id as the primary key for the table to improve query and update times.
  • The name is a Dart String, and will be stored as a TEXT SQLite Datatype
  • The age is also a Dart int, and will be stored as an INTEGER DatatypeFor more information about the available Datatypes that can be stored in aSQLite database, please see the official SQLite Datatypesdocumentation.
  1. final Future<Database> database = openDatabase(
  2. // Set the path to the database.
  3. join(await getDatabasesPath(), 'doggie_database.db'),
  4. // When the database is first created, create a table to store dogs
  5. onCreate: (db, version) {
  6. // Run the CREATE TABLE statement on the database
  7. return db.execute(
  8. "CREATE TABLE dogs(id INTEGER PRIMARY KEY, name TEXT, age INTEGER)",
  9. );
  10. },
  11. // Set the version. This executes the onCreate function and provides a
  12. // path to perform database upgrades and downgrades.
  13. version: 1,
  14. );

5. Insert a Dog into the database

Now that you have a database with a table suitable for storing information about various dogs, it’s time to read and write data!

First, insert a Dog into the dogs table. This involves two steps:

  • Convert the Dog into a Map
  • Use the insert method to store the Map in the dogs table
  1. // First, update the Dog class to include a `toMap` method.
  2. class Dog {
  3. final int id;
  4. final String name;
  5. final int age;
  6. Dog({this.id, this.name, this.age});
  7. // Convert the dog into a Map. The keys must correspond to the names of the
  8. // columns in the database.
  9. Map<String, dynamic> toMap() {
  10. return {
  11. 'id': id,
  12. 'name': name,
  13. 'age': age,
  14. };
  15. }
  16. }
  17. // Next, define a function that inserts dogs into the database
  18. Future<void> insertDog(Dog dog) async {
  19. // Get a reference to the database
  20. final Database db = await database;
  21. // Insert the Dog into the correct table. You may also specify the
  22. // `conflictAlgorithm` to use in case the same dog is inserted twice.
  23. //
  24. // In this case, replace any previous data.
  25. await db.insert(
  26. 'dogs',
  27. dog.toMap(),
  28. conflictAlgorithm: ConflictAlgorithm.replace,
  29. );
  30. }
  31. // Now, you can create a Dog to and add it to the dogs table!
  32. final fido = Dog(
  33. id: 0,
  34. name: 'Fido',
  35. age: 35,
  36. );
  37. await insertDog(fido);

6. Retrieve the list of Dogs

Now that you have a Dog stored in the database, you can query the databasefor a specific dog or a list of all dogs! This involves two steps:

  • Run a query against the dogs table. This will return a List<Map>
  • Convert the List<Map> into a List<Dog>
  1. // A method that will retrieve all the dogs from the dogs table
  2. Future<List<Dog>> dogs() async {
  3. // Get a reference to the database
  4. final Database db = await database;
  5. // Query the table for All The Dogs.
  6. final List<Map<String, dynamic>> maps = await db.query('dogs');
  7. // Convert the List<Map<String, dynamic> into a List<Dog>.
  8. return List.generate(maps.length, (i) {
  9. return Dog(
  10. id: maps[i]['id'],
  11. name: maps[i]['name'],
  12. age: maps[i]['age'],
  13. );
  14. });
  15. }
  16. // Now, you can use the method above to retrieve all the dogs!
  17. print(await dogs()); // Prints a list that include Fido

7. Update a Dog in the database

After you’ve inserted some information into the database, you may want to updatethat information at a later time. To do so, use theupdatemethod from the sqflite library.

This involves two steps:

  • Convert the Dog into a Map
  • Use a where clause to ensure you update the correct Dog
  1. Future<void> updateDog(Dog dog) async {
  2. // Get a reference to the database
  3. final db = await database;
  4. // Update the given Dog
  5. await db.update(
  6. 'dogs',
  7. dog.toMap(),
  8. // Ensure we only update the Dog with a matching id
  9. where: "id = ?",
  10. // Pass the Dog's id through as a whereArg to prevent SQL injection
  11. whereArgs: [dog.id],
  12. );
  13. }
  14. // Now, you can update Fido's age!
  15. await updateDog(Dog(
  16. id: 0,
  17. name: 'Fido',
  18. age: 42,
  19. ));
  20. // And you could print the updated results
  21. print(await dogs()); // Prints Fido with age 42.

Warning:Always use whereArgs to pass arguments to a where statement. This helps safeguard against SQL injection attacks.

Do not use string interpolation, such as where: "id = ${dog.id}"!

8. Delete a Dog from the database

In addition to inserting and updating information about Dogs, you can alsoremove dogs from the database. To delete data, use thedeletemethod from the sqflite library.

In this portion, create a function that takes in an id and deletes the dog witha matching id from the database. To make this work, you must provide a whereclause to limit the records being deleted.

  1. Future<void> deleteDog(int id) async {
  2. // Get a reference to the database
  3. final db = await database;
  4. // Remove the Dog from the Database
  5. await db.delete(
  6. 'dogs',
  7. // Use a `where` clause to delete a specific dog
  8. where: "id = ?",
  9. // Pass the Dog's id through as a whereArg to prevent SQL injection
  10. whereArgs: [id],
  11. );
  12. }

Example

To run the example:

  • Create a new Flutter project
  • Add the sqfite and path packages to your pubspec.yaml
  • Paste the following code into a new file called lib/db_test.dart
  • Run the code with flutter run lib/db_test.dart
  1. import 'dart:async';
  2. import 'package:path/path.dart';
  3. import 'package:sqflite/sqflite.dart';
  4. void main() async {
  5. final database = openDatabase(
  6. // Set the path to the database. Note: Using the `join` function from the
  7. // `path` package is best practice to ensure the path is correctly
  8. // constructed for each platform.
  9. join(await getDatabasesPath(), 'doggie_database.db'),
  10. // When the database is first created, create a table to store dogs
  11. onCreate: (db, version) {
  12. return db.execute(
  13. "CREATE TABLE dogs(id INTEGER PRIMARY KEY, name TEXT, age INTEGER)",
  14. );
  15. },
  16. // Set the version. This executes the onCreate function and provides a
  17. // path to perform database upgrades and downgrades.
  18. version: 1,
  19. );
  20. Future<void> insertDog(Dog dog) async {
  21. // Get a reference to the database
  22. final Database db = await database;
  23. // Insert the Dog into the correct table. We will also specify the
  24. // `conflictAlgorithm` to use in this case. If the same dog is inserted
  25. // multiple times, it will replace the previous data.
  26. await db.insert(
  27. 'dogs',
  28. dog.toMap(),
  29. conflictAlgorithm: ConflictAlgorithm.replace,
  30. );
  31. }
  32. Future<List<Dog>> dogs() async {
  33. // Get a reference to the database
  34. final Database db = await database;
  35. // Query the table for All The Dogs.
  36. final List<Map<String, dynamic>> maps = await db.query('dogs');
  37. // Convert the List<Map<String, dynamic> into a List<Dog>.
  38. return List.generate(maps.length, (i) {
  39. return Dog(
  40. id: maps[i]['id'],
  41. name: maps[i]['name'],
  42. age: maps[i]['age'],
  43. );
  44. });
  45. }
  46. Future<void> updateDog(Dog dog) async {
  47. // Get a reference to the database
  48. final db = await database;
  49. // Update the given Dog
  50. await db.update(
  51. 'dogs',
  52. dog.toMap(),
  53. // Ensure we only update the Dog with a matching id
  54. where: "id = ?",
  55. // Pass the Dog's id through as a whereArg to prevent SQL injection
  56. whereArgs: [dog.id],
  57. );
  58. }
  59. Future<void> deleteDog(int id) async {
  60. // Get a reference to the database
  61. final db = await database;
  62. // Remove the Dog from the Database
  63. await db.delete(
  64. 'dogs',
  65. // Use a `where` clause to delete a specific dog
  66. where: "id = ?",
  67. // Pass the Dog's id through as a whereArg to prevent SQL injection
  68. whereArgs: [id],
  69. );
  70. }
  71. var fido = Dog(
  72. id: 0,
  73. name: 'Fido',
  74. age: 35,
  75. );
  76. // Insert a dog into the database
  77. await insertDog(fido);
  78. // Print the list of dogs (only Fido for now)
  79. print(await dogs());
  80. // Update Fido's age and save it to the database
  81. fido = Dog(
  82. id: fido.id,
  83. name: fido.name,
  84. age: fido.age + 7,
  85. );
  86. await updateDog(fido);
  87. // Print Fido's updated information
  88. print(await dogs());
  89. // Delete Fido from the Database
  90. await deleteDog(fido.id);
  91. // Print the list of dogs (empty)
  92. print(await dogs());
  93. }
  94. class Dog {
  95. final int id;
  96. final String name;
  97. final int age;
  98. Dog({this.id, this.name, this.age});
  99. Map<String, dynamic> toMap() {
  100. return {
  101. 'id': id,
  102. 'name': name,
  103. 'age': age,
  104. };
  105. }
  106. // Implement toString to make it easier to see information about each dog when
  107. // using the print statement.
  108. @override
  109. String toString() {
  110. return 'Dog{id: $id, name: $name, age: $age}';
  111. }
  112. }