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

import 'dart:io';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path_provider/path_provider.dart';
class DatabaseHelper {
static final _databaseName = "MyDatabase.db";
static final _databaseVersion = 1;
static final table = 'my_table';
static final columnId = '_id';
static final columnName = 'name';
static final columnAge = 'age';
// make this a singleton class
DatabaseHelper._privateConstructor();
static final DatabaseHelper instance = DatabaseHelper._privateConstructor();
// only have a single app-wide reference to the database
static Database _database;
Future<Database> get database async {
if (_database != null) return _database;
// lazily instantiate the db the first time it is accessed
_database = await _initDatabase();
return _database;
}
// this opens the database (and creates it if it doesn't exist)
_initDatabase() async {
Directory documentsDirectory = await getApplicationDocumentsDirectory();
String path = join(documentsDirectory.path, _databaseName);
return await openDatabase(path,
version: _databaseVersion,
onCreate: _onCreate);
}
// SQL code to create the database table
Future _onCreate(Database db, int version) async {
await db.execute('''
CREATE TABLE $table (
$columnId INTEGER PRIMARY KEY,
$columnName TEXT NOT NULL,
$columnAge INTEGER NOT NULL
)
''');
}
// Helper methods
// Inserts a row in the database where each key in the Map is a column name
// and the value is the column value. The return value is the id of the
// inserted row.
Future<int> insert(Map<String, dynamic> row) async {
Database db = await instance.database;
return await db.insert(table, row);
}
// All of the rows are returned as a list of maps, where each map is
// a key-value list of columns.
Future<List<Map<String, dynamic>>> queryAllRows() async {
Database db = await instance.database;
return await db.query(table);
}
// All of the methods (insert, query, update, delete) can also be done using
// raw SQL commands. This method uses a raw query to give the row count.
Future<double> queryRowCount() async {
Database db = await instance.database;
List<Map<String, dynamic>> x = await db.rawQuery('SELECT COUNT(*) FROM $table');
int rowCount = Sqflite.firstIntValue(x);
return rowCount.toDouble();
}
// We are assuming here that the id column in the map is set. The other
// column values will be used to update the row.
Future<int> update(Map<String, dynamic> row) async {
Database db = await instance.database;
int id = row[columnId];
return await db.update(table, row, where: '$columnId = ?', whereArgs: [id]);
}
// Deletes the row specified by the id. The number of affected rows is
// returned. This should be 1 as long as the row exists.
Future<int> delete(int id) async {
Database db = await instance.database;
return await db.delete(table, where: '$columnId = ?', whereArgs: [id]);
}
Future<List<Map<String, dynamic>>> queryOmnivore() async {
Database db = await instance.database;
return await db.query(table, where: '$columnName = ?', whereArgs: ['omnivore']);
}
Future<List<Map<String, dynamic>>> queryPescatarian() async {
Database db = await instance.database;
return await db.query(table, where: '$columnName = ?', whereArgs: ['pescatarian']);
}
Future<List<Map<String, dynamic>>> queryVegetarian() async {
Database db = await instance.database;
return await db.query(table, where: '$columnName = ?', whereArgs: ['vegetarian']);
}
Future<int> queryVegetarianCount() async {
var vegList = await queryVegetarian();
int count = vegList.length;
return count;
}
Future<double> queryOmnivoreCount() async {
var omniList = await queryOmnivore();
int omniCount = omniList.length;
return omniCount.toDouble();
}
Future<double> calcOmnivorePercentage() async {
var x = await queryOmnivoreCount();
var y = await queryRowCount();
double omniPercentage = (x / y) * 100;
return omniPercentage;
}
}
Hey Folks!
I was hoping someone may be able to help me please?!
I'm trying to figure out how to take data out of a a sqflite table I've created, perform a calculation that expresses it as a percentage of the other values, and display it inside a text widget in the app.
I've actually managed to get the result to print in the console using this code:
void omnivorePercentageQ() async {
final omni = await dbHelper.calcOmnivorePercentage();
print('omnivore percentage: ${omni.toStringAsFixed(1)}');
}
But I have no idea how to get it to show up in a text widget in the app itself.
Any ideas would be greatly appreciated!
Thank you,
Jason

you are not far off the answer, and already catch the value of the calculation needed. As i can see you dont need to pass any parameters to the function so i would recomend using a futurebuilder:
return FutureBuilder(
future: dbHelper.calcOmnivorePercentage(),
builder: (context, AsyncSnapshot<double> snapshot) {
if (snapshot.hasData) {
return Center( child: Text('${snapshot.data.toStringAsFixed(1)}'),);
}else
return Center(
child: CupertinoActivityIndicator(),
);
});
The Future Builder class https://api.flutter.dev/flutter/widgets/FutureBuilder-class.html
Serves to manage widgets that depend on futures, since your calculation and database querys are async you can check its state (As inside the widget in snapshot.hasData). That conditional checks if the future has finished and else shows an indicator. Hope it helps

Related

How to make sqflite database faster?

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

I got issue when i wanna create db to sqflite, DatabaseException(Error Domain=FMDatabase Code=1 "no such table)

I wanna create db with sqflite but this no create and show something issue, Unhandled Exception: DatabaseException(Error Domain=FMDatabase Code=1 "no such table: todo" UserInfo={NSLocalizedDescription=no such table: todo}) sql 'SELECT * FROM todo WHERE id = ?' args [11], oke let's check it my db helper :
db helper
import 'package:sqflite/sqflite.dart';
import 'model.dart';
class DatabaseHelper {
//singleton base
static DatabaseHelper? _instance;
DatabaseHelper._internal() {
_instance = this;
}
factory DatabaseHelper() => _instance ??= DatabaseHelper._internal();
//TODO: add database name
static late Database _database;
//TODO: add get database
Future<Database> get databse async {
_database = await _initializeDatabase();
return _database;
}
//TODO: add db name
static String _tableName = 'todo';
//TODO: initialize db
Future<Database> _initializeDatabase() async {
final path = await getDatabasesPath();
final db = openDatabase('notes.db', version: 1, onCreate: (db, version) {
version:
1;
db.execute('''CREATE TABLE $_tableName(
id INTEGER PRIMARY KEY,
title TEXT,
description TEXT,
)''');
});
return db;
}
Future<void> insertData(Note note) async {
final Database db = await databse;
List<Map<String, dynamic>> result = await db.query(
_tableName,
where: 'id = ?',
whereArgs: [note.id],
);
await db.insert(_tableName, note.toJson());
}
//get
Future<List<Note>> getAllNotes() async {
final Database db = await databse;
List<Map<String, dynamic>> result = await db.query(_tableName);
return result.map((e) => Note.fromJson(e)).toList();
}
}

Issues sqflite in flutter

It's been a while I started flutter. And now I am working on the database part. I started with sqflite since it was the ideal one for offline apps but now I can't understand a thing please can somebody help me with this.
import 'package:flutter/widgets.dart';
import 'note.dart';
import 'package:sqflite/sqflite.dart';
import 'dart:async';
import 'dart:io';
import 'package:path_provider/path_provider.dart';
class DatabaseHelper {
static DatabaseHelper _databaseHelper = DatabaseHelper();
static Database? _database;
String noteTable = 'note_table';
String colID = 'id';
String colTitle = 'title';
String colDescription = 'description';
String colPriority = 'priority';
String colDate = 'date';
DatabaseHelper._createInstance();
factory DatabaseHelper() {
if (_databaseHelper == null) {
var databaseHelper = DatabaseHelper._createInstance();
_databaseHelper = databaseHelper;
}
return _databaseHelper;
}
//custom getter for the database
Future<Database> get database async {
// ignore: prefer_conditional_assignment
if (_database == null) {
_database = await initializeDatabase();
}
return _database!;
}
Future<Database> initializeDatabase() async {
Directory directory = await getApplicationDocumentsDirectory();
String path = directory.path + 'note.db';
var notesDatabase =
await openDatabase(path, version: 1, onCreate: _createDb);
return notesDatabase;
}
void _createDb(Database db, int newVersion) async {
await db.execute(
'CREATE TABLE $noteTable($colID INTEGER PRIMARY KEY AUTOINCREMENT, $colTitle TEXT, $colDescription TEXT, $colPriority INTEGER, $colDate TEXT)');
}
Future<List<Map<String, dynamic>>> getNoteMapList() async {
Database db = await this.database;
var result = await db.query(noteTable, orderBy: '$colPriority ASC');
return result;
}
Future<int> insertData(Note note) async {
Database db = await this.database;
var result = await db.insert(noteTable, note.toMap());
return result;
}
Future<int> updateNote(Note note) async {
Database db = await this.database;
var result = await db
.update(noteTable, note.toMap(), where: '$colID', whereArgs: [note.id]);
return result;
}
Future<int> deleteNote(int id) async {
Database db = await this.database;
var result =
await db.rawDelete('DELETE FROM $noteTable where $colID = $id');
return result;
}
Future<int> getCount() async {
Database db = await this.database;
List<Map<String, dynamic>> x =
await db.rawQuery('SELECT COUNT (*) FROM $noteTable');
int? result = Sqflite.firstIntValue(x);
return result!;
}
Future<List<Note>> getNoteList() async {
var noteMapList = await getNoteMapList();
int count = noteMapList.length;
}
}
This is the whole code. I have watched many videos but can't understand a thing. The problem starts when I the database part starts. And while answering the questions please try to be a little simple.

How to store only new value to sqflite data table in (Flutter)

I have stored my phones call list into a data table. I want to store only new call list data into this data table. It means, only new data will be saved and existing data will be skipped.
Please tell me with example.
Here is my code:
this is the Database Helper
database_helper.dart
import 'dart:io';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path_provider/path_provider.dart';
class DatabaseHelper {
static final _databaseName = "MyDatabase.db";
static final _databaseVersion = 1;
static final table = 'my_table';
static final columnId = '_id';
static final columnName = 'name';
static final columnNumber = 'number';
static final columnType = 'type';
static final columnDate = 'date';
static final columnDuration = 'duration';
// make this a singleton class
DatabaseHelper._privateConstructor();
static final DatabaseHelper instance = DatabaseHelper._privateConstructor();
// only have a single app-wide reference to the database
static Database _database;
Future<Database> get database async {
if (_database != null) return _database;
// lazily instantiate the db the first time it is accessed
_database = await _initDatabase();
return _database;
}
// this opens the database (and creates it if it doesn't exist)
_initDatabase() async {
Directory documentsDirectory = await getExternalStorageDirectory();
String path = join(documentsDirectory.path, _databaseName);
await deleteDatabase(path);
return await openDatabase(path,
version: _databaseVersion, onCreate: _onCreate);
}
// SQL code to create the database table
Future _onCreate(Database db, int version) async {
await db.execute('''
CREATE TABLE $table (
$columnId INTEGER PRIMARY KEY,
$columnName TEXT,
$columnNumber INTEGER,
$columnType TEXT,
$columnDate DATETIME,
$columnDuration INTEGER
)
''');
}
// Helper methods
// Inserts a row in the database where each key in the Map is a column name
// and the value is the column value. The return value is the id of the
// inserted row.
Future<int> insert(Map<String, dynamic> row, {ConflictAlgorithm conflictAlgorithm = ConflictAlgorithm.replace}) async {
Database db = await instance.database;
return await db.insert(table, row, conflictAlgorithm: conflictAlgorithm);
}
// All of the rows are returned as a list of maps, where each map is
// a key-value list of columns.
Future<List<Map<String, dynamic>>> queryAllRows() async {
Database db = await instance.database;
return await db.query(table);
}
// All of the methods (insert, query, update, delete) can also be done using
// raw SQL commands. This method uses a raw query to give the row count.
Future<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(Map<String, dynamic> row) async {
Database db = await instance.database;
int id = row[columnId];
return await db.update(table, row, where: '$columnId = ?', whereArgs: [id]);
}
// Deletes the row specified by the id. The number of affected rows is
// returned. This should be 1 as long as the row exists.
Future<int> delete(int id) async {
Database db = await instance.database;
return await db.delete(table, where: '$columnId = ?', whereArgs: [id]);
}
}
This is the main file. I have added here only the Database insertion method. home.dart
...
Future callLogDB() async {
Iterable<CallLogEntry> cLog = await CallLog.get();
final dbHelper = DatabaseHelper.instance;
cLog.forEach((log) async {
// row to insert
Map<String, dynamic> row = {
DatabaseHelper.columnName: '${log.name}',
DatabaseHelper.columnNumber: '${log.number}',
DatabaseHelper.columnType: '${log.callType}',
DatabaseHelper.columnDate:
'${DateTime.fromMillisecondsSinceEpoch(log.timestamp)}',
DatabaseHelper.columnDuration: '${log.duration}'
};
await dbHelper.insert(row, conflictAlgorithm: ConflictAlgorithm.replace);
print('CallLog:: $row');
});
return cLog;
}
...
What's the problem with my code?
there are several ways to do this, and the one I will offer are not the best or the nicest ones, but hope that they will help
1) Simply write all your data to table
You can just insert all your data to the table setting the ConflictAlgorithm to replace or ignore
db.insert(table, data, conflictAlgorithm: ConflictAlgorithm.replace);
This will replace/ignore same entries
2) Query, compare, replace
This is a less 'elegant' solution, you can first query all your data from table
db.query(table, columns: availableColumns, where: 'columnToQueryBy = ?', whereArgs: [neededValue]);
Then compare to the data you have
Then write using db.insert() as above
I think that in your case the first option suits better, this example pretty much covers most things that might help you
Hope it helps!
WHAT ABOUT Read Data from Sqflite and Show in datatable?

Flutter does not run Async function

I am trying to get an async function _read() to run and the function does not pass the line:
Reading reading = await helper.queryReading(rowId); in this function:
_read() async {
DatabaseHelper helper = DatabaseHelper.instance;
int rowId = 1;
//lines above here executes
Reading reading = await helper.queryReading(rowId); //this is the line it stops on
// nothing below here is executed
if (reading == null) {
print('read row $rowId: empty');
} else {
print('read row $rowId: ${reading.reading}');
}
}
It is being called from the following function
class Profile {
Widget getScreen(){
print("Attempting to read db");
_read();
return Scaffold( ...)
Here is my helper class:
import 'dart:ffi';
import 'dart:io';
import 'package:ema/Readings.dart';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path_provider/path_provider.dart';
//table structure
final String tableName = 'Readings';
final String databasecolumnId = '_id';
final String databaseReading = 'Reading';
final String databaseDate = 'Time';
class DatabaseHelper {
//This is the name of the database file on disk
static final _databaseName = "readings.db";
//handles versioning for databases
static final _databaseVersion = 1;
//makes a singleton classs
DatabaseHelper._privateConstructor();
static final DatabaseHelper instance = DatabaseHelper._privateConstructor();
//allows only one item to access the database
static Database _database;
Future<Database> get database async {
_database = await _initDatabase();
return database;
}
//this opens and creates the database
_initDatabase() async {
// The path_provider plugin gets the right directory for Android or iOS.
Directory documentsDirectory = await getApplicationDocumentsDirectory();
String path = join(documentsDirectory.path, _databaseName);
// Open the database. Can also add an onUpdate callback parameter.
return await openDatabase(path,
version: _databaseVersion,
onCreate: _onCreate);
}
//Creates the database
Future _onCreate(Database db, int version) async {
await db.execute(
'''
CREATE TABLE $tableName (
$databasecolumnId INTEGER PRIMARY KEY,
$databaseReading REAL NOT NULL,
$databaseDate INTERGER NOT NULL
)
'''
);
}
Future<int> insertReading(Reading reading) async {
Database db = await database;
int id = await db.insert(tableName, reading.toMap());
return id;
}
//gets reading
Future<Reading> queryReading(int id) async {
print("queryReading"); //gets here
Database db = await database;
print("Getting Db"); // not actually getting here
List<Map> maps = await db.query(tableName,
columns: [databasecolumnId, databaseReading, databaseDate],
where: '$databasecolumnId = ?',
whereArgs: [id]);
if (maps.length > 0) {
return Reading.fromMap(maps.first);
}
print('maps length : ${maps.length}');
return null;
}
}
Here is my Readings class:
class Reading {
int id;
double reading;
DateTime date;
//constructor
Reading({this.id, this.reading, this.date});
Map<String, dynamic> toMap() {
var map = <String, dynamic>{
databaseReading: reading,
databaseDate: date.millisecondsSinceEpoch,
};
if (id != null) {
map[databasecolumnId] = id;
}
return map;
}
//extracts a node object from the map obect
Reading.fromMap(Map<String, dynamic> map) {
id = map[databasecolumnId];
reading = map[databaseReading];
date = new DateTime.fromMillisecondsSinceEpoch(map [databaseDate]);
}
}
Turns out there was a deadlock in getting the database. By putting a lock on it it worked.
Here is the code to resolve it:
///declreation of the database
Database _database;
///Gets the database ensuring that there are no locks currently on the database
Future<Database> get database async {
if (_database != null) return _database;
_database = await _initDatabase();
return _database;
}