sqflite broke on Android after adding a new table Flutter - sqlite

I just added a new "Translations" table to the database init and wrote all the crud methods. Commands db.query and db.insert work just fine, but as tried inside updateVersion() either db.delete or db.update throw the missing plugin error :
ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: MissingPluginException(No implementation found for method update on channel com.tekartik.sqflite).
After a while I realised that also older two tables are now throwing the same error on delete and update command. Looks like adding a new table broke it all.. I tried flutter clean but nothing changed, so I uninstalled and reinstalled the app, but I still get same errors. I then commented out new table and reinstalled the app but still get the errors..I also tried invalidating the cache a restart but still the same..
On iPhone dough it does not throw any error..
In iOS CoreData kinda behaves the same when you change something.. but with a fresh app install it all resets.. here it seems that the db is still written on disk..
How do I make sure I erased it? I tried await deleteDatabase(path);.
Any Idea of what's going on?
As always thank you very much for your time and help.
This is the db:
class DBProvider {
//1.Create a private constructor that can be used only inside the class :
DBProvider._();
static final DBProvider db = DBProvider._();
//2.Setup the database
//Next we will create the database object and provide it with a getter
//where we will instantiate the database if it’s not (lazy initialization).
static Database _database;
Future<Database> get database async {
if (_database != null) return _database;
// await Sqflite.devSetDebugModeOn(true);
// if _database is null we instantiate it
_database = await initDB();
return _database;
}
// If there is no object assigned to the database,
// we use the initDB function to create the database.
// In this function, we will get the path for storing the database
// and create the desired tables:
initDB() async {
Directory documentsDirectory = await getApplicationDocumentsDirectory();
String path = join(documentsDirectory.path, "Fixit.db");
//TODO: tried deleting
// await deleteDatabase(path); // still errors
return await openDatabase(path, version: 1, onOpen: (db) {},
onCreate: (Database db, int version) async {
await db.execute("CREATE TABLE Route("
"routeId TEXT,"
"routeName TEXT,"
"routeDistance TEXT,"
"routeDuration TEXT,"
"coordinates TEXT"
")");
await db.execute("CREATE TABLE Alarm("
"alarmId TEXT,"
"alarmName TEXT,"
"enabled BOOL,"
"repeatWeekdays TEXT,"
"time TEXT,"
"sound TEXT,"
"routeName TEXT"
")");
// await db.execute("CREATE TABLE Translations("
// "version TEXT"
// ")");
});
}
}
and these are the CRUD methods:
class DefaultsDbRepository {
var database = DBProvider.db.database;
Future<int> checkVersion() async {
final db = await database;
try {
var res = await db.query('Translations');
assert(res != null);
int version = res != null ? int.parse(res.first['version']) : 0;
print('checkVersion() db version is: $version');
return version;
} catch (err) {
print(err);
}
}
saveVersion({int version}) async {
print('saveVersion() version to save is : $version');
assert(version != null);
final db = await database;
try {
Map<String, dynamic> map = {'version': version};
db.insert("Translations", map);
} catch (e) {
print(e);
}
}
updateVersion({int newVersion, int oldVersion}) async {
print('updateVersion() version to save is : $newVersion');
assert(newVersion != null);
final db = await database;
db.delete("Translations");
try {
// Map<String, dynamic> map = {'version': newVersion};
//
// db.insert("Translations", map);
// db.update("Translations", map); // [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: MissingPluginException(No implementation found for method update on channel com.tekartik.sqflite)
// db.update("Translations", map,
// where: "version = ?", whereArgs: [oldVersion]);
} catch (e) {
print(e);
}
}
}

Finally made it work again. After quite a few, uninstall, flutter clean and reinstalls I saw the problem. I was using ' instead of " in my db methods.
So I changed'ed from:
saveVersion({int version}) async {
print('saveVersion() version to save is : $version');
assert(version != null);
final db = await database;
try {
Map<String, dynamic> map = {'version': version};
db.insert("Translations", map);
} catch (e) {
print(e);
}
}
to:
saveVersion({int version}) async {
print('saveVersion() version to save is : $version');
assert(version != null);
final db = await database;
try {
Map<String, dynamic> map = {"version": version};
db.insert("Translations", map);
} catch (e) {
print(e);
}
}
and all commands are working as expected.
Hope this will be of help to others.
Cheers.

Related

Flutter Unhandled Exception: LateInitializationError: Local 'name' has not been initialized

I have a flutter app which tries to compare two sets of app version numbers, the first version number is stored locally using hive and the second version number is from firestore. I can fetch the data from firestore but I cannot get to compare both since it takes a while to fetch data from firestore.
This is the code to fetch data from firestore
late final Box detailsBox;
#override
void initState() {
super.initState();
detailsBox = Hive.box('appDetails');
updateApplication();
}
CollectionReference groceries =
FirebaseFirestore.instance.collection('updates');
late String? name;
late String? version;
late String? downloadUrl;
getData() {
groceries.orderBy('name').snapshots().listen((gets) {
try {
for (var gettt in gets.docs) {
name = gettt['name'] ?? 'null';
version = gettt['version'] ?? 'null';
downloadUrl = gettt['download url'] ?? 'null';
debugPrint('name: $name');
debugPrint('version: $version');
debugPrint('downloadUrl: $downloadUrl');
_addInfo(name!, version!, downloadUrl!); }
} catch (e) {
print(e);
}
});
}
This is the code to compare the version numbers
int getExtendedVersionNumber(String version) {
List versionCells = version.split('.');
if (kDebugMode) {
print(versionCells);
}
versionCells = versionCells.map((i) => int.parse(i)).toList();
return versionCells[0] * 10000 + versionCells[1] * 100 + versionCells[2];
}
Future compareData() async {
await getData();
String localName = detailsBox.get('name');
String localVersion = detailsBox.get('version');
String downloadLink = detailsBox.get('downloadLink');
debugPrint(
'Info retrieved from detailsBox below:\n $localName\n ($localVersion) \n $downloadLink');
debugPrint(
'Info retrieved from firebase below:\n $name\n ($version) \n $downloadUrl');
int version1Number = getExtendedVersionNumber(localVersion); // return 102003
int version2Number = getExtendedVersionNumber(version!); // return 102003
if (kDebugMode) {
print(version1Number == version2Number);
print(version1Number > version2Number);
print(version1Number < version2Number);
}
if (version2Number > version1Number) {
debugPrint('true');
debugPrint(downloadUrl);
}
}
When it gets to this point debugPrint( 'Info retrieved from firebase below:\n $name\n ($version) \n $downloadUrl'); I get the late initialization error [ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: LateInitializationError: Field 'name' has not been initialized.
How can I modify the code such that when it runs I can account for the time it takes to get data then finally compare the versions
Just change
late String? name;
to
String? name;

Get data from Google Cloud Firestore

[![I am unable to pass data of collection and document from android project to PCL,the way I tried threw null reference exception please help me with that.
Thanks
Android project to get data
FirestoreDb firestore = FirestoreDb.Create("iyf-raipur");
public async Task<IEnumerable<T>> GetCouncelorsAsync()
{
Query preachers = firestore.Collection(nameof(T));
QuerySnapshot snapshots = await preachers.GetSnapshotAsync();
foreach(DocumentSnapshot documentSnapshot in snapshots.Documents)
{
T data = documentSnapshot.ConvertTo<T>();
System.Console.WriteLine();
}
return (IEnumerable<T>)preachers;
}
PCL ViewModel Code
private ObservableCollection<Preacher> preachers;
private IRepository<Preacher> _repository = DependencyService.Get<IRepository<Preacher>>();
public PreacherViewModel()
{
Preachers = new ObservableCollection<Preacher>();
PopulatePreachers();
}
async void PopulatePreachers()
{
try
{
var list = await _repository.GetPreachersAsync();
foreach (var preacher in list)
{
Preachers.Add(preacher);
name = preacher.Name;
email = preacher.Email;
}
}
catch (Exception ex)
{
await Shell.Current.DisplayAlert("ERROR", ex.Message, "ok");
}
}

"native "AssertionError_throwNew" " error with Firebase Firestore

I have this function that checks if a document exists or not, if exists returns true otherwise false :
Future<bool> checkMissingId(String id) async {
String str = id.toLowerCase();
String letter = str[0];
String path = letter + "/" + str;
try {
final snapShot =
await FirebaseFirestore.instance.collection(path).doc(str).get();
if (snapShot == null || !snapShot.exists) {
return true;
} else
return false;
} catch (e) {
print(e);
return false;
}
}
But when I call it from here (after save and validate form ) :
Future<void> _submit() async {
//Create Artist and send it to the database
if (_validateAndSaveForm()) {
await checkMissingId(userNameF);
}
}
All freezes and it opens a new file called "errors_patch.dart" with this exception :
static _doThrowNew(int assertionStart, int assertionEnd, Object? message)
native "AssertionError_throwNew";
I think the problem is that checkMissingId is of type Future and maybe I'm not handling futures in the right way...But the error highlights also await FirebaseFirestore.instance.collection(path).doc(str).get(); so I don't know exactly how isolate the problem.
This is the stack :
assert(isValidCollectionPath(collectionPath),
await FirebaseFirestore.instance.collection(path).doc(str).get();
with a red quotes :
_AssertionError ('package:cloud_firestore/src/firestore.dart': Failed assertion: line 74 pos 12: 'isValidCollectionPath(collectionPath)': a collection path must point to a valid collection.)
await checkMissingId(userNameF);
Maybe it depends by wrong document path from Firestore?
I think you are passing the wrong collection name in this line:
final snapShot = await FirebaseFirestore.instance.collection(path).doc(str).get();
because, your path variable is this:
String path = letter + "/" + str; // I guess here is something wrong
Your collection name would be a fixed string as shown below in this screenshot (comments, posts, users etc):

Flutter: SQLite database in external storage. database is null

I am writing an app to store some events and dates at which events happened (For history students as part of a project). I wish to create an SQLite database, stored in some preferred location in my device. Later I want this database to merge with main database using computer. Here is my database helper class;
class DatabaseHelper {
static final _dbName = 'mainDatabase.db';
static final _dbVersion = 1;
static final _mainTable = 'mainTable';
static final _storyTable = 'storyTable';
static final _topicTable = 'topicTable';
static final columnId = '_id';
static final title = 'title';
static final description = 'description';
static final parentId = 'topic_id';
static final priority = 'priority';
static final iconId = 'iconId';
static final futureImage = 'image';
static final year = 'year';
static final month = 'month';
static final day = 'day';
DatabaseHelper._privateConstructor();
static final DatabaseHelper instance = DatabaseHelper._privateConstructor();
static Database _database;
Future<Database> get database async {
if (_database != null) {
return _database;
}
_database = await _checkPermission();
return _database;
}
var status;
_checkPermission() async {
this.status = await Permission.storage.status;
debugPrint(' storage permission status : ${this.status}');
if (await Permission.storage.request().isGranted) {
_initiateDatabase();
} else if (await Permission.storage.request().isUndetermined) {
debugPrint('Undetermined permission');
} else if (await Permission.storage.request().isDenied) {
debugPrint('Permission denied');
_checkPermission();
} else if (await Permission.storage.request().isPermanentlyDenied) {
debugPrint(' it has been permenantly denied');
}
}
_initiateDatabase() async {
debugPrint(' database initialized');
Directory directory = await getExternalStorageDirectory();
String path = join(directory.path, _dbName);
debugPrint('Path for database: ${path}');
return await openDatabase(path, version: _dbVersion, onCreate: _onCreate);
}
Future _onCreate(Database db, int version) async {
await db.execute('''
CREATE TABLE $_mainTable (
$columnId INTEGER NOT NULL,
$year INTEGER,
$month INTEGER,
$day INTEGER,
$title TEXT NOT NULL,
$description TEXT,
$priority INTEGER,
$parentId INTEGER,
PRIMARY KEY($columnId AUTOINCREMENT)
);
'''); /
Future<int> insertEvent(Map<String, dynamic> row) async {
Database db = await instance.database;
debugPrint(' event : ${row}');
assert(db != null); //Issue!!!!
return await db.insert(_mainTable, row);
}
}
My code may seem messy, because I am not from this background. I apologize for that.
When I tried to add an event I get this error;
I/flutter ( 8090): event id is null
I/flutter ( 8090): storage permission status : PermissionStatus.granted
I/flutter ( 8090): database initialized
I/flutter ( 8090): event : {year: null, month: null, day: null, title: adsfasdf, description: null, priority: 0, topic_id: null}
E/flutter ( 8090): [ERROR:flutter/lib/ui/ui_dart_state.cc(166)] Unhandled Exception: 'package:thisDay/config/databaseHelper.dart': Failed assertion: line 189 pos 12: 'db != null': is not true.
/////////////// some code
E/flutter ( 8090):
I/flutter ( 8090): Path for database: /storage/emulated/0/Android/data/com.example.thisDay/files/mainDatabase.db
What should I do? I am stuck at this point. I have my db file at /storage/emulated/0/Android/data/com.example.thisDay/files/mainDatabase.db . But can't add anything in it.
Any help would be greately appreciated.
Nb:- At first I used getApplicationDirectory instead of External directory. There were no issues. I have to switch because of portability issues
Finally, I fixed my issue by myself. _initializeDatabase() has a return type Future<Database>. So making respective changes on code fixed my issue.
Future<Database> _checkPermission() async {
this.status = await Permission.storage.status;
debugPrint(' storage permission status : ${this.status}');
if (await Permission.storage.request().isGranted) {
return await _initiateDatabase();
} else if (await Permission.storage.request().isUndetermined) {
debugPrint('Undetermined permission');
} else if (await Permission.storage.request().isDenied) {
debugPrint('Permission denied');
_checkPermission();
} else if (await Permission.storage.request().isPermanentlyDenied) {
debugPrint(' it has been permenantly denied');
}
}
Future<Database> _initiateDatabase() async {
debugPrint(' database initialized');
Directory directory = await getExternalStorageDirectory();
String path = join(directory.path, _dbName);
debugPrint('Path for database: ${path}');
return await openDatabase(path, version: _dbVersion, onCreate: _onCreate);
}
for creating a database and table you can refer this code
Database database;
opendatabase() async {
// Delete the database
String databasesPath = await getDatabasesPath();
String path = join(databasesPath, "sample.db");
// await deleteDatabase(path);
database = await openDatabase(path, version: 1,
onCreate: (Database db, int version) async {
// When creating the db, create the table
await db.execute(
'CREATE TABLE tbl_test (id INTEGER PRIMARY KEY, sample TEXT, code TEXT, format TEXT)',
);
});
}

Escalate DBProvider Flutter

I am new to working with databases in flutter. I have a data folder with a database.dart in the following form. This is fine for one table and with a few CRUD functions such as newNote or getNotes, but what happens when you have multiple tables and multiple CRUD Functions? Are you supposed to dump them all on this database.dart file making it chaotic? What are better alternatives for organization?
class DBProvider {
DBProvider._();
static final DBProvider db = DBProvider._();
Database _database;
Future<Database> get database async {
if (_database != null) {
return _database;
}
_database = await initDB();
return _database;
}
initDB() async {
Directory documentsDir = await getApplicationDocumentsDirectory();
String path = join(documentsDir.path, 'app.db');
return await openDatabase(path, version: 1, onOpen: (db) async {
}, onCreate: (Database db, int version) async {
// Create the note table
await db.execute('''
CREATE TABLE note(
id INTEGER PRIMARY KEY,
contents TEXT DEFAULT ''
)
''');
});
}
newNote(Note note) async {
final db = await database;
var res = await db.insert('note', note.toJson());
return res;
}
}

Resources