How to connect a Flutter Application with local Database? - database

I'm doing a simple flutter application with a small Database but when I try to insert data to my table this error appears to me:
Exception has occurred.
NoSuchMethodError (NoSuchMethodError: The method 'insert' was called on null. Receiver: null Tried calling: insert("userTable", _LinkedHashMap len:3))
this is my code:
// database_helper.dart
import 'dart:io';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
import 'package:sqflite/sqflite.dart';
import 'package:sqflite/sqlite_api.dart';
import 'package:test_sql/models/user.dart';
class DataBaseHelper{
final String tableUser = "userTable" ;
final String columnId = "id" ;
final String columnName = "username" ;
final String columnPassword = "password" ;
static final DataBaseHelper _instance = new DataBaseHelper.internal();
DataBaseHelper.internal();
factory DataBaseHelper() => _instance ;
static Database _db ;
Future<Database> get dbase async{
if(_db !=null){
return _db ;
}
else
{
_db = await initDb() ;
}
}
initDb() async {
Directory documentDirectory = await getApplicationDocumentsDirectory() ;
String path = join(documentDirectory.path,"mydb.db") ;
var ourDb = await openDatabase(path, version: 1,onCreate: _onCreate);
return ourDb ;
}
void _onCreate(Database db , int version) async {
await db.execute("CREATE TABLE $tableUser($columnId INTEGER PRIMARY KEY , $columnName TEXT,$columnPassword TEXT)");
}
Future<int> saveUser (User user) async{
var dbClient = await dbase ;
await initDb() ;
int result = await dbClient.insert(tableUser, user.toMap(),); // insert(tableUser, user.toMap())
return result ;
}
Future<List> getAllUsers() async {
var dbClient = await dbase ;
var result = await dbClient.rawQuery("SELECT * FROM $tableUser") ;
return result.toList() ;
}
Future<User> getUser(int userId) async {
var dbClient = await dbase ;
var result = await dbClient.rawQuery("SELECT * FROM $tableUser Where $columnId = $userId ") ;
if(result.length==0) return null ;
return new User.fromMap(result.first) ;
}
Future<int> deleteUser(int userId) async{
var dbClient = await dbase ;
return await dbClient.delete(tableUser,where: "$columnId=?",whereArgs: [userId]) ;
}
Future<int> updateUser(User user) async {
var dbClient = await dbase ;
return dbClient.update(tableUser, user.toMap(),where: "$columnId=?",whereArgs: [user.id]) ;
}
Future close() async {
var dbClient = await dbase ;
return dbClient.close() ;
}
}
//user.dart
class User{
String _username ;
String _password ;
int _id ;
User(this._id,this._username,this._password) ;
User.map(dynamic obj ){
this._username = obj['username'] ;
this._password = obj['password'] ;
this._id = obj['id'] ;
}
String get username => _username ;
String get password => _password ;
int get id => _id ;
Map<String,dynamic> toMap() {
var map = new Map<String,dynamic>() ;
map["username"] = _username ;
map["password"] = _password ;
if(_id != null){
map["id"] = _id ;
}
return map ;
}
User.fromMap(Map<String,dynamic>map){
this._username=map["username"] ;
this._password=map["password"] ;
this._id=map["id"] ;
}
}
// main.dart
import 'package:flutter/material.dart';
import 'package:test_sql/database_helper.dart';
import 'package:test_sql/models/user.dart';
void main() async {
var db = new DataBaseHelper() ;
int savedUserId = await db.saveUser(new User(1,"Samir Ben Halima", "Sonia el Ayeb")) ;
print(savedUserId) ;
runApp( MaterialApp(
title: "3atba",
home: MyApp(),
)
);
}
class MyApp extends StatelessWidget {
const MyApp({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
child: Scaffold(
appBar: AppBar(
title: Text("3atba"),
centerTitle: true,
backgroundColor: Colors.green,
),
),
);
}
}
pubspec.yaml
name: test_sql
description: A new Flutter project.
# The following defines the vers[![this is the place of the ERROR][1]][1]ion and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43
# followed by an optional build number separated by a +.
# Both the version and the builder number may be overridden in flutter
# build by specifying --build-name and --build-number, respectively.
# In Android, build-name is used as versionName while build-number used as versionCode.
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 1.0.0+1
environment:
sdk: ">=2.1.0 <3.0.0"
dependencies:
flutter:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^0.1.2
sqflite: any
path_provider: any
dev_dependencies:
flutter_test:
sdk: flutter
# For information on the generic Dart part of this file, see the
# following page: https://www.dartlang.org/tools/pub/pubspec
# The following section is specific to Flutter.
flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true
# To add assets to your application, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware.
# For details regarding adding assets from package dependencies, see
# https://flutter.dev/assets-and-images/#from-packages
# To add custom fonts to your application, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts from package dependencies,
# see https://flutter.dev/custom-fonts/#from-packages

i think the bug was here (this is the fixed code):
if(_db !=null){
return _db ;
}else
{
_db = await initDb() ;
return _db;
}
you just need to return the variable _db after you initialise it
hope it helped

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.

Unhandled Exception: LateInitializationError: Field '_database#91313886' has not been initialized

I'm a beginner in flutter
I want to link the db file that I have.
There is an error related to initialization, but I don't know what the problem is
2022-02-16 17:47:47.530 20199-20218/com.example.flower_sunset_2 E/flutter: [ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: LateInitializationError: Field '_database#91313886' has not been initialized.
#0 DatabaseProvider._database (package:flower_sunset_2/database.dart)
#1 DatabaseProvider.database (package:flower_sunset_2/database.dart:19:9)
class DatabaseProvider {
static final DatabaseProvider provider = DatabaseProvider();
late Database _database;
Future<Database> get database async {
if (_database != null) return _database;
_database = await createDatabase();
return _database;
}
createDatabase() async {
// Directory docsDir = await getApplicationDocumentsDirectory();
// String path = join(docsDir.path, "repo_db_senior.db");
var databasePath = await getDatabasesPath();
var path = join(databasePath, "repo_db_senior.db");
// Check if the database exists
var exists = await databaseExists(path);
print("database start");
if(!exists) {
// Should happen only the first time you launch your application
print("Creating new copy from asset");
// Make sure the parent directory exists
try {
await Directory(dirname(path)).create(recursive: true);
} catch (_) {}
// Copy from asset
ByteData data = await rootBundle.load(join('repo', 'db', 'senior.db'));
List<int> bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes);
// Write and flush the bytes written
await File(path).writeAsBytes(bytes, flush: true);
}
else {
print("Opening existing database");
}
var database = await openDatabase(path, readOnly: true);
// var database = await openDatabase(path, version: 1, onCreate: initDB, onUpgrade: onUpgrade);
return database;
}
}
pubspec.yaml
assets:
- repo/db/senior.db
your database file names are not matching. In pubspec.yaml file you have it as 'senior.db' while in your code you are looking for 'repo_db_senior.db'.

NOT NULL constraint failed: accounts_personalcolor.user_id

I am new to Django and have trouble making django-rest-framework API for post, inheriting APIView. I'm using a serializer, that inherits djangos ModelSerializer. I face NOT NULL constraint failed: accounts_personalcolor.user_id error whenever I try saving the serializer or model object.
color.js posts image using Django rest framework as follows.
function PersonalColorScreen({navigation,route}) {
const {image} = route.params;
console.log('uri is', image.uri);
const [userToken, setUserToken] = React.useState(route.params?.userToken);
const requestHeaders = {
headers: {
"Content-Type": "multipart/form-data"
}
}
// helper function: generate a new file from base64 String
//convert base64 image data to file object to pass it onto imagefield of serializer.
//otherwise, serializer outputs 500 Internal server error code
const dataURLtoFile = (dataurl, filename) => {
const arr = dataurl.split(',')
const mime = arr[0].match(/:(.*?);/)[1]
const bstr = atob(arr[1])
let n = bstr.length
const u8arr = new Uint8Array(n)
while (n) {
u8arr[n - 1] = bstr.charCodeAt(n - 1)
n -= 1 // to make eslint happy
}
return new File([u8arr], filename, { type: mime })
}
//random number between 0-9
function getRandomInt(max) {
return Math.floor(Math.random() * max);
}
// generate file from base64 string
const file = dataURLtoFile(image.uri, `${getRandomInt(10)}.png`)
const formData= new FormData();
formData.append('img',file,file.name);
console.log(file.name);
//axios post request to send data
// axios.post('http://localhost:8000/accounts/personalcolor/', formData,requestHeaders)
//multipartparser
axios.post('http://localhost:8000/accounts/personalcolor/', formData, requestHeaders)
.then(res => {
console.log(res);
if (res.data === 'upload another image') {
setimageError('upload another image');
} else {
// signUp(userToken);
let color;
switch (res.data){
case ('spring'):
color = 'spring';
break;
case ('summer'):
color = 'summer';
break;
case ('fall'):
color = 'fall';
break;
case ('winter'):
color = 'winter';
break;
}
}
})
.catch(err => {
console.error(err.response.data)
})
view.py handles the image posted. I tried #1 but it did not work. So I tried #2, or #3 instead and they return the same error saying NOT NULL constraint failed: accounts_personalcolor.user_id. I thought saving the serializer or model object creates id(Autofield)automatically and I don't understand why I face this error.
views.py
#api_view(['POST'])
def personalcolor(request):
# 1
image=request.FILES['img']
personal_color=Personalcolor()
personal_color.img=image
personal_color.save()
# 2
image=request.FILES['img']
personal_color=Personalcolor.objects.create(img=image)
personal_color.save()
# 3
serializer = ColorSerializer(data=request.data)
# validation of input data
if serializer.is_valid():
serializer.save()
else:
return Response(serializer.errors, status = status.HTTP_400_BAD_REQUEST)
model.py
class Personalcolor(models.Model):
objects = models.Manager()
img = models.ImageField('personal_img',upload_to="personalcolor/", blank=True)
serializer.py
class ColorSerializer(serializers.ModelSerializer):
class Meta:
model = Personalcolor
fields = ['img']
As mentioned above, executing the code returns django.db.utils.IntegrityError: NOT NULL constraint failed: accounts_personalcolor.user_id. Any help would be greatly appreciated.
Set null to true in your img field like:
img = models.ImageField('personal_img',upload_to="personalcolor/", blank=True, null=True)
Then in your migrations folder within the app where the Personalcolor model is located, delete all of the files that look like 000*_initial.py
Then run makemigrations and migrate

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) .

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