How to make sqflite database faster? - flutter

I am trying to developing an app. In this app I need a local database. I choose sqflite database but it is very slow. It is taking so much time to fetch and insert data. It is my database helper class. As you can see in code that I have lot of rows. I am new at asking question if you need more information you can comment.
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart' as sqf;
class DataBaseHelper {
static Future<sqf.Database> database() async {
final dbPath = sqf.getDatabasesPath();
return sqf.openDatabase(
join(await dbPath, 'habits_record.db'),
onCreate: (db, version) async {
await db.transaction((txn) async {
var batch = txn.batch();
await txn.execute(
'''CREATE TABLE habitTable(id INTEGER PRIMARY KEY, title TEXT, reason TEXT,plan TEXT, iconData TEXT,hour INTEGER, minute INTEGER, notificationText TEXT, notificationId INTEGER,alarmHour INTEGER, alarmMinute INTEGER)''',
);
await txn.execute(
'''CREATE TABLE event(id TEXT PRIMARY KEY, dateTime TEXT, habitId INTEGER)''',
);
await txn.execute(
'''CREATE TABLE note(id TEXT PRIMARY KEY, dateTime TEXT, habitId INTEGER, Note TEXT)''',
);
await batch.commit();
});
},
version: 1,
);
}
static Future<void> insertNote(Map<String, Object> data) async {
final db = await DataBaseHelper.database();
db.insert('note', data, conflictAlgorithm: sqf.ConflictAlgorithm.replace);
}
static Future<void> deleteNote(String id) async {
final db = await DataBaseHelper.database();
await db.delete(
'note',
where: 'id = ?',
whereArgs: [id],
);
}
static Future<List<Map<String, dynamic>>> fetchAndSetNotes() async {
final db = await DataBaseHelper.database();
return await db.query('note');
}
static Future<void> updateNote(Map<String, Object> newNote) async {
final db = await DataBaseHelper.database();
final batch = db.batch();
batch.update(
'note', newNote, where: 'id = ?',
whereArgs: [newNote['id']],
);
batch.commit(continueOnError: true);
}
static Future<void> insertEvent(Map<String, Object> data) async {
final db = await database();
await db.insert('event', data,
conflictAlgorithm: sqf.ConflictAlgorithm.replace);
}
static Future<void> deleteEvent(String id) async {
final db = await DataBaseHelper.database();
await db.delete(
'event',
where: 'id = ?',
whereArgs: [id],
);
}
static Future<List<Map<String, dynamic>>> fethEvent() async {
final db = await DataBaseHelper.database();
return await db.query('event');
}
static Future<void> insertHabit(Map<String, Object> data) async {
final db = await database();
await db.insert('habitTable', data,
conflictAlgorithm: sqf.ConflictAlgorithm.replace);
}
static Future<List<Map<String, dynamic>>> habits() async {
final db = await DataBaseHelper.database();
return await db.query('habitTable');
}
static Future<void> deleteHabit(int id) async {
final db = await DataBaseHelper.database();
await db.delete(
'habitTable',
where: 'id = ?',
whereArgs: [id],
);
}
static Future<void> updateHabit(Map<String, Object> oneHabit) async {
final db = await DataBaseHelper.database();
await db.update(
'habitTable',
oneHabit,
where: 'id = ?',
whereArgs: [oneHabit['id']],
);
}
}

Here is the ObjectBox vs Hive vs
Sqflite performance benchmarks.
You can decide which one you want to go with.
In CRUD operations, you can see that sqflite is very slow when comparing with others.

If sqflite has not the speed you want , you can use hive or objectbox.

Related

Share data while the Internet is available using flutter

I have a query I would like to store some data in the cache in the event that the phone is not connected to the Internet and when I connect to the Internet, it is sent to the server. I would like to know the best scenario for this case...
And thank u.
You can try saving the data to the server and on failure you can write it in cache. Like
var response = await http.post(Uri.parse("api url here"),
body: json.encode(body),
);
if(response.statusCode != 200)
{
//save data in local like for example
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString("apiBody", json.encode(body);
}
If you need to store or receive a large amount of data, consider to store this datas in a localdatabase like sqflite: https://pub.dev/packages/sqflite
base on SQLLite, you can store and manipulate data with SQL queries.
Here is an example DB helper call that manipulate customer datas:
class DBProvider {
DBProvider._();
static final DBProvider db = DBProvider._();
late Database _database;
Future<Database> get database async {
_database = await initDB();
return _database;
}
initDB() async {
Directory documentsDirectory = await getApplicationDocumentsDirectory();
String path = join(
documentsDirectory.path,
"andidoor.db",
);
return await openDatabase(
path,
version: 2,
onOpen: (db) {},
onCreate: createDatabase,
onUpgrade: upgradeDatabase,
);
}
upgradeDatabase(Database db, int oldVersion, int newVersion) async {
print('Upgrade Database ...');
}
createDatabase(Database db, int version) async {
print('Create Database ...');
await db.execute("CREATE TABLE customers ("
"id INTEGER PRIMARY KEY,"
"user_id INTEGER,"
"company TEXT,"
"address_1 TEXT,"
"address_2 TEXT,"
"address_3 TEXT,"
"zipcode TEXT,"
"city TEXT,"
"country TEXT,"
"email TEXT,"
"phone TEXT,"
"status INTEGER,"
"synced TEXT"
")");
/**
* Customers helper functions
*/
Future<List<dynamic>> getCustomers() async {
Database db = await database;
return await db.rawQuery('SELECT * FROM customers ');
}
Future<void> populateCustomers(List<dynamic> customers) async {
final completer = Completer();
await deleteAllcustomers();
for (var i = 0; i < customers.length; i++) {
await addCustomer(Customer.fromMap(customers[i]), true);
}
completer.complete();
return completer.future;
}
Future<dynamic> deleteAllcustomers() async {
Database db = await database;
return await db.rawQuery('DELETE FROM customers');
}
Future<dynamic> deleteCustomer(String customerID) async {
final db = await database;
return db.delete(
"customers",
where: "id = ? ",
whereArgs: [customerID],
);
}
}
And in other dart file just use like this:
final db = DBProvider.db;
List<dynamic> allDatas = [];
allDatas = await db.getCustomers();

flutter sqflite clears table after hot restart

On creating my sqflite database,i create my table activity_table using the 'onCreate' parameter.
it starts with 0 records.
i can insert ,edit and delete records in it.
i can query and check the count of records. everything is working fine.
problem:
But after hot restart,the table is getting empty.
class ActivityDB {
static final ActivityDB instance = ActivityDB._internal();
ActivityDB._internal();
static Database? _database;
Future<Database> get database async {
if (_database != null) {
return _database!;
}
_database = await _init();
return _database!;
}
Future<Database> _init() async {
Database db = await openDatabase(
join(await getDatabasesPath(), 'activity.db'), //path
onCreate: (db, version) async {
await db.execute('''CREATE TABLE activity_table
(
id TEXT PRIMARY KEY,
title TEXT NOT NULL,
)''');
},
version: 1,
);
}
editActivity({required Activity activity,}) async {
Database db = await database;
await db.update(
'activity_table',
activity.toMap(),
where: 'id = ?',
whereArgs: [
activity.id,
],
);
}
addActivity(Activity activity) async {
Database db = await database;
await db
.insert(
'activity_table',
activity.toMap(),
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
removeActivity(String id) async {
Database db = await database;
await db.delete(
'activity_table',
where: 'id = ?',
whereArgs: [id],
);
}
On every hot restart allActivity() is empty
allActivity() async {
Database db = await database;
List<Map<String, dynamic>> activitiesMapFromDB = await db.query('activity_table');
return activitiesMapFromDB;
}
}

FLUTTER, SQFLITE ,How do I prevent deletion if related data still exists?

Hi guys I'm newbie in Flutter and currently having intern.
So I got 3 tables here, which are Item database is the main , group database, category database.
If the items databases is using the group, how should I prevent user delete group database because the group is in used in item database?
Will be great if someone can guide on me , because I'm totally new to this and because of intern I had to done it quickly..
import 'dart:async';
import 'dart:io' as io;
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path_provider/path_provider.dart';
import 'package:ezystock/model/item_model.dart';
import 'package:ezystock/model/group_model.dart';
import 'package:ezystock/model/category_model.dart';
class DBHelper {
static Database _db;
static const String DB_NAME = 'database.db';
Future<Database> get db async {
if (_db != null) {
return _db;
}
_db = await initDb();
return _db;
}
initDb() async {
io.Directory documentsDirectory = await getApplicationDocumentsDirectory();
String path = join(documentsDirectory.path, DB_NAME);
var db = await openDatabase(path, version: 1, onCreate: _onCreate);
return db;
}
_onCreate(Database db, int version) async {
await db.execute("CREATE TABLE 'item' ('id' INTEGER PRIMARY KEY, 'code' TEXT, 'description' TEXT,'cost' REAL,'price' REAL,'category' TEXT,'group' TEXT)");
await db.execute("CREATE TABLE 'group' ('id' INTEGER PRIMARY KEY, 'code' TEXT, 'description' TEXT)");
await db.execute("CREATE TABLE 'category' ('id' INTEGER PRIMARY KEY, 'code' TEXT, 'description' TEXT)");
}
//Category
Future<Category> saveCategory(Category category) async {
var dbClient = await db;
category.id = await dbClient.insert("category", category.toMap());
return category;
}
Future<List<Category>> getCategory() async {
var dbClient = await db;
List<Map> maps = await dbClient.query("category", columns: ["id", "code","description"]);
//List<Map> maps = await dbClient.rawQuery("SELECT * FROM $TABLE");
List<Category> category = [];
if (maps.length > 0) {
for (int i = 0; i < maps.length; i++) {
category.add(Category.fromMap(maps[i]));
}
}
return category;
}
Future<int> deleteCategory(int id) async {
var dbClient = await db;
return await dbClient.delete("category", where: 'id = ?', whereArgs: [id]);
}
Future<int> updateCategory(Category category) async {
var dbClient = await db;
return await dbClient.update("category", category.toMap(),
where: 'id = ?', whereArgs: [category.id]);
}
//Group
Future<Group> saveGroups(Group group) async {
var dbClient = await db;
group.id = await dbClient.insert("group", group.toMap());
return group;
}
Future<List<Group>> getGroups() async {
var dbClient = await db;
List<Map> maps = await dbClient.query("group", columns: ["id", "code","description"]);
//List<Map> maps = await dbClient.rawQuery("SELECT * FROM $TABLE");
List<Group> groups = [];
if (maps.length > 0) {
for (int i = 0; i < maps.length; i++) {
groups.add(Group.fromMap(maps[i]));
}
}
return groups;
}
Future<int> deleteGroups(int id) async {
var dbClient = await db;
List<Map> items = await dbClient.query("item", where: 'group = ?', whereArgs: [id]);
if (items.length > 0) {
return await dbClient.delete("group", where: 'id = ?', whereArgs: [id]);
} else {
}
}
Future<int> updateGroups(Group group) async {
var dbClient = await db;
return await dbClient.update("group", group.toMap(),
where: 'id = ?', whereArgs: [group.id]);
}
//items
Future<Items> saveItems(Items items) async {
var dbClient = await db;
items.id = await dbClient.insert("item", items.toMap());
return items;
}
Future<List<Items>> getItems() async {
var dbClient = await db;
List<Map> maps = await dbClient.query("item", columns: ["id", "code","description","cost","price","category","group"]);
//List<Map> maps = await dbClient.rawQuery("SELECT * FROM $TABLE");
List<Items> groups = [];
if (maps.length > 0) {
for (int i = 0; i < maps.length; i++) {
groups.add(Items.fromMap(maps[i]));
}
}
return groups;
}
Future<int> deleteItems(int id) async {
var dbClient = await db;
return await dbClient.delete("item", where: 'id = ?', whereArgs: [id]);
}
Future<int> updateItems(Items items) async {
var dbClient = await db;
return await dbClient.update("item", items.toMap(),
where: 'id = ?', whereArgs: [items.id]);
}
Future close() async {
var dbClient = await db;
dbClient.close();
}
}
The answer is: dont provide any method to delete group table.
So remove this code:
Future<int> deleteGroups(int id) async {
var dbClient = await db;
List<Map> items = await dbClient.query("item", where: 'group = ?', whereArgs: [id]);
if (items.length > 0) {
return await dbClient.delete("group", where: 'id = ?', whereArgs: [id]);
} else {
}

Flutter (dart) Delete by Sqflite

I need help :(
I'm trying to delete Data from a Sqflite Database.
I can insert and get the data to show, but I cannot delete it if I do a mistake in saving it.
I have my DB:
import 'package:sqflite/sqflite.dart' as sql;
import 'package:path/path.dart' as path;
import 'package:sqflite/sqflite.dart';
//SQFLITE
class DBHelper {
static Future<sql.Database> database() async {
final dbPath = await sql.getDatabasesPath();
//einmal die Datenbank erstellen in einer Variablen
return sql.openDatabase(path.join(dbPath, 'wanderwege.db'),
onCreate: (db, version) {
return db.execute(
'CREATE TABLE user_places(id TEXT PRIMARY KEY, title TEXT, image TEXT, createTime TEXT, place TEXT, description TEXT)');
// 'CREATE TABLE user_places(id TEXT PRIMARY KEY, title TEXT, image TEXT, loc_lat REAL, loc_lng REAL, address TEXT)');
}, version: 1);
}
//Da der Eintrag dauern kann bis es in die Daten gespeichert weerden = Future + async
static Future<void> insert(String table, Map<String, Object> data) async {
final db = await DBHelper.database();
db.insert(table, data, conflictAlgorithm: sql.ConflictAlgorithm.replace);
}
// Delete data from table
// deleteData(table, itemId) async {
// final db = await DBHelper.database();
// return await db.rawDelete("DELETE FROM $table WHERE id = $itemId");
// }
//Methode um Einträge zu holen
static Future<List<Map<String, dynamic>>> getData(String table) async {
final db = await DBHelper.database();
return db.query(table);
}
}
Then I have my Places:
import 'dart:io';
import 'package:flutter/widgets.dart';
import 'package:places/helpers/db_helper.dart';
import 'package:places/models/place.dart';
import 'package:intl/intl.dart';
class GreatPlaces with ChangeNotifier {
//To set Creation Time
static DateTime actualTime = DateTime.now();
String formattedDate = DateFormat('dd-MM-yyyy').format(actualTime);
List<Place> _items = [];
List<Place> get items {
return [..._items];
}
//Information des gesamten places
Place findById(String id) {
return items.firstWhere((place) => place.id == id);
}
Future<void> addPlace(
String pickedTitle,
File pickedImage,
String pickedDate,
String pickedLocation,
String pickedDescription,
//PlaceLocation pickedLocation,
) async {
//final address = await LocationHelper.getPlaceAddress(
// pickedLocation.latitude, pickedLocation.longitude);
// final updatedLocation = PlaceLocation(
// latitude: pickedLocation.latitude,
// longitude: pickedLocation.longitude,
// address: address);
final newPlace = Place(
id: DateTime.now().toString(),
image: pickedImage,
title: pickedTitle,
createTime: pickedDate,
place: pickedLocation,
description: pickedDescription,
);
//location: null);
_items.add(newPlace);
notifyListeners();
//übergabe 'wanderwege' so wie in db_helper definiert , Data ist von typ map
DBHelper.insert('user_places', {
'id': newPlace.id,
'title': newPlace.title,
'image': newPlace.image.path,
'createTime': newPlace.createTime,
'place': newPlace.place,
'description': newPlace.description
// 'loc_lat': newPlace.location.latitude,
// 'loc_lng': newPlace.location.longitude,
// 'address': newPlace.location.address,
});
}
//Die ganzen places aus der DB holen
Future<void> fetchAndSetPlaces() async {
final dataList = await DBHelper.getData('user_places');
_items = dataList
.map((item) => Place(
id: item['id'],
title: item['title'],
image: File(item['image']),
createTime: item['createTime'],
place: item['place'],
description: item['description'],
//location: null // latitude: item['loc_lat'],
// longitude: item['loc_lat'],
// address: item['address']),
))
.toList();
notifyListeners();
}
}
and now on the screen where all the Places listed I want to implement a delete function, but I don't get how it works... tried so much from youtube and the documentation from Sqflite, but I don't get it.
I hope someone can help me.
Greetings :)
I've made this simple helper file (like you have done) for a little app that uses sqflite as backend in local. It's complete: it accesses to the local file system to get grant permissions, it opens the file for write , and you can make also Batch updates:
import 'dart:async';
import 'package:sqflite/sqflite.dart';
class DBClient {
final logger = Log.getLogger('\u{1F5AB} DBClient '); // a little logger utility, you can substitute with the simple 'print(...)' function.
static final String dbFilename = 'my_database.db';
final Permission permission;
Database _database;
bool openingDB = false;
DBClient(this.permission) : assert(permission != null);
Batch batch() {
return _database.batch();
}
Future<void> init() async {
if (_database == null && !openingDB) {
String path;
try {
path = await permission.getAbsoluteFileName(dbFilename);
} on Error {
logger.e('Error: cannot open db');
rethrow;
}
openingDB = true;
logger.d('Opening database on path: $path');
_database = await openDatabase(path, version: 1,
onCreate: (Database db, int version) async {
await db.execute("""
CREATE TABLE IF NOT EXISTS ......
""");
await db.execute("""
CREATE TABLE IF NOT EXISTS ....
""");
....
await db.execute("""
CREATE TABLE IF NOT EXISTS ....
""");
});
}
}
Future<int> insert(String tableName, Map<String, dynamic> json) async =>
_database.insert(tableName, json);
Future<List<Map<String, dynamic>>> query(
String tableName, {
String where,
List<dynamic> whereArgs,
}) async {
return _database.query(
tableName,
where: where,
whereArgs: whereArgs,
);
}
Future<int> update(String tableName, Map<String, dynamic> json,
{String where, List<dynamic> whereArgs}) async {
return _database.update(
tableName,
json,
where: where,
whereArgs: whereArgs,
);
}
Future<int> delete(String tableName,
{String where, List<dynamic> whereArgs}) async {
return _database.delete(
tableName,
where: where,
whereArgs: whereArgs,
);
}
}
You can use in this manner:
deleted = await db.delete(Place.tableName(),
where: "name = ?", whereArgs: [name]);
Hint: in the init() method you can use AsyncMemoizer class, it is better to grant that the method is called only once (at the time I wrote the app I didn't do it....)
Hereafter my (very old) dependencies:
dependencies:
date_utils: ^0.1.0+3
equatable: ^1.1.1
logger: ^0.8.3
permission_handler: ^5.0.0+hotfix.3
quiver: ^2.1.3
sqflite: ^1.3.0
path_provider: ^1.6.5
flutter:
sdk: flutter
String q = "DELETE FROM Test WHERE id= '$id'";
Just use this:
await db.rawDelete('DELETE FROM $table WHERE id = ?',[itemId],);
Just use this function with parameter as id
Future<int> delete(int id) async {
return await db.delete(tableName, where: '$columnId = ?', whereArgs: [id]);}

How do I get data from an sqflite table and display it as a % inside text widget

import 'dart:io';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path_provider/path_provider.dart';
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
)
''');
}
// Helper methods
// Inserts a row in the database where each key in the Map is a column name
// and the value is the column value. The return value is the id of the
// inserted row.
Future<int> insert(Map<String, dynamic> row) async {
Database db = await instance.database;
return await db.insert(table, row);
}
// All of the rows are returned as a list of maps, where each map is
// a key-value list of columns.
Future<List<Map<String, dynamic>>> queryAllRows() async {
Database db = await instance.database;
return await db.query(table);
}
// All of the methods (insert, query, update, delete) can also be done using
// raw SQL commands. This method uses a raw query to give the row count.
Future<double> queryRowCount() async {
Database db = await instance.database;
List<Map<String, dynamic>> x = await db.rawQuery('SELECT COUNT(*) FROM $table');
int rowCount = Sqflite.firstIntValue(x);
return rowCount.toDouble();
}
// We are assuming here that the id column in the map is set. The other
// column values will be used to update the row.
Future<int> update(Map<String, dynamic> row) async {
Database db = await instance.database;
int id = row[columnId];
return await db.update(table, row, where: '$columnId = ?', whereArgs: [id]);
}
// Deletes the row specified by the id. The number of affected rows is
// returned. This should be 1 as long as the row exists.
Future<int> delete(int id) async {
Database db = await instance.database;
return await db.delete(table, where: '$columnId = ?', whereArgs: [id]);
}
Future<List<Map<String, dynamic>>> queryOmnivore() async {
Database db = await instance.database;
return await db.query(table, where: '$columnName = ?', whereArgs: ['omnivore']);
}
Future<List<Map<String, dynamic>>> queryPescatarian() async {
Database db = await instance.database;
return await db.query(table, where: '$columnName = ?', whereArgs: ['pescatarian']);
}
Future<List<Map<String, dynamic>>> queryVegetarian() async {
Database db = await instance.database;
return await db.query(table, where: '$columnName = ?', whereArgs: ['vegetarian']);
}
Future<int> queryVegetarianCount() async {
var vegList = await queryVegetarian();
int count = vegList.length;
return count;
}
Future<double> queryOmnivoreCount() async {
var omniList = await queryOmnivore();
int omniCount = omniList.length;
return omniCount.toDouble();
}
Future<double> calcOmnivorePercentage() async {
var x = await queryOmnivoreCount();
var y = await queryRowCount();
double omniPercentage = (x / y) * 100;
return omniPercentage;
}
}
Hey Folks!
I was hoping someone may be able to help me please?!
I'm trying to figure out how to take data out of a a sqflite table I've created, perform a calculation that expresses it as a percentage of the other values, and display it inside a text widget in the app.
I've actually managed to get the result to print in the console using this code:
void omnivorePercentageQ() async {
final omni = await dbHelper.calcOmnivorePercentage();
print('omnivore percentage: ${omni.toStringAsFixed(1)}');
}
But I have no idea how to get it to show up in a text widget in the app itself.
Any ideas would be greatly appreciated!
Thank you,
Jason
you are not far off the answer, and already catch the value of the calculation needed. As i can see you dont need to pass any parameters to the function so i would recomend using a futurebuilder:
return FutureBuilder(
future: dbHelper.calcOmnivorePercentage(),
builder: (context, AsyncSnapshot<double> snapshot) {
if (snapshot.hasData) {
return Center( child: Text('${snapshot.data.toStringAsFixed(1)}'),);
}else
return Center(
child: CupertinoActivityIndicator(),
);
});
The Future Builder class https://api.flutter.dev/flutter/widgets/FutureBuilder-class.html
Serves to manage widgets that depend on futures, since your calculation and database querys are async you can check its state (As inside the widget in snapshot.hasData). That conditional checks if the future has finished and else shows an indicator. Hope it helps