Access SQLite DB in Flutter - database

I am creating a DB in my native Android code using SQLite. Now I want to access the same DB in Flutter, below is my code:
class DatabaseHelper
{
static final _dbName="abc.db";
static final _dbVersion=1;
DatabaseHelper._privateConstructor();
static final DatabaseHelper instance=DatabaseHelper._privateConstructor();
static Database _database;
Future<Database> get datatbase async
{
if(_database!=null) {
print(_database.path);
return _database;
}/* */
}
Future<List<Map<String,dynamic>>> getAllLogs()async
{
final Database db= await instance.datatbase;
return await db.query("calls_history");
}
Whenever I call getAllLogs in my Flutter Widget I get the error: calls_history doesn't exist.
However, when I run the same query on native, it returns me the result

On flutter side you can use sqflite plugin, this plugin is built on top of native SQLite so you don't need to do anything special just refer to the same database name in flutter.
In one of my flutter projects I have used native android to code to receive messages and save them in SQLite database and on the flutter side I have used same database using sqflite to display the contents of database.
Here is the flutter side code
import 'dart:async';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
class SMSHelper {
Database? db;
Future open() async {
db = await openDatabase(
// by default path for database on the device is /data/data/<your app id>/databases/<your database file.db>
join(await getDatabasesPath(), 'ofs_sms_database.db'),
version: 1, onCreate: (Database db, int version) async {
await db.execute(
"CREATE TABLE smslogs(id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, employeeID TEXT, department TEXT, module TEXT, message TEXT, safeUnsafeStatus TEXT, contactNo Text, dateTime INTEGER)");
});
}
Future<void> insertSMS(SMSLog smsLog) async {
await db?.insert(
'smslogs',
smsLog.toMap(),
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
Future<List<SMSLog>> getAllSMS() async {
if (db == null) {
open();
}
final List<Map<String, dynamic>>? maps = await db?.query('smslogs');
// Convert the List<Map<String, dynamic> into a List<Dog>.
if (maps != null) {
return List.generate(maps.length, (i) {
return SMSLog(
employeeID: maps[i]['employeeID'],
department: maps[i]['department'],
module: maps[i]['module'],
message: maps[i]['message'],
safeUnsafeStatus: maps[i]['safeUnsafeStatus'],
contactNo: maps[i]['contactNo'],
dateTime: maps[i]['dateTime']);
});
} else {
return [];
}
}
Future close() async => db?.close();
}
class SMSLog {
final String employeeID;
final String department;
final String module;
final String message;
final String safeUnsafeStatus;
final String contactNo;
final int dateTime;
SMSLog(
{required this.employeeID,
required this.department,
required this.module,
required this.message,
required this.safeUnsafeStatus,
required this.contactNo,
required this.dateTime});
Map<String, dynamic> toMap() {
return {
'employeeID': employeeID,
'department': department,
'module': module,
'message': message,
'safeUnsafeStatus': safeUnsafeStatus,
'contactNo': contactNo,
'dateTime': dateTime
};
}
}

Related

Flutter Sqflite database DatabaseException no such table Is Occur

[ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: DatabaseException(no such table: MST_University (code 1 SQLITE_ERROR): , while compiling: SELECT * FROM MST_University) sql 'SELECT * FROM MST_University' args []
import 'dart:developer';
import 'dart:io';
import 'package:flutter/services.dart';
import 'package:path_provider/path_provider.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
class DataBaseHelper {
static var _database;
DataBaseHelper._privateConstructor();
static final DataBaseHelper instance = DataBaseHelper._privateConstructor();
Future<Database> get database async {
if (_database != null) {
log("Database new is not creatred and retuned ");
return _database;
}
// Call init Databasee method for New Database Creation
log("new Database is create");
_database = await _initDatabase();
return _database;
}
Future<Database> _initDatabase() async {
Directory appDocDir = await getApplicationDocumentsDirectory();
log("new dAta");
String databasePath = join(appDocDir.path, 'pharmacy2.db');
return await openDatabase(
databasePath,
version: 2,
);
}
Future<void> copyPasteAssetFileToRoot() async {
Directory documentsDirectory = await getApplicationDocumentsDirectory();
log("assets database is loading");
String path = join(documentsDirectory.path, "pharmacy2.db");
if (FileSystemEntity.typeSync(path) == FileSystemEntityType.notFound) {
ByteData data =
await rootBundle.load(join('assests/database', 'pharmacy2.db'));
List<int> bytes =
data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes);
await File(path).writeAsBytes(bytes);
}
}
Future<void> getAllUniversity() async {
Database db = await instance.database;
var result = await db.query("MST_University");
log(result.toString());
}
}
I try a privet constructor for single instance and also load database from a assets
I cannot see where you call copyPasteAssetFileToRoot, so maybe never?
assests/database looks very much like a typo.
Even if that all is correct, the database you open has no such table.
Why don't you set a few breakpoints in your debugger and find out what steps work and which don't. Whether you path is set correctly and your file gets copied or not. Whether you open the correct file. Whether that file actually has such a table.

Non-nullable instance field 'id' must be initialized, Non-nullable instance field '_database' must be initialized. in flutter

could anyone help me with how to manage this problem? I am trying to code exactly likes the course's video, but I got this problem:
I'm getting (Non-nullable instance field 'id' must be initialized.
Non-nullable instance field 'miles' must be initialized.
Non-nullable instance field 'name' must be initialized. Non-nullable instance field '_database' must be initialized.)
. errors in my project.
My car class:
import './dbhelper.dart';
class Car {
int id;
String name;
int miles;
Car(this.id, this.name, this.miles);
**GET ERROR FROM Car.fromMap..**
Car.fromMap(Map<String, dynamic> map) {
** Non-nullable instance field 'id' must be initialized.
Non-nullable instance field 'miles' must be initialized.
Non-nullable instance field 'name' must be initialized.**
id = map['id'];
name = map['name'];
miles = map['miles'];
}
Map<String, dynamic> toMap() {
return {
DatabaseHelper.columnId: id,
DatabaseHelper.columnName: name,
DatabaseHelper.columnMiles: miles,
};
}
}
My dbHelper.dart:
import './car.dart';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
class DatabaseHelper {
static final _databaseName = "cardb.db";
static final _databaseVersion = 1;
static final table = 'cars_table';
static final columnId = 'id';
static final columnName = 'name';
static final columnMiles = 'miles';
** GET ERROR FROM DatabaseHelper._provateConstructor(); AND _database;
Non-nullable instance field '_database' must be initialized.**
DatabaseHelper._privateConstructor();
static final DatabaseHelper instance = DatabaseHelper._privateConstructor();
** GET ERROR Non-nullable instance field '_database' must be initialized.**
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 {
String path = join(await getDatabasesPath(), _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 AUTOINCREMENT,
$columnName TEXT NOT NULL,
$columnMiles 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(Car car) async {
Database db = await instance.database;
return await db.insert(table, {'name': car.name, 'miles': car.miles});
}
// 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);
}
// Queries rows based on the argument received
Future<List<Map<String, dynamic>>> queryRows(name) async {
Database db = await instance.database;
return await db.query(table, where: "$columnName LIKE '%$name%'");
}
// 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<int> queryRowCount() async {
Database db = await instance.database;
return Sqflite.firstIntValue(await db.rawQuery('SELECT COUNT(*) FROM $table'));
}
// 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(Car car) async {
Database db = await instance.database;
int id = car.toMap()['id'];
return await db.update(table, car.toMap(), 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]);
}
}
You have to put the null aware operator in the variables type for example:
class Car {
int? id; // <---- add a ? to int so it will be int?
String? name; // <-- same here
int? miles; // <--- and here
Car({this.id, this.name, this.miles});
Car.fromMap(Map<String, dynamic> map) {
id = map['id'];
name = map['name'];
miles = map['miles'];
}
Map<String, dynamic> toMap() {
return {
DatabaseHelper.columnId: id,
DatabaseHelper.columnName: name,
DatabaseHelper.columnMiles: miles,
};
}
}
Null safety as it is explain in the dart documentation:
When you opt into null safety, types in your code are non-nullable by
default, meaning that variables can’t contain null unless you say they
can. With null safety, your runtime null-dereference errors turn into
edit-time analysis errors.
More information in: https://dart.dev/null-safety
Maybe the tutorial you are taking was previous the null aware implementation and it does not apply the null aware operators, you can do the same at the beggining of the type declartion where the error is happening in other classes, you just have to add ? to the variable type at the beginning of the variable.

Flutter sqflite insert does not store full data fetched using http get

I'm new to Flutter
my app fetches json records from the http get method and stores the data to database.
very thing works fine json returns around 149 rows but app only inserts 101 row in the table there is no error or anything.
Firstly I thought that http only response with only 101 records but on testing http get prints all the 149 record but on insert to database it only insert's only 101 only
if I parse 80 or 90 records it all gets inserted but even 102 records only insert's 101
even tried Future Delay for 10s but it gets break at 101 and then waits till the delay in competed
APIProvider
class ApiProvider {
Future<List<NewProduct>?> getPro() async {
Response response = await Dio().get(Info.productUrl);
var res = response.data;
print("PRODUCT LENGTH-" + res.length);
(res as List).map((product) {
DatabaseHelper.db.insertProduct(NewProduct.fromJson(product));
}).toList();
}
}
Database Helper Class
import 'dart:io';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
import 'package:models/newProductHome.dart';
import 'package:sqflite/sqflite.dart';
class DatabaseHelper {
static final _dbName = 'sawanDB.db';
static final _dbVersion = 1;
static Database? _database;
static final DatabaseHelper db = DatabaseHelper._();
DatabaseHelper._();
Future<Database> get database async {
if (_database != null) return _database!;
_database = await _initiateDatabase();
return _database!;
}
_initiateDatabase() async {
Directory directory = await getApplicationDocumentsDirectory();
String path = join(directory.path, _dbName);
return await openDatabase(path, version: _dbVersion, onCreate: _onCreate);
}
Future _onCreate(Database db, int version) async {
await db.execute('DROP TABLE If EXISTS SA_PRO_MASTER');
await db.execute('''
CREATE TABLE SA_PRO_MASTER(
sa_proid TEXT,
sa_proname TEXT NOT NULL,
sa_pcat TEXT NOT NULL,
sa_pscat TEXT NOT NULL,
sa_pbrand TEXT NOT NULL,
sa_pro_code TEXT,
sa_pro_capacity TEXT,
sa_pro_status TEXT,
sa_pro_mtitle TEXT,
sa_pro_mkey TEXT,
sa_pro_mdesc TEXT,
sa_pro_date DATE,
sa_pro_mainImg TEXT,
sa_pro_ishome TEXT,
sa_pro_desc TEXT,
sa_pro_new TEXT,
sa_pro_type TEXT
)
''');
print("DB TABLE SA_PRO_MASTER CREATED");
}
insertProduct(NewProduct listPro) async {
final db = await database;
final res = await db.insert('SA_PRO_MASTER', listPro.toJson());
//print("INSERT RES - " + res.toString());
return res;
}
Future<int> deleteAllProduct() async {
final db = await database;
final res = await db.rawDelete('DELETE FROM SA_PRO_MASTER');
return res;
}
}
Main where it loads Data while opening the app
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
FirebaseMessaging.onBackgroundMessage(backgroundHandler);
DatabaseHelper.db.database;
if (await InternetConnectionChecker().hasConnection) {
print("INTERNERT AVAILABLE");
DatabaseHelper.db.deleteAllProduct();
await apiProvider.getAllProducts();
await Future.delayed(const Duration(seconds: 1));
runApp(MyApp());
} else {
print("NO INTERNET");
runApp(NoConn());
}
//await Future.delayed(const Duration(seconds: 1));
}

How to save nested list with sqflite in flutter?

Here I want to add bookmark functionality in my flutter news application. I get data from APIs. I display that data below.
This Image shows you how I get data from APIs
I am using this snippet for saving data with SQflite which I display below. I save this file with name bookmark_db_provider.dart.
import 'dart:io';
import 'home_screen_data.dart';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
import 'package:sqflite/sqflite.dart';
class DBProvider {
static Database _database;
static final DBProvider db = DBProvider._();
DBProvider._();
Future<Database> get database async {
if (_database != null) _database = await initDB();
return _database;
}
initDB() async {
Directory documentsDirectory = await getApplicationDocumentsDirectory();
final path = join(documentsDirectory.path, 'ProductData.db');
return await openDatabase(path, version: 1, onOpen: (db) {},
onCreate: (Database db, int version) async {
await db.execute('CREATE TABLE ProductData('
'id INTEGER PRIMARY KEY,' //id
'categoryName Text,' //headline
'publisherName Text,' //description
'isAvailable Text,' //content
'categoryImgUrl Text' //image
')');
});
}
createProductData(ProductData productData) async {
await deleteAllProductData();
final db = await database;
final res = await db.insert('ProductData', productData.toJson());
return res;
}
Future<int> deleteAllProductData() async {
final db = await database;
final res = await db.delete('DELETE FROM ProductData');
return res;
}
Future<List<ProductData>> getProductDataList() async {
final db = await database;
final res = await db.rawQuery("SELECT * FROM ProductData");
List<ProductData> list = res.isNotEmpty ? res.map((x) => ProductData.fromJson(x)).toList() : [];
return list;
}
}
So, I want to how to save data and get data this with SQflite database in flutter. How I accomplish this?
This is an old question and hopes you have gotten an answer to it. However, a lot of flutter dev using sqflite struggles with handling data in a nested array format of the type mentioned e.g
``` {
"id": 1,
"name": "xyz",
"images": [
{
"imageId": 1,
"image": "image1"
},
{
"imageId": 2,
"image": "image2"
}
]
}
```
Since json handling is not part of sqflite at the moment, it is suggested to either;
a., save inner array as a string/text field in the 'image' column of table 'data' like
**
"[{"imageId": 1, "image": 'image1'}, {"imageId": 2, "image":
'image2'},}"
** , no guaranty.
or, b., flatten out the inner array so as to have only **
data[id, name, image1, image2, image3,...].
** this approach may be possible in a simple array as given but in a complex system. flattening out may be really cumbersome.
my suggestion, create two tables, one for data, and another for images. let each row of images table have reference or relationship with corresponding data table row. Your data class and images class will be something like,
```
class Data {
int dataId;
String name;
List<Image> images;
data({this.id, this.images, this.name});
...
```
and
```
class Image {
int imageId;
int dataId;
String image;
Image({this.imageId, this.dataId, this.image});
...
}
```
Your sqflite table data will have only two fields 'dataId' and 'name' and the image table must include 'dataId' as the relationship between the two tables.
to save data, you can use transactions like
```
void saveData(Data data, Map<String, Object> map) async {
await db.execute(""" INSERT INTO "data" (name) values (?) """, [data.name]);
// retrieve dataId of the new row inserted. last_inserted_rowid can also be used if the database does not contain several tables that may have been updated or saved before completing the transaction.
int dataId;
List<Map> x = await db.query('data', columns: ['dataId'], where: 'name = ?', whereArgs: [data.name]);
dataId = x[x.length - 1]['dataId'];
db.transaction((txn) async {
var batch = txn.batch();
data.images
.map((e) => {
batch.rawInsert(
""" INSERT INTO "images" (dataId,image,) values (?,?,?,? ) """, [dataId, e.image])
}).toList();
});
}
```
to retrieve data and images, you can try something like
```
Data _data = new Data();
Future<void> fetchData() async {
if (db != null) {
// do not execute if db is not instantiate
//retrieve all data from data table and store in instantiated, also instantiate images array as empty list
final dataList = await db.query(data);
_data = (dataList as List)
.map(
(data) => Data(
dataId: data['dataId'],
name: data['name'],
images: data['images'] != null ? data['images'] : []
)) .toList();
_data.forEach((data) async {if (data.images != null) {
List<Image> _images = [];
var imageResults = await db.query(images,where: 'dataId =?', whereArgs: [data.dataId]);
_images = (imageResults as List).map((e) => Image(
imageId: e['imageId'],
dataId: e['dataId'],
image: e['image']
)).toList();
_data.images.addAll(_images);
} });
}
}
```
with that approach, you should be able to handle nested array in flutter and sqflite
I might be unclear with your question, but according to what I understood,
You need to call the method of this provider with data that you want:
DBProvider.init() // Use this only one which when the application is instaled
After that, you can call these methods from anywhere to put and get data
DBProvider.createProductData(productData) //insert data
Get data
DBProvider.getProductDataList() //get data

How to create multiple table in Sqlite database at different times in Flutter

So Initially I created a table named "TABLE" in the database. I wanted to add another table. So I added another query to create a table. However I get an error saying "TABLE1 does not exist" when I run the app.
I do feel like there is a flaw in my code. I think the _onCreate() method will only be called the first time I run the app. So any code I add on _onCreate() method afterwards will not run. Any help will be appreciated.
class DBHelper {
static Database _db;
static const String DB_NAME = 'employeeDB';
static const String ID = 'id';
static const String NAME = 'name';
static const String TABLE = 'Employee';
static const String ID1 = 'id1';
static const String NAME1 = 'name1';
static const String TABLE1 = 'Employee1';
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 $TABLE ($ID INTEGER PRIMARY KEY,$NAME TEXT)");
await db.execute("CREATE TABLE $TABLE1 ($ID1 INTEGER PRIMARY KEY,$NAME1 TEXT)");
}
Future<Employee> save(Employee employee) async {
var dbClient = await db;
employee.id = await dbClient.insert(TABLE, employee.toMap());
return employee;
}
Future<Employee1> saveEmp1(Employee1 employee) async {
var dbClient = await db;
employee.id = await dbClient.insert(TABLE1, employee.toMap());
return employee;
}
Future<List<Employee>> getEmployees() async {
var dbClient = await db;
List<Map> maps = await dbClient.query(TABLE, columns: [ID, NAME]);
List<Employee> employees = [];
if (maps.length > 0) {
for (int i = 0; i < maps.length; i++) {
employees.add(Employee.fromMap(maps[i]));
}
}
return employees;
}
Future<List<Employee1>> getEmployees1() async {
var dbClient = await db;
List<Map> maps = await dbClient.query(TABLE1, columns: [ID1,
NAME1]);
List<Employee1> employees = [];
if (maps.length > 0) {
for (int i = 0; i < maps.length; i++) {
employees.add(Employee.fromMap(maps[i]));
}
}
return employees;
}
}
The first time run this app and initDb() in emulator, the db file employeeDB has created.
and it will not be created again
For only test app execution,
you can change
String DB_NAME = 'employeeDB'
to another name
String DB_NAME = 'employeeDB1'
or you can uninstall this app from Emulator first then run it again.
source code snippet of https://github.com/tekartik/sqflite/blob/master/sqflite/lib/sqlite_api.dart
/// Called when the database is created.
OnDatabaseCreateFn onCreate;
For schema migration, you can use OnUpgrade, detail reference https://efthymis.com/migrating-a-mobile-database-in-flutter-sqlite/
code snippet
await openDatabase(path,
version: 1,
onCreate: (Database db, int version) async {
await db.execute(initialSchema));
},
onUpgrade: (Database db, int oldVersion, int newVersion) async {
await db.execute(migrationScript));
});
You have to create migrations and run them against your existing database using the onUpgrade handler. Basically you need to check the existing database version and upgrade if the version is smaller than the migration number.
You can check out the detail steps/code tutorial here.
I needed to create more than just multiple tables; but multiple database. I have since developed a dart package; sqlite_at_runtime that stretches to as far as creating all these entities at run time.
Below is an example code that creates three tables with similar attributes.
await Sqlartime.tableCreate(['sample1','sample2','sample3'],['name TEXT','age INTEGER','temp REAL']);

Resources