Failing to update value sembast database Flutter - database

I'm trying to use sambas database as I need it for the web version of my app but I'm struggling with it.
I have an OpeningTimes form in my app that I save to db as a Map.
The logic I use in the saving method is: Load the form from db using the userName parameter of the OpeningTimes object I pass in , if none is found then save it, if instead a record is found then update it with the passed in object. I'm using flutter_bloc to manage it all so in OpeningTimesBloc when the form is saved to db, it loads it and yield it so to update UI to last saved sate. The problem is that when loading from db after updating, the loaded value is not the one passed in update method( eg monMorAct: to be updated from true to false):
flutter: updateOpeningTimes() : update opening time received {userName: zazza zenigata, monMorOp: 10:00, monMorCl: 19:00, monMorAct: false, monAftOp: , monAftCl: , monAftAct: false, tueMorOp: , tueMorCl: , tueMorAct: false, tueAftOp: , tueAftCl: , tueAftAct: false, wedMorOp: , wedMorCl: , wedMorAct: false, wedAftOp: , wedAftCl: , wedAftAct: false, thuMorOp: , thuMorCl: , thuMorAct: false, thuAftOp: , thuAftCl: , thuAftAct: false, friMorOp: , friMorCl: , friMorAct: false, friAftOp: , friAftCl: , friAftAct: false, satMorOp: , satMorCl: , satMorAct: false, satAftOp: , satAftCl: , satAftAct: false, sunMorOp: , sunMorCl: , sunMorAct: false, sunAftOp: , sunAftCl: , sunAftAct: false}
but when loading it:
flutter: loadOpeningTimes() snapshot is: Record(openingTimeStorage, 1) {userName: zazza zenigata, monMorOp: 10:00, monMorCl: 19:00, monMorAct: true, monAftOp: , monAftCl: , monAftAct: false, tueMorOp: , tueMorCl: , tueMorAct: false, tueAftOp: , tueAftCl: , tueAftAct: false, wedMorOp: , wedMorCl: , wedMorAct: false, wedAftOp: , wedAftCl: , wedAftAct: false, thuMorOp: , thuMorCl: , thuMorAct: false, thuAftOp: , thuAftCl: , thuAftAct: false, friMorOp: , friMorCl: , friMorAct: false, friAftOp: , friAftCl: , friAftAct: false, satMorOp: , satMorCl: , satMorAct: false, satAftOp: , satAftCl: , satAftAct: false, sunMorOp: , sunMorCl: , sunMorAct: false, sunAftOp: , sunAftCl: , sunAftAct: false}
Can you spot what I am doing wrong?
As always thank you very much for your time and help.
This is the singleton:
import 'dart:async';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
import 'package:sembast/sembast.dart';
import 'package:sembast/sembast_io.dart';
class AppDatabase {
// Singleton instance
static final AppDatabase _singleton = AppDatabase._();
// Singleton accessor
static AppDatabase get instance => _singleton;
// Completer is used for transforming synchronous code into asynchronous code.
Completer<Database> _dbOpenCompleter;
// A private constructor. Allows us to create instances of AppDatabase
// only from within the AppDatabase class itself.
AppDatabase._();
// Sembast database object
Database _database;
// Database object accessor
Future<Database> get database async {
// If completer is null, AppDatabaseClass is newly instantiated, so database is not yet opened
if (_dbOpenCompleter == null) {
_dbOpenCompleter = Completer();
// Calling _openDatabase will also complete the completer with database instance
_openDatabase();
}
// If the database is already opened, awaiting the future will happen instantly.
// Otherwise, awaiting the returned future will take some time - until complete() is called
// on the Completer in _openDatabase() below.
return _dbOpenCompleter.future;
}
Future _openDatabase() async {
// Get a platform-specific directory where persistent app data can be stored
final appDocumentDir = await getApplicationDocumentsDirectory();
// Path with the form: /platform-specific-directory/demo.db
final dbPath = join(appDocumentDir.path, 'demo.db');
final database = await databaseFactoryIo.openDatabase(dbPath);
// Any code awaiting the Completer's future will now start executing
_dbOpenCompleter.complete(database);
}
}
This is the bloc:
class OpeningTimesBloc extends Bloc<OpeningTimesEvent, OpeningTimesState> {
OpeningTimesRepository _openingTimesRepository = OpeningTimesRepository();
#override
OpeningTimesState get initialState => InitialState();
#override
Stream<OpeningTimesState> mapEventToState(OpeningTimesEvent event) async* {
if (event is LoadOpeningTimes) {
print('GetOpeningTimes event received');
yield* _mapLoadOpeningTimesToState(event);
}
if (event is SaveOpeningTimes) {
yield* _mapSaveOpeningTimesToState(event);
}
}
Stream<OpeningTimesState> _mapLoadOpeningTimesToState(
LoadOpeningTimes event) async* {
OpeningTimes openingTimes = await _openingTimesRepository.loadOpeningTimes(
userName: event.user.name);
print(
'_mapLoadOpeningTimesToState() loaded openingTimes is: ${openingTimes.toMap().toString()}');
yield ShopOpeningTimes(openingTimes);
}
Stream<OpeningTimesState> _mapSaveOpeningTimesToState(
SaveOpeningTimes event) async* {
await _openingTimesRepository.saveOpeningTimes(event.openingTimes);
add(LoadOpeningTimes(event.user));
}
}
this is the repository:
import 'package:sembast/sembast.dart';
class OpeningTimesRepository {
// name for the storage
static const String openingTimeStorage = 'openingTimeStorage';
// storage reference
final _openingTimesFolder = intMapStoreFactory.store(openingTimeStorage);
// database instance
Future<Database> get _db async => await AppDatabase.instance.database;
Future saveOpeningTimes(OpeningTimes openingTimes) async {
print(
'saveOpeningTimes(): save opening times received ${openingTimes.toMap().toString()}'); // prints correct
final snapshot = await loadOpeningTimes(userName: openingTimes.userName);
print('saveOpeningTimes() snapshot is $snapshot');
if (snapshot == null) {
print('opening times are to save');
await _openingTimesFolder.add(await _db, openingTimes.toMap());
} else {
print('opening times are to update');
await updateOpeningTimes(openingTimes);
}
}
Future updateOpeningTimes(OpeningTimes openingTimes) async {
print(
'updateOpeningTimes() : update opening time received ${openingTimes.toMap().toString()}'); // correct
final Finder finder = Finder(filter: Filter.byKey(openingTimes.userName));
await _openingTimesFolder.update(await _db, openingTimes.toMap(),
finder: finder);
}
Future<OpeningTimes> loadOpeningTimes({String userName}) async {
print(
'loadOpeningTimes() called userName is $userName'); // prints correct userName
final finder = Finder(sortOrders: [SortOrder('userName')]);
final snapshot =
await _openingTimesFolder.findFirst(await _db, finder: finder);
print('loadOpeningTimes() snapshot is: ${snapshot}'); // correct
return OpeningTimes.fromMap(snapshot.value);
}
}
and this is the model:
import 'package:flutter/material.dart';
class OpeningTimes {
int id;
final String userName;
final String monMorOp;
final String monMorCl;
final bool monMorAct;
final String monAftOp;
final String monAftCl;
final bool monAftAct;
final String tueMorOp;
final String tueMorCl;
final bool tueMorAct;
final String tueAftOp;
final String tueAftCl;
final bool tueAftAct;
final String wedMorOp;
final String wedMorCl;
final bool wedMorAct;
final String wedAftOp;
final String wedAftCl;
final bool wedAftAct;
final String thuMorOp;
final String thuMorCl;
final bool thuMorAct;
final String thuAftOp;
final String thuAftCl;
final bool thuAftAct;
final String friMorOp;
final String friMorCl;
final bool friMorAct;
final String friAftOp;
final String friAftCl;
final bool friAftAct;
final String satMorOp;
final String satMorCl;
final bool satMorAct;
final String satAftOp;
final String satAftCl;
final bool satAftAct;
final String sunMorOp;
final String sunMorCl;
final bool sunMorAct;
final String sunAftOp;
final String sunAftCl;
final bool sunAftAct;
OpeningTimes(
{#required this.userName,
#required this.monMorOp,
#required this.monMorCl,
#required this.monMorAct,
#required this.monAftOp,
#required this.monAftCl,
#required this.monAftAct,
#required this.tueMorOp,
#required this.tueMorCl,
#required this.tueMorAct,
#required this.tueAftOp,
#required this.tueAftCl,
#required this.tueAftAct,
#required this.wedMorOp,
#required this.wedMorCl,
#required this.wedMorAct,
#required this.wedAftOp,
#required this.wedAftCl,
#required this.wedAftAct,
#required this.thuMorOp,
#required this.thuMorCl,
#required this.thuMorAct,
#required this.thuAftOp,
#required this.thuAftCl,
#required this.thuAftAct,
#required this.friMorOp,
#required this.friMorCl,
#required this.friMorAct,
#required this.friAftOp,
#required this.friAftCl,
#required this.friAftAct,
#required this.satMorOp,
#required this.satMorCl,
#required this.satMorAct,
#required this.satAftOp,
#required this.satAftCl,
#required this.satAftAct,
#required this.sunMorOp,
#required this.sunMorCl,
#required this.sunMorAct,
#required this.sunAftOp,
#required this.sunAftCl,
#required this.sunAftAct});
#override
List<Object> get props => [
userName,
monMorOp,
monMorCl,
monMorAct,
monAftOp,
monAftCl,
monAftAct,
tueMorOp,
tueMorCl,
tueMorAct,
tueAftOp,
tueAftCl,
tueAftAct,
wedMorOp,
wedMorCl,
wedMorAct,
wedAftOp,
wedAftCl,
wedAftAct,
thuMorOp,
thuMorCl,
thuMorAct,
thuAftOp,
thuAftCl,
thuAftAct,
friMorOp,
friMorCl,
friMorAct,
friAftOp,
friAftCl,
friAftAct,
satMorOp,
satMorCl,
satMorAct,
satAftOp,
satAftCl,
satAftAct,
sunMorOp,
sunMorCl,
sunMorAct,
sunAftOp,
sunAftCl,
sunAftAct
];
factory OpeningTimes.fromMap(Map<String, dynamic> map) => new OpeningTimes(
userName: map['userName'],
monMorOp: map['monMorOp'],
monMorCl: map['monMorCl'],
monMorAct: map['monMorAct'],
monAftOp: map['monAftOp'],
monAftCl: map['monAftCl'],
monAftAct: map['monAftAct'],
tueMorOp: map['tueMorOp'],
tueMorCl: map['tueMorCl'],
tueMorAct: map['tueMorAct'],
tueAftOp: map['tueAftOp'],
tueAftCl: map['tueAftCl'],
tueAftAct: map['tueAftAct'],
wedMorOp: map['wedMorOp'],
wedMorCl: map['wedMorCl'],
wedMorAct: map['wedMorAct'],
wedAftOp: map['wedAftOp'],
wedAftCl: map['wedAftCl'],
wedAftAct: map['wedAftAct'],
thuMorOp: map['thuMorOp'],
thuMorCl: map['thuMorCl'],
thuMorAct: map['thuMorAct'],
thuAftOp: map['thuAftOp'],
thuAftCl: map['thuAftCl'],
thuAftAct: map['thuAftAct'],
friMorOp: map['friMorOp'],
friMorCl: map['friMorCl'],
friMorAct: map['friMorAct'],
friAftOp: map['friAftOp'],
friAftCl: map['friAftCl'],
friAftAct: map['friAftAct'],
satMorOp: map['satMorOp'],
satMorCl: map['satMorCl'],
satMorAct: map['satMorAct'],
satAftOp: map['satAftOp'],
satAftCl: map['satAftCl'],
satAftAct: map['satAftAct'],
sunMorOp: map['sunMorOp'],
sunMorCl: map['sunMorCl'],
sunMorAct: map['sunMorAct'],
sunAftOp: map['sunAftOp'],
sunAftCl: map['sunAftCl'],
sunAftAct: map['sunAftAct'],
);
Map<String, dynamic> toMap() => {
'userName': userName,
'monMorOp': monMorOp,
'monMorCl': monMorCl,
'monMorAct': monMorAct,
'monAftOp': monAftOp,
'monAftCl': monAftCl,
'monAftAct': monAftAct,
'tueMorOp': tueMorOp,
'tueMorCl': tueMorCl,
'tueMorAct': tueMorAct,
'tueAftOp': tueAftOp,
'tueAftCl': tueAftCl,
'tueAftAct': tueAftAct,
'wedMorOp': wedMorOp,
'wedMorCl': wedMorCl,
'wedMorAct': tueMorAct,
'wedAftOp': wedAftOp,
'wedAftCl': wedAftCl,
'wedAftAct': wedAftAct,
'thuMorOp': thuMorOp,
'thuMorCl': thuMorCl,
'thuMorAct': thuMorAct,
'thuAftOp': thuAftOp,
'thuAftCl': thuAftCl,
'thuAftAct': thuAftAct,
'friMorOp': friMorOp,
'friMorCl': friMorCl,
'friMorAct': friMorAct,
'friAftOp': friAftOp,
'friAftCl': friAftCl,
'friAftAct': friAftAct,
'satMorOp': satMorOp,
'satMorCl': satMorCl,
'satMorAct': satMorAct,
'satAftOp': satAftOp,
'satAftCl': satAftCl,
'satAftAct': satAftAct,
'sunMorOp': sunMorOp,
'sunMorCl': sunMorCl,
'sunMorAct': sunMorAct,
'sunAftOp': sunAftOp,
'sunAftCl': sunAftCl,
'sunAftAct': sunAftAct
};
}
Updated repository methods to use autogenerated key:
static const String openingTimeStorage = 'openingTimeStorage';
// storage reference
final _openingTimesFolder = intMapStoreFactory.store(openingTimeStorage);
// database instance
Future<Database> get _db async => await AppDatabase.instance.database;
Future saveOpeningTimes(OpeningTimes openingTimes) async {
print(
'saveOpeningTimes(): save opening times received ${openingTimes.toMap().toString()}'); // prints correct
final snapshot = await loadOpeningTimes();
print('saveOpeningTimes() snapshot is $snapshot');
if (snapshot == null) {
print('opening times are to save');
await _openingTimesFolder.add(await _db, openingTimes.toMap());
return;
} else {
print('opening times are to update');
await updateOpeningTimes(openingTimes);
}
}
Future updateOpeningTimes(OpeningTimes openingTimes) async {
print(
'updateOpeningTimes() : update opening time received ${openingTimes.toMap().toString()}'); // correct
final Finder finder = Finder(
filter: Filter.byKey(
_openingTimesFolder.record(openingTimes.id).get(await _db)));
await _openingTimesFolder.update(await _db, openingTimes.toMap(),
finder: finder);
}
Future<OpeningTimes> loadOpeningTimes() async {
final finder = Finder(sortOrders: [SortOrder('userName')]);
final snapshot =
await _openingTimesFolder.findFirst(await _db, finder: finder);
if (snapshot != null) {
print('loadOpeningTimes() snapshot is: $snapshot'); // correct
OpeningTimes openingTimes = OpeningTimes.fromMap(snapshot.value);
openingTimes.id = snapshot.key;
return openingTimes;
} else {
return null;
}
}
on updating I get flutter: Erros is Invalid argument(s): Record key cannot be null.

In updateOpeningTimes your filter looks incorrect.
final Finder finder = Finder(filter: Filter.byKey(openingTimes.userName));
Here you are looking for objects with the userName as the key but the key is an int (autogenerated) here so it cannot match. You should have something like:
final Finder finder = Finder(filter: Filter.equals('userName', userName));
In loadOpeningTimes, you just take the first record sorted by userName. If you look for an explicit userName, you should use the same finder than for update.
As a side note, it is a lot faster to load records by key: store.record(key).get(db)

Related

SqfLite No such table (code 1 SQLITE_ERROR)

Maybe this question was already answered but I try to add sqfLite to my project but I get this error:
In my debug console:
no such table: infotable in "SELECT * FROM infotable"
This is my code:
final String infoTable = 'infotable';
final String columnID = '_id';
final String columnName = 'name';
final String columnCivilStatus = 'civil';
final String columnWorking = 'working';
final String columnJobTitle = 'job';
final String columnWorkingDomain = 'workingdomain';
final String columnMonthlyIncome = 'monthlyincome';
class SqfLite {
int id;
int sliderValue;
int monthChoiceChip;
String reasonChoiceChip;
String name;
String civilStatus;
int working;
String jobTitle;
int workingDomain;
String monthlyIncome;
String currency;
Position latitude;
Position longitude;
Map<String, dynamic> toMap() {
var map = <String, dynamic>{
columnCivilStatus: civilStatus,
columnJobTitle: jobTitle,
columnMonthlyIncome: monthlyIncome,
columnWorkingDomain: workingDomain,
columnWorking: working,
columnName: name,
};
if (id != null) {
map[columnID] = id;
}
return map;
}
SqfLite();
SqfLite.fromMap(Map<String, dynamic> map) {
id = map[columnID];
name = map[columnName];
civilStatus = map[columnCivilStatus];
working = map[columnWorking];
jobTitle = map[columnJobTitle];
workingDomain = map[columnWorkingDomain];
monthlyIncome = map[columnMonthlyIncome];
}
}
class SqfLiteProvider {
Database db;
Future open(String path) async {
db = await openDatabase(path, version: 1,
onCreate: (Database db, int version) async {
await db.execute('''
create table $infoTable (
$columnID integer primary key autoincrement,
$columnName text not null,
$columnCivilStatus text not null,
$columnWorking integer not null,
$columnJobTitle text not null,
$columnWorkingDomain integer not null,
$columnMonthlyIncome text not null)
''');
});
}
Future<SqfLite> getSqfLite(int id) async {
List<Map> maps = await db.query(infoTable,
columns: [
columnID,
columnName,
columnCivilStatus,
columnWorking,
columnJobTitle,
columnWorkingDomain,
columnMonthlyIncome,
],
where: '$columnID = ?',
whereArgs: [id]);
if (maps.length > 0) {
return SqfLite.fromMap(maps.first);
}
return null;
}
Future close() async => db.close();
}
I get the error here:
Future<void> openDataBase() async {
db = await openDatabase('infotable.db');
records = await db.query('infotable');
mapRead = records.first;
....
}
I've followed the example from the official page of the plugin
I can't use the latest version because of some pubspec.yaml conflicts.
I've tried to flutter clean and reinstalling the app but it didn't work
You have defined the open method inside SqfLiteProvider, but you are not using it. Instead, you are calling the same openDatabase('infotable.db'); method from sqflite package where the onCreate callback is not defined - you get an error.
So, instead of calling db = await openDatabase('infotable.db'); directly, use the SqfLiteProvider.open() you have defined.

Caused By : SQL(query) error or missing database. - In Flutter/Dart

I was trying to build an app that takes in the Title, Date, Link, Priority level and then displays them using Flutter and SQLite.
I originally built it without 'link' and it was working perfectly, but when I added the filed 'link' it gives me this error:
E/flutter ( 8491): [ERROR:flutter/lib/ui/ui_dart_state.cc(186)] Unhandled Exception: DatabaseException(table task_table has no column named link (code 1): , while compiling: INSERT INTO task_table (title, date, link, priority, status) VALUES (?, ?, ?, ?, ?)
E/flutter ( 8491): #################################################################
E/flutter ( 8491): Error Code : 1 (SQLITE_ERROR)
E/flutter ( 8491): Caused By : SQL(query) error or missing database.
E/flutter ( 8491): (table task_table has no column named link (code 1): , while compiling: INSERT INTO task_table (title, date, link, priority, status) VALUES (?, ?, ?, ?, ?))
E/flutter ( 8491): #################################################################) sql 'INSERT INTO task_table (title, date, link, priority, status) VALUES (?, ?, ?, ?, ?)' args [math, 2021-04-28T00:00:00.000, google, Medium, 0]}
The code associated with this is distributed in two files: a database helper file that basically stores all the functions for database management
import 'dart:io';
import 'package:path_provider/path_provider.dart';
import 'package:sqflite/sqflite.dart';
import 'package:todo/models/task_model.dart';
class DatabaseHelper {
static final DatabaseHelper instance = DatabaseHelper._instance();
static Database _db;
DatabaseHelper._instance();
String taskTable = 'task_table';
String colId = 'id';
String colTitle = 'title';
String colDate = 'date';
String colLink = 'link';
String colPriority = 'priority';
String colStatus = 'status';
// task tables
// Id | Title | Date | Link | Priority | Status
// 0 '' '' '' ''
// 2 '' '' '' ''
// 3 '' '' '' ''
Future<Database> get db async {
if (_db == null) {
_db = await _initDb();
}
return _db;
}
Future<Database> _initDb() async {
Directory dir = await getApplicationDocumentsDirectory();
String path = dir.path + 'todo_list.db';
final todoListDb =
await openDatabase(path, version: 1, onCreate: _createDb);
return todoListDb;
}
void _createDb(Database db, int version) async {
await db.execute(
'CREATE TABLE $taskTable($colId INTEGER PRIMARY KEY AUTOINCREMENT, $colTitle TEXT, $colDate TEXT, $colLink TEXT, $colPriority TEXT, $colStatus INTEGER)');
}
Future<List<Map<String, dynamic>>> getTaskMapList() async {
Database db = await this.db;
final List<Map<String, dynamic>> result = await db.query(taskTable);
return result;
}
Future<List<Task>> getTaskList() async {
final List<Map<String, dynamic>> taskMapList = await getTaskMapList();
final List<Task> taskList = [];
taskMapList.forEach((taskMap) {
taskList.add(Task.fromMap(taskMap));
});
taskList.sort((taskA, taskB) => taskA.date.compareTo(taskB.date));
return taskList;
}
Future<int> insertTask(Task task) async {
Database db = await this.db;
final int result = await db.insert(taskTable, task.toMap());
return result;
}
Future<int> updateTask(Task task) async {
Database db = await this.db;
final int result = await db.update(taskTable, task.toMap(),
where: '$colId = ?', whereArgs: [task.id]);
return result;
}
Future<int> deleteTask(int id) async {
Database db = await this.db;
final int result =
await db.delete(taskTable, where: '$colId = ?', whereArgs: [id]);
return result;
}
}
And the second file is a database model file, which contains the database creation etc. :
class Task {
int id;
String title;
DateTime date;
String link;
String priority;
int status; // 0 - complete, 1- complete
Task({
this.title,
this.date,
this.link,
this.priority,
this.status,
});
Task.withId({
this.id,
this.title,
this.date,
this.link,
this.priority,
this.status,
});
Map<String, dynamic> toMap() {
final map = Map<String, dynamic>();
if (id != null) {
map['id'] = id;
}
map['title'] = title;
map['date'] = date.toIso8601String();
map['link'] = link;
map['priority'] = priority;
map['status'] = status;
return map;
}
factory Task.fromMap(Map<String, dynamic> map) {
return Task.withId(
id: map['id'],
title: map['title'],
date: DateTime.parse(map['date']),
link: map['link'],
priority: map['priority'],
status: map['status']);
}
}
This is my first time working with databases in flutter so any feedback would be greatly appreciated. Thank you
The error says that you don't have the column available in the table. The problem is that the db is not created every time you start the app. There are two solutions to your problem here:
For debug purposes, just delete the app and re-run the code, this will regenerate the database with the correct columns. WARNING: this is just for debug only, not for production.
For production, when you add changes on database, you have to increase the database version, in your case from 1 to let's say 2. Next, the openDatabase method has a parameter onUpgrade that will be called when the database version is upgraded, in your case from 1 to 2, here you'll have to run additional sql commands to update your table. Something like this:
await openDatabase(path, version: 1, onCreate: _createDb, onUpgrade: (db, old, newVersion) async {
if(old < 2) {
await db.execute("ALTER TABLE task_table ADD link TEXT");
}
});
Also do not forget to update your create table statement to add your new column there.
One scenario would be that you don't want to save the link in the db, in this case you'll have to remove it from json serialization (your toMap method) .

Access SQLite DB in Flutter

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
};
}
}

Parsing object in dart (Unsupported operation: Cannot add to a fixed-length list)

I have a user object which saved to the cloud firestore database when the user Sign In / Sign Up.
So the user object is retrieved from the database when he signs in, and everything works until i try to do the 'add' operation on the list 'usersProject':
// Add the new project ID to the user's project list
user.userProjectsIDs.add(projectID);
So i get the exception Unhandled Exception: Unsupported operation: Cannot add to a fixed-length list
i believe the problem is when converting the user from json to object, because when the user is signing up the object is converted to json and stored in the database and the user will be automatically signed in using the object before converting.
void createUser(String email, String password, String username, String name, String birthDate) async {
try {
// Check first if username is taken
bool usernameIsTaken = await UserProfileCollection()
.checkIfUsernameIsTaken(username.toLowerCase().trim());
if (usernameIsTaken) throw FormatException("Username is taken");
// Create the user in the Authentication first
final firebaseUser = await _auth.createUserWithEmailAndPassword(
email: email.trim(), password: password.trim());
// Encrypting the password
String hashedPassword = Password.hash(password.trim(), new PBKDF2());
// Create new list of project for the user
List<String> userProjects = new List<String>();
// Create new list of friends for the user
List<String> friends = new List<String>();
// Creating user object and assigning the parameters
User _user = new User(
userID: firebaseUser.uid,
userName: username.toLowerCase().trim(),
email: email.trim(),
password: hashedPassword,
name: name,
birthDate: birthDate.trim(),
userAvatar: '',
userProjectsIDs: userProjects,
friendsIDs: friends,
);
// Create a new user in the fire store database
await UserProfileCollection().createNewUser(_user);
// Assigning the user controller to the 'user' object
Get.find<UserController>().user = _user;
Get.back();
} catch (e) {
print(e.toString());
}}
When the user is signed off then he signs in and try to make operation on the user object, here comes the problem some of the properties (the List type) can't be used.
This code creates project and add projectID to the user's list
Future<void> createNewProject(String projectName, User user) async {
String projectID = Uuid().v1(); // Project ID, UuiD is package that generates random ID
// Add the creator of the project to the members list and assign him as admin
var member = Member(
memberUID: user.userID,
isAdmin: true,
);
List<Member> membersList = new List();
membersList.add(member);
// Save his ID in the membersUIDs list
List <String> membersIDs = new List();
membersIDs.add(user.userID);
// Create chat for the new project
var chat = Chat(chatID: projectID);
// Create the project object
var newProject = Project(
projectID: projectID,
projectName: projectName,
image: '',
joiningLink: '$projectID',
isJoiningLinkEnabled: true,
pinnedMessage: '',
chat: chat,
members: membersList,
membersIDs: membersIDs,
);
// Add the new project ID to the user's project list
user.userProjectsIDs.add(projectID);
try {
// Convert the project object to be a JSON
var jsonUser = user.toJson();
// Send the user JSON data to the fire base
await Firestore.instance
.collection('userProfile')
.document(user.userID)
.setData(jsonUser);
// Convert the project object to be a JSON
var jsonProject = newProject.toJson();
// Send the project JSON data to the fire base
return await Firestore.instance
.collection('projects')
.document(projectID)
.setData(jsonProject);
} catch (e) {
print(e);
}}
Here where the exception happens only when the user signs off then signs in, but when he signed up for the first time there will be no exception.
// Add the new project ID to the user's project list
user.userProjectsIDs.add(projectID);
The sign in function
void signIn(String email, String password) async {
try {
// Signing in
FirebaseUser firebaseUser = await _auth.signInWithEmailAndPassword(email: email.trim(), password: password.trim());
// Getting user document form firebase
DocumentSnapshot userDoc = await UserProfileCollection().getUser(firebaseUser.uid);
// Converting the json data to user object and assign the user object to the controller
Get.find<UserController>().user = User.fromJson(userDoc.data);
print(Get.find<UserController>().user.userName);
} catch (e) {
print(e.toString());
}}
I think the problem caused by User.fromJson
why it makes the array from the firestore un-modifiable ?
The user class
class User {
String userID;
String userName;
String email;
String password;
String name;
String birthDate;
String userAvatar;
List<String> userProjectsIDs;
List<String> friendsIDs;
User(
{this.userID,
this.userName,
this.email,
this.password,
this.name,
this.birthDate,
this.userAvatar,
this.userProjectsIDs,
this.friendsIDs});
User.fromJson(Map<String, dynamic> json) {
userID = json['userID'];
userName = json['userName'];
email = json['email'];
password = json['password'];
name = json['name'];
birthDate = json['birthDate'];
userAvatar = json['UserAvatar'];
userProjectsIDs = json['userProjectsIDs'].cast<String>();
friendsIDs = json['friendsIDs'].cast<String>();
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['userID'] = this.userID;
data['userName'] = this.userName;
data['email'] = this.email;
data['password'] = this.password;
data['name'] = this.name;
data['birthDate'] = this.birthDate;
data['UserAvatar'] = this.userAvatar;
data['userProjectsIDs'] = this.userProjectsIDs;
data['friendsIDs'] = this.friendsIDs;
return data;
}
}
Just add the growable argument..
If [growable] is false, which is the default, the list is a fixed-length list of length zero. If [growable] is true, the list is growable and equivalent to [].
final growableList = List.empty(growable: true);
this works for me:
list = list.toList();
list.add(value);
Your JSON decoding is likely returning fixed-length lists, which you're then using to initialize userProjectsIDs in the User class. This prevent you from adding additional elements.
Change the following from the fromJson constructor:
userProjectsIDs = json['userProjectsIDs'].cast<String>();
to
userProjectsIDs = List.of(json['userProjectsIDs'].cast<String>());

Unsupported Media Type Spring ReST resource

I have a simple method defined in my Rest Resource as below:
#RequestMapping(value = "/{studyId}/cases/{caseId}/exportlocation/{exportLocation}", method = RequestMethod.PUT)
#Timed
public void exportCase(#PathVariable Long studyId, #PathVariable Long caseId, #PathVariable String exportLocation,
#RequestBody Case acase) throws Exception {
log.debug("REST request to export Case {} for Study : {}", acase, studyId);
String exportFileName = exportService.exportCase(acase, "test");
// if (exportFileName == null) {
// response.sendError(HttpServletResponse.SC_NOT_FOUND, "Can't Export");
// }
// return exportFileName;
}
When I make a call on the page, I can see the URL as being /app/rest/studies/1/cases/1/exportlocation/test
I have the Request Mapping defined as
#RequestMapping(value = StudyResource.REQUEST_MAPPING, produces = MediaType.APPLICATION_JSON_VALUE)
#Secured(AuthoritiesConstants.USER)
public class StudyResource {
private final Logger log = LoggerFactory.getLogger(StudyResource.class);
public static final String REQUEST_MAPPING = "/app/rest/studies";
But keep getting a 415 Unsupported Media type. Can someone please look at the lines of code and tell me what is wrong. I highly appreciate your time and help.
My JS layer from where the calls are made on the page are as shown"
$scope.exportCase = function(studyId, caseId, exportLocation){
StudyService.updatecase.get({studyId:studyId,caseId:caseId}).$promise.then(function(acase){
$scope.acase = acase;
console.log(acase);
});
StudyService.exportcase.exportc({studyId: studyId,caseId:caseId,exportLocation:exportLocation},$scope.acase,
function () {
AND JS Service part below
exportcase : $resource('app/rest/studies/:studyId/cases/:caseId/exportlocation/:exportLocation', {}, {
'exportc' : {
method : 'PUT',
params : {
studyId : '#studyId',
caseId : '#caseId',
exportLocation : '#exportLocation'
}
},
})

Resources