SQFLITE ERROR: SqfliteDatabaseException (DatabaseException(database_closed)) - ios

The two pages in the application are listed with database(sqlite). but when I want to switch between pages, I get such an error: SqfliteDatabaseException (DatabaseException(database_closed))
please help mee.. I don't understand why see this error.
import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
import 'package:bankingapp/models/coin.dart';
import 'package:bankingapp/models/histories.dart';
import 'package:flutter/services.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
class DbHelper {
static Database? _db;
Future<Database> get db async {
return _db ??= await initDb();
}
Future<Database> initDb() async {
var dbFolder = await getDatabasesPath();
String path = join(dbFolder, 'app.db');
// Delete any existing database:
await deleteDatabase(path);
// Create the writable database file from the bundled demo database file:
try {
await Directory(dirname(path)).create(recursive: true);
} catch (_) {}
ByteData data =
await rootBundle.load(join("assets/database", "bankingapp.db"));
List<int> bytes =
data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes);
await new File(path).writeAsBytes(bytes, flush: true);
//open the database
return await openDatabase(path);
}
Future<List<Histories>> getHistories() async {
var dbClient = await db;
var result = await dbClient.rawQuery("SELECT * FROM Histories");
return result.map((data) => Histories.fromMap(data)).toList();
}
Future<List<Coins>> getCoins() async {
var dbClient = await db;
var result = await dbClient.rawQuery("SELECT * FROM Coins");
return result.map((data) => Coins.fromMap(data)).toList();
}
}

I don't exactly know the answer but I suggest you to use Floor package. This is really simple to implement and it is the abstraction of SQLite database so I think it will be familiar to you. I don't seem to face such error with this package.
https://pub.dev/packages/floor

Related

SqfliteDatabaseException (DatabaseException(database_closed))

The two pages in the application are listed with database(sqlite). but when I want to switch between pages, I get such an error: SqfliteDatabaseException (DatabaseException(database_closed))
please help mee.. I don't understand why see this error.
import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
import 'package:bankingapp/models/coin.dart';
import 'package:bankingapp/models/histories.dart';
import 'package:flutter/services.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
class DbHelper {
static Database? _db;
Future<Database> get db async {
return _db ??= await initDb();
}
Future<Database> initDb() async {
var dbFolder = await getDatabasesPath();
String path = join(dbFolder, 'app.db');
// Delete any existing database:
await deleteDatabase(path);
// Create the writable database file from the bundled demo database file:
try {
await Directory(dirname(path)).create(recursive: true);
} catch (_) {}
ByteData data =
await rootBundle.load(join("assets/database", "bankingapp.db"));
List<int> bytes =
data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes);
await new File(path).writeAsBytes(bytes, flush: true);
//open the database
return await openDatabase(path);
/*
var dbFolder = await getDatabasesPath();
String path = join(dbFolder, 'app.db');
return await openDatabase(path);*/
}
Future<List<Histories>> getHistories() async {
var dbClient = await db;
var result = await dbClient.rawQuery("SELECT * FROM Histories");
return result.map((data) => Histories.fromMap(data)).toList();
}
Future<List<Coins>> getCoins() async {
var dbClient = await db;
var result = await dbClient.rawQuery("SELECT * FROM Coins");
return result.map((data) => Coins.fromMap(data)).toList();
}
}
First, uninstall the app from the device and run flutter clean and then build a new app and run it again to see if the error occurs.
Then, check the device in which you are running your app has restricted your app or not, see if your device requires any particular permission.
If it still occurs according to your provided information it seems like the problem is either the database is getting closed or it's not opening it can be because of many reasons like not doing proper initialization or even just forgetting to put await. Check the database initialization code in which you are opening your database or not then check if you are by mistake closing the database somewhere, which should have been done only when the app is terminated.
If you have done both of the above steps correct check your database location and mapping.
If nothing is working please try to follow official sqflite docs and example, as your code seems different from the one shown in the plugin example.

Get the list of files from directory

I am trying to add the content of directory using this approach and asign it to List but not sure why it's not working. I see it's async problem but not sure how to solve it.I expect to return the list of files from retCont() but instead it's returning empty list.
import 'dart:io';
void main() async {
print('CONT: ${await retCont()}');
}
Future retCont() async {
var myDir = Directory.current;
List cont = [];
await myDir.list().listen((FileSystemEntity entity) {
print(entity.path);
cont.add(entity.path);
});
return cont;
}
listen() returns a StreamSubscription<FileSystemEntity> which is not a Future so you cannot await on that.
The list() call returns Stream<FileSystemEntity>. For streams you can instead of await and listen() use await for like this:
import 'dart:io';
Future<void> main() async {
print('CONT: ${await retCont()}');
}
Future<List<String>> retCont() async {
final myDir = Directory.current;
final cont = <String>[];
await for (final entity in myDir.list()) {
print(entity.path);
cont.add(entity.path);
}
return cont;
}
And as a bonus fact, the same program can be written like this if you skip the print(entity.path);.
import 'dart:io';
Future<void> main() async {
print('CONT: ${await retCont()}');
}
Future<List<String>> retCont() =>
Directory.current.list().map((event) => event.path).toList();
If you really want to use StreamSubscription I think the easiest way is to use a Completer instance which you can complete when you have got all the elements from List():
import 'dart:async';
import 'dart:io';
Future<void> main() async {
print('CONT: ${await retCont()}');
}
Future<List<String>> retCont() {
final myDir = Directory.current;
final cont = <String>[];
final completer = Completer<List<String>>();
myDir.list().listen((FileSystemEntity entity) {
print(entity.path);
cont.add(entity.path);
}, onDone: () => completer.complete(cont));
return completer.future;
}

How to write a `ByteData` instance to a File in Dart?

I am using Flutter to load an "asset" into a File so that a native application can access it.
This is how I load the asset:
final dbBytes = await rootBundle.load('assets/file');
This returns an instance of ByteData.
How can I write this to a dart.io.File instance?
ByteData is an abstraction for:
A fixed-length, random-access sequence of bytes that also provides
random and unaligned access to the fixed-width integers and floating
point numbers represented by those bytes.
As Gunter mentioned in the comments, you can use File.writeAsBytes. It does require a bit of API work to get from ByteData to a List<int>, however.
import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
Future<void> writeToFile(ByteData data, String path) {
final buffer = data.buffer;
return new File(path).writeAsBytes(
buffer.asUint8List(data.offsetInBytes, data.lengthInBytes));
}
I've also filed an issue to make the docs on Flutter more clear for this use case.
you need to have path_provider package installed, then
This should work :
import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
import 'package:path_provider/path_provider.dart';
final dbBytes = await rootBundle.load('assets/file'); // <= your ByteData
//=======================
Future<File> writeToFile(ByteData data) async {
final buffer = data.buffer;
Directory tempDir = await getTemporaryDirectory();
String tempPath = tempDir.path;
var filePath = tempPath + '/file_01.tmp'; // file_01.tmp is dump file, can be anything
return new File(filePath).writeAsBytes(
buffer.asUint8List(data.offsetInBytes, data.lengthInBytes));
}
//======================
to get your file :
var file;
try {
file = await writeToFile(dbBytes); // <= returns File
} catch(e) {
// catch errors here
}
Hope this helps,
Thank you.
to search flutter ByteData to List<int> then found here, but not fully answer my question:
how to convert ByteData to List<int> ?
after self investigate, solution is:
use .cast<int>()
ByteData audioByteData = await rootBundle.load(audioAssetsFullPath);
Uint8List audioUint8List = audioByteData.buffer.asUint8List(audioByteData.offsetInBytes, audioByteData.lengthInBytes);
List<int> audioListInt = audioUint8List.cast<int>();
or 2. use .map
ByteData audioByteData = await rootBundle.load(audioAssetsFullPath);
Uint8List audioUint8List = audioByteData.buffer.asUint8List(audioByteData.offsetInBytes, audioByteData.lengthInBytes);
List<int> audioListInt = audioUint8List.map((eachUint8) => eachUint8.toInt()).toList();
For those looking to write bytes (aka Uint8List) instead of ByteData please note that ByteData is a wrapper for Uint8List.
From /runtime/lib/typed_data.patch:
#patch
class ByteData implements TypedData {
#patch
#pragma("vm:entry-point")
factory ByteData(int length) {
final list = new Uint8List(length) as _TypedList;
_rangeCheck(list.lengthInBytes, 0, length);
return new _ByteDataView(list, 0, length);
}
#patch
class Uint8List {
#patch
#pragma("vm:exact-result-type", _Uint8List)
factory Uint8List(int length) native "TypedData_Uint8Array_new";
}
If you are using the latter type you can use the answer provided by Rami and modify the return as follow:
import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
import 'package:path_provider/path_provider.dart';
Future<File> writeToFile(Uint8List data) async {
(...)
return new File(filePath).writeAsBytes(data);
}

Generate one file for a list of parsed files using source_gen in dart

I have a list of models that I need to create a mini reflective system.
I analyzed the Serializable package and understood how to create one generated file per file, however, I couldn't find how can I create one file for a bulk of files.
So, how to dynamically generate one file, using source_gen, for a list of files?
Example:
Files
user.dart
category.dart
Generated:
info.dart (containg information from user.dart and category.dart)
Found out how to do it with the help of people in Gitter.
You must have one file, even if empty, to call the generator. In my example, it is lib/batch.dart.
source_gen: ^0.5.8
Here is the working code:
The tool/build.dart
import 'package:build_runner/build_runner.dart';
import 'package:raoni_global/phase.dart';
main() async {
PhaseGroup pg = new PhaseGroup()
..addPhase(batchModelablePhase(const ['lib/batch.dart']));
await build(pg,
deleteFilesByDefault: true);
}
The phase:
batchModelablePhase([Iterable<String> globs =
const ['bin/**.dart', 'web/**.dart', 'lib/**.dart']]) {
return new Phase()
..addAction(
new GeneratorBuilder(const
[const BatchGenerator()], isStandalone: true
),
new InputSet(new PackageGraph.forThisPackage().root.name, globs));
}
The generator:
import 'dart:async';
import 'package:analyzer/dart/element/element.dart';
import 'package:build/build.dart';
import 'package:source_gen/source_gen.dart';
import 'package:glob/glob.dart';
import 'package:build_runner/build_runner.dart';
class BatchGenerator extends Generator {
final String path;
const BatchGenerator({this.path: 'lib/models/*.dart'});
#override
Future<String> generate(Element element, BuildStep buildStep) async {
// this makes sure we parse one time only
if (element is! LibraryElement)
return null;
String libraryName = 'raoni_global', filePath = 'lib/src/model.dart';
String className = 'Modelable';
// find the files at the path designed
var l = buildStep.findAssets(new Glob(path));
// get the type of annotation that we will use to search classes
var resolver = await buildStep.resolver;
var assetWithAnnotationClass = new AssetId(libraryName, filePath);
var annotationLibrary = resolver.getLibrary(assetWithAnnotationClass);
var exposed = annotationLibrary.getType(className).type;
// the caller library' name
String libName = new PackageGraph.forThisPackage().root.name;
await Future.forEach(l.toList(), (AssetId aid) async {
LibraryElement lib;
try {
lib = resolver.getLibrary(aid);
} catch (e) {}
if (lib != null && Utils.isNotEmpty(lib.name)) {
// all objects within the file
lib.units.forEach((CompilationUnitElement unit) {
// only the types, not methods
unit.types.forEach((ClassElement el) {
// only the ones annotated
if (el.metadata.any((ElementAnnotation ea) =>
ea.computeConstantValue().type == exposed)) {
// use it
}
});
});
}
});
return '''
$libName
''';
}
}
It seems what you want is what this issue is about How to generate one output from many inputs (aggregate builder)?
[Günter]'s answer helped me somewhat.
Buried in that thread is another thread which links to a good example of an aggregating builder:
1https://github.com/matanlurey/build/blob/147083da9b6a6c70c46eb910a3e046239a2a0a6e/docs/writing_an_aggregate_builder.md
The gist is this:
import 'package:build/build.dart';
import 'package:glob/glob.dart';
class AggregatingBuilder implements Builder {
/// Glob of all input files
static final inputFiles = new Glob('lib/**');
#override
Map<String, List<String>> get buildExtensions {
/// '$lib$' is a synthetic input that is used to
/// force the builder to build only once.
return const {'\$lib$': const ['all_files.txt']};
}
#override
Future<void> build(BuildStep buildStep) async {
/// Do some operation on the files
final files = <String>[];
await for (final input in buildStep.findAssets(inputFiles)) {
files.add(input.path);
}
String fileContent = files.join('\n');
/// Write to the file
final outputFile = AssetId(buildStep.inputId.package,'lib/all_files.txt');
return buildStep.writeAsString(outputFile, fileContent);
}
}

Dependency injection resolution of async factory

Having some issues with injecting a database connection into a class via the di.dart package. Specifically, resolving an async dependency in the toFactory option.
Both attempts result in the
NoSuchMethodError: Class '_Future' has no instance method 'query'.
error and it's unclear the correct path forward. I would prefer to keep the conn property without being wrapped in a Future. I've attempted doing the unwrapping of the Future in the class constructor but async constructors are not allowed in Dart at this time.
import 'package:postgresql/pool.dart';
import 'package:postgresql/postgresql.dart';
import 'package:di/di.dart';
main() async {
var uri = "postgres://username:password#localhost:5432/database";
var pool = new Pool(uri, minConnections: 2, maxConnections: 5);
await pool.start();
Module module = new Module();
module.bind(TestQuery);
module.bind(TestController);
module.bind(Pool, toValue: pool);
module.bind(Connection, toFactory: (pool) => pool.connect(), inject: [Pool]);
var injector = new ModuleInjector([module]);
var html = await injector.get(TestController).index();
print(html);
}
class BaseQuery {
Connection conn;
BaseQuery(Connection this.conn);
}
class TestQuery extends BaseQuery {
TestQuery(Connection conn) : super(conn); // type '_Future' is not a subtype of type 'Connection' of 'conn' where
run() async {
var results = await conn.query("select 1").toList();
// Do some data manipulation
return results;
}
}
class TestController {
TestQuery testQuery;
TestController(TestQuery this.testQuery);
index() async {
var results = await testQuery.run();
var html = "<pre>" + results.toString() + "</pre>";
return html;
}
}
I can reproduce but when I change the line to
var conn = await injector.get(Connection);
var res = await conn.query("select 1").toList();
it working fine.
I think your code should work as well though.

Resources