Using SQLite in main.dart from seperate file? - sqlite

I've followed this easy example from the flutter documentation. However, this is wrote in a separate file (db_test.db). I'm aiming to convert data into a ListView at some point. So, how would I use CRUD operations like retrieving data in my main.dart? I could add this to my main.dart file but I'd like to keep it clean and separate.
Official Flutter tutorial
My db.dart file
void main () async {
final database = openDatabase(
join(await getDatabasesPath(), 'to_do.db'),
onCreate: (db, version) {
return db.execute("CREATE TABLE tasks(id INTEGER PRIMARY KEY, title TEXT, created TEXT, INTEGER is_complete)");
},
version: 1,
);
Future<void> insertTask (Task task) async {
final Database db = await database;
await db.insert(
'tasks',
task.toMap(),
conflictAlgorithm: ConflictAlgorithm.replace
);
}
Future<List<Task>> tasks () async {
final Database db = await database;
final List<Map<String, dynamic>> maps = await db.query('tasks');
return List.generate(maps.length, (i) {
return Task(
id: maps[i]['id'],
title: maps[i]['title'],
created: maps[i]['created'],
isComplete: maps[i]['is_complete']
);
});
}
Future<void> updateTask(Task task) async {
// Get a reference to the database.
final db = await database;
// Update the given Dog.
await db.update(
'tasks',
task.toMap(),
// Ensure that the Dog has a matching id.
where: "id = ?",
// Pass the Dog's id as a whereArg to prevent SQL injection.
whereArgs: [task.id],
);
}
Future<void> deleteTask(int id) async {
// Get a reference to the database.
final db = await database;
// Remove the Dog from the database.
await db.delete(
'tasks',
// 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],
);
}
}

You can create a new file containing Class with static members to help. Static members ensure that only one instance of database is created in your whole app.
class DatabaseHelper {
static Database _database;
///Returns db instance if already opened
///else call the initDatabase
static Future<Database> getDBConnector() async {
if (_database != null) {
return _database;
}
return await _initDatabase();
}
///Open DB Connection, returns a Database instance.
///
static Future<Database> _initDatabase() async {
_database = await openDatabase(
join(await getDatabasesPath(), "my_path.db"),
onCreate: (db, version) async {
//on create
},
version: 1,
);
return _database;
}
//put your CRUD in static function
static Future<void> insertTask (Task task) async {
final Database db = await getDBConnector();
await db.insert(
'tasks',
task.toMap(),
conflictAlgorithm: ConflictAlgorithm.replace
);
}
//the same with edit, delete
}
Then in your other file (like main.dart) you can just call it like this:
import "./databaseHelper.dart";
void caller() async{
//create task
//insert
await DatabaseHelper.insertTask(task);
}
Make sure the caller is asynchronous.

Related

Flutter SQLITE how to delete with limit

In my database I have several times the same value which returns, I want to delete the most recent value but when I want to perform a LIMIT I have an error.
My databaseHelper :
class DatabaseHelper{
static final _dbName = AppConfig.DATABASE_NAME;
static final _dbVersion = AppConfig.DATABASE_VERSION;
static final tableName = 'counter';
static final columnId = '_id';
static final columnType = 'type';
static final columnStatus = 'status';
DatabaseHelper._privateConstructor();
static final DatabaseHelper instance = DatabaseHelper._privateConstructor();
static Database _database;
Future<Database> get database async{
if(_database != null) return _database;
_database = await _initiateDatabase();
return _database;
}
_initiateDatabase() async{
return await openDatabase(join(await getDatabasesPath(), _dbName), version: _dbVersion, onCreate: _onCreate);
}
Future _onCreate(Database db, int version) async{
await db.execute('''
CREATE TABLE $tableName (
$columnId ID INTEGER PRIMARY KEY,
$columnType TEXT NOT NULL,
$columnStatus TEXT NOT NULL
)
'''
);
}
Future<int> insert(Map<String, dynamic> row) async
{
Database db = await instance.database;
return await db.insert(tableName, row);
}
Future<List<Map<String, dynamic>>> queryAll() async
{
Database _db = await instance.database;
return await _db.query(tableName);
}
Future<int> delete(int id) async
{
Database _db = await instance.database;
return await _db.delete(tableName, where: '$columnId = ?', whereArgs: [id]);
}
Future<void> deleteAll() async
{
Database _db = await instance.database;
return await _db.delete(tableName);
}
// Here is my problem
Future<int> deletePersonnel(String type) async
{
Database _db = await instance.database;
return await _db.rawDelete("DELETE FROM $tableName WHERE $columnType = $type LIMIT 1");
}
}
In my deletePersonnel function, The request works if I write this :
wait _db.rawDelete("DELETE FROM $tableName");
But I have an error if I write this :
wait _db.rawDelete("DELETE FROM $tableName WHERE $columnType = $type LIMIT 1");
Here is an exemple of the value in my database:
[
{type: Test, status: High},
{type: Test, status: High},
{type: Active, status: Low},
{type: Test, status: High}
]
As you can see I have several times the same values ​​which appear in my database, so I want to delete a value which corresponds to my type by deleting the last entry
The error that I have :
E/SQLiteLog(10576): (1) near "LIMIT": syntax error
BUT if I remove the LIMIT 1 in my request I have an error too :
no such column: Test

Fluttet sqfLite delete

I am trying to build a note app. I watch some tutorials on sqflite in order to save data after the app is terminated. I have managed to save the data but i cant delete data.
Here is the DBelper class i have created:
import 'package:sqflite/sqflite.dart' as sql;
import 'package:path/path.dart' as path;
import 'package:sqflite/sqlite_api.dart';
class DBHelper {
static Future<Database> databse() async {
final dbPath = await sql.getDatabasesPath();
return sql.openDatabase(path.join(dbPath, 'notes.db'),
onCreate: (db, version) {
return db.execute(
'CREATE TABLE user_notes(id TEXT PRIMARY KEY, title TEXT, text TEXT)');
}, version: 1);
}
static Future<void> insert(String table, Map<String, Object> data) async {
final db = await DBHelper.databse();
db.insert(
table,
data,
);
}
static Future<List<Map<String, dynamic>>> getData(String table) async {
final db = await DBHelper.databse();
return db.query(table);
}
}
And this is how i save the data :
onPressed: () {
Provider.of<Notes>(context, listen: false)
.addNote(newNoteTitle, newNoteText);
DBHelper.insert('user_notes',
{'title': newNoteTitle, 'text': newNoteText});
Navigator.pop(context);
},
I have been trying to create the DBHelper.deleteNote but even if i manage to write some code without errors nothing gets deleted. Thanks in advance for any help
If you simply want to delete a note then you can use its id, something like this:
deleteNote(String id) async {
final db = await DBHelper.databse();
db.delete('user_notes', where: 'id = ?', whereArgs: [id]);
}

Import csv to sqlite in Flutter (insert issue)

I've already read the related article and solution + mentioned method, but still I don't get it what should I add to my code for importing csv to sqlite table List<<Map<String, dynamic>. I need to replace existing table with a new one and insert each lines of converted csv. How can I solve it? Here's my code below.The problem is importVoca() of db.dart.
db.dart
class DBHelper {
var _db;
// create database
Future<Database> get database async {
if (_db != null) return _db;
_db = openDatabase(
join(await getDatabasesPath(), 'vocas.db'),
onCreate: (db, version) {
return db.execute(
"CREATE TABLE vocas(id TEXT PRIMARY KEY, word TEXT, meaning TEXT, createTime TEXT)",
);
},
version: 1,
);
return _db;
}
// insert voca
Future<void> insertVoca(Voca voca) async {
final db = await database;
await db.insert('vocas', voca.toMap(),
conflictAlgorithm: ConflictAlgorithm.replace);
}
// Voca list
Future<List<Voca>> vocas() async {
final db = await database;
final List<Map<String, dynamic>> maps = await db.query('vocas');
return List.generate(maps.length, (i) {
return Voca(
id: maps[i]['id'],
word: maps[i]['word'],
meaning: maps[i]['meaning'],
createTime: maps[i]['createTime']);
});
}
//update voca list
Future<void> updateVoca(Voca voca) async {
final db = await database;
await db.update(
'vocas',
voca.toMap(),
where: "id = ?",
whereArgs: [voca.id],
);
}
//delete voca
Future<void> deleteVoca(String id) async {
final db = await database;
await db.delete(
'vocas',
where: "id = ?",
whereArgs: [id],
);
}
//find voca to edit
Future<List<Voca>> findVoca(String id) async {
final db = await database;
final List<Map<String, dynamic>> maps =
await db.query('vocas', where: 'id = ?', whereArgs: [id]);
return List.generate(maps.length, (i) {
return Voca(
id: maps[i]['id'],
word: maps[i]['word'],
meaning: maps[i]['meaning'],
createTime: maps[i]['createTime'],
);
});
}
//export voca to csv
Future exportVoca() async {
var year = DateFormat('yy').format(DateTime.now());
var month = DateFormat('MM').format(DateTime.now());
var day = DateFormat('d').format(DateTime.now());
final db = await database;
var result = await db.query('vocas');
var csv = mapListToCsv(result);
final directory = await getApplicationDocumentsDirectory();
final pathOfFile = await directory.path;
File file = File("$pathOfFile/dontForget_$year$month$day.csv");
file.writeAsString(csv);
}
//import csv to sqlite
Future importVoca() async {
File file = await FilePicker.getFile(
type: FileType.custom, allowedExtensions: ['csv']);
final data = file.openRead();
final fields = await data
.transform(utf8.decoder)
.transform(new CsvToListConverter())
.toList();
Database _db = await openDatabase(
join(await getDatabasesPath(), 'vocas.db'),
version: 1, onCreate: (Database db, int version) async {
await db.execute("DROP TABLE IF EXISTS vocas");
await db.execute(
"CREATE TABLE vocas(id TEXT PRIMARY KEY, word TEXT, meaning TEXT, createTime TEXT)");
});
}
}

Flutter: Why are the methods inside this function not accessing the database variable?

I have a file named database_services.dart. This file have a class with various methods. The first method open the database and store the reference, while the other methods make inclusions, updates and deletes on this database. The problem is that the other methods are unable to see the database created by the first method. What am I missing?
Here is the code:
import 'dart:async';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
import 'counter.dart';
class DatabaseServices {
initDatabase() async {
// Open the database and store the reference.
final Future<Database> database = openDatabase(
// Set the path to the database.
join(await getDatabasesPath(), 'counter_database.db'),
// When the database is first created, create a table to store counters;
onCreate: (db, version) {
// Run the CREATE TABLE statement on the database.
return db.execute(
"CREATE TABLE counters(id INTEGER PRIMARY KEY, name TEXT, value INTEGER)",
);
},
// Set the version. This executes the onCreate function and provides a
// path to perform database upgrades and downgrades.
version: 1,
);
}
// Define a function that inserts counters into the database.
Future<void> insertCounter(Counter counter) async {
// Get a reference to the database.
final Database db = await database;
// Insert the Counter into the correct table. Here, if a counter is inserted twice,
// it replace any previous data.
await db.insert(
'counters',
counter.toMap(),
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
}
// A method that retrieves all the counters from the counters table.
Future<List<Counter>> counters() async {
// Get a reference to the database.
final Database db = await database;
// Query the table for all the Counters.
final List<Map<String, dynamic>> maps = await db.query('counters');
// Counvert the List<Map<String, dynamic>> into a List<Counter>
return List.generate(maps.length, (i) {
return Counter(
id: maps[i]['id'],
name: maps[i]['name'],
value: maps[i]['value'],
);
});
}
// Method to update a Counter in the database
Future<void> updateCounter(Counter counter) async {
final db = await database;
await db.update(
'counters',
counter.toMap(),
where: "id = ?",
whereArgs: [counter.id],
);
}
//Delete a Counter from the database
Future<void> deleteCounter(int id) async {
final db = await database;
await db.delete(
'counters',
where: "id = ?",
whereArgs: [id],
);
}
}
The database variable is only stored in the initDatabase method instead of on the DatabaseServices class, which means that it will only be accessible from the initDatabase method.
The code sample below shows how you could store the database as a property on the DatabaseServices class, which would allow it to be used by all the methods inside that class.
class DatabaseServices {
Future<Database> _db;
Future<void> initDatabase() async {
// Open the database and store the reference.
_db = openDatabase(
// Set the path to the database.
join(await getDatabasesPath(), 'counter_database.db'),
// When the database is first created, create a table to store counters;
onCreate: (db, version) {
// Run the CREATE TABLE statement on the database.
return db.execute(
"CREATE TABLE counters(id INTEGER PRIMARY KEY, name TEXT, value INTEGER)",
);
},
// Set the version. This executes the onCreate function and provides a
// path to perform database upgrades and downgrades.
version: 1,
);
}
// Define a function that inserts counters into the database.
Future<void> insertCounter(Counter counter) async {
// Get a reference to the database.
final db = await _db;
// Insert the Counter into the correct table. Here, if a counter is inserted twice,
// it replace any previous data.
await db.insert(
'counters',
counter.toMap(),
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
// A method that retrieves all the counters from the counters table.
Future<List<Counter>> counters() async {
// Get a reference to the database.
final db = await _db;
// Query the table for all the Counters.
final List<Map<String, dynamic>> maps = await db.query('counters');
// Counvert the List<Map<String, dynamic>> into a List<Counter>
return List.generate(maps.length, (i) {
return Counter(
id: maps[i]['id'],
name: maps[i]['name'],
value: maps[i]['value'],
);
});
}
// Method to update a Counter in the database
Future<void> updateCounter(Counter counter) async {
final db = await _db;
await db.update(
'counters',
counter.toMap(),
where: "id = ?",
whereArgs: [counter.id],
);
}
//Delete a Counter from the database
Future<void> deleteCounter(int id) async {
final db = await _db;
await db.delete(
'counters',
where: "id = ?",
whereArgs: [id],
);
}
}
You can find more information about opening a database here.

How to do a database query with SQFlite in Flutter

How do you query data from SQLite database in Flutter using the SQFlite plugin?
I have been working on learning this recently, so I am adding my answer below as a means to help me learn and also as a quick reference for others in the future.
Add the dependencies
Open pubspec.yaml and in the dependencies section add the following lines:
sqflite: ^1.0.0
path_provider: ^0.4.1
The sqflite is the SQFlite plugin of course and the path_provider will help us get the user directory on Android and iPhone. You can check the most up-to-date version numbers here: sqflite and path_provider.
Make a database helper class
I'm keeping a global reference to the database in a singleton class. This will prevent concurrency issues and data leaks. You can also add helper methods (like query) in here for accessing the database.
Create a new file called database_helper.dart and paste in the following code:
import 'dart:io' show Directory;
import 'package:path/path.dart' show join;
import 'package:sqflite/sqflite.dart';
import 'package:path_provider/path_provider.dart' show getApplicationDocumentsDirectory;
class DatabaseHelper {
static final _databaseName = "MyDatabase.db";
static final _databaseVersion = 1;
static final table = 'my_table';
static final columnId = '_id';
static final columnName = 'name';
static final columnAge = 'age';
// make this a singleton class
DatabaseHelper._privateConstructor();
static final DatabaseHelper instance = DatabaseHelper._privateConstructor();
// only have a single app-wide reference to the database
static Database _database;
Future<Database> get database async {
if (_database != null) return _database;
// lazily instantiate the db the first time it is accessed
_database = await _initDatabase();
return _database;
}
// this opens the database (and creates it if it doesn't exist)
_initDatabase() async {
Directory documentsDirectory = await getApplicationDocumentsDirectory();
String path = join(documentsDirectory.path, _databaseName);
return await openDatabase(path,
version: _databaseVersion,
onCreate: _onCreate);
}
// SQL code to create the database table
Future _onCreate(Database db, int version) async {
await db.execute('''
CREATE TABLE $table (
$columnId INTEGER PRIMARY KEY,
$columnName TEXT NOT NULL,
$columnAge INTEGER NOT NULL
)
''');
// prepopulate a few rows (consider using a transaction)
await db.rawInsert('INSERT INTO $table ($columnName, $columnAge) VALUES("Bob", 23)');
await db.rawInsert('INSERT INTO $table ($columnName, $columnAge) VALUES("Mary", 32)');
await db.rawInsert('INSERT INTO $table ($columnName, $columnAge) VALUES("Susan", 12)');
}
}
Note that when the database is created I pre-populated a few rows. This is so that we have something to work with in the query examples below.
Query data
We'll use an async method to do our query because database operations can be expensive.
Get all rows
To do a SELECT * and return everything in the table you just pass in the table name.
_query() async {
// get a reference to the database
Database db = await DatabaseHelper.instance.database;
// get all rows
List<Map> result = await db.query(DatabaseHelper.table);
// print the results
result.forEach((row) => print(row));
// {_id: 1, name: Bob, age: 23}
// {_id: 2, name: Mary, age: 32}
// {_id: 3, name: Susan, age: 12}
}
Get a single row
We can pass an argument in for the where parameter to select specific rows that meet our criteria. In this example we will query the row with an ID of 1.
_query() async {
// get a reference to the database
Database db = await DatabaseHelper.instance.database;
// get single row
List<String> columnsToSelect = [
DatabaseHelper.columnId,
DatabaseHelper.columnName,
DatabaseHelper.columnAge,
];
String whereString = '${DatabaseHelper.columnId} = ?';
int rowId = 1;
List<dynamic> whereArguments = [rowId];
List<Map> result = await db.query(
DatabaseHelper.table,
columns: columnsToSelect,
where: whereString,
whereArgs: whereArguments);
// print the results
result.forEach((row) => print(row));
// {_id: 1, name: Bob, age: 23}
}
The items in the whereArguments list get substituted in place of the ?s in the whereString. In this case there was only one ? so the whereArguments only had one item. If there were two ?s (for example an integer and a string), then you would have two items in the list.
Raw query
If you prefer the familiarity or flexibility of SQL code itself, you can do a raw query. In this example we will select any row whose name column is 'Mary'.
_query() async {
// get a reference to the database
Database db = await DatabaseHelper.instance.database;
// raw query
List<Map> result = await db.rawQuery('SELECT * FROM my_table WHERE name=?', ['Mary']);
// print the results
result.forEach((row) => print(row));
// {_id: 2, name: Mary, age: 32}
}
Be sure to use data binding using ? string replacements. This will guard against SQL injection attacks.
Notes
You will have to import the DatabaseHelper class and sqflite if you are in another file (like main.dart).
The SQFlite plugin uses a Map<String, dynamic> to map the column names to the data in each row.
Supplemental code
For your copy-and-paste convenience, here is the layout code for main.dart:
import 'package:flutter/material.dart';
// I called my project 'flutter_database_operations'. You can update for yours.
import 'package:flutter_database_operations/database_helper.dart';
import 'package:sqflite/sqflite.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'SQFlite Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('sqflite'),
),
body: RaisedButton(
child: Text('query', style: TextStyle(fontSize: 20),),
onPressed: () {_query();},
),
);
}
_query() async {
// get a reference to the database
Database db = await DatabaseHelper.instance.database;
// get all rows
List<Map> result = await db.query(DatabaseHelper.table);
// get single row
//List<Map> result = await db.query(DatabaseHelper.table,
// columns: [DatabaseHelper.columnId, DatabaseHelper.columnName, DatabaseHelper.columnAge],
// where: '${DatabaseHelper.columnId} = ?',
// whereArgs: [1]);
// raw query
//List<Map> result = await db.rawQuery('SELECT * FROM my_table WHERE name=?', ['Mary']);
// get each row in the result list and print it
result.forEach((row) => print(row));
}
}
Going on
This post is a development from my previous post: Simple SQFlite database example in Flutter. See that post for other SQL operations and advice.

Resources