Importing Sqlite Database from a File in Flutter - sqlite

I have a Sqlite database file that is always in the same spot on the device, in Documents named backup.db. What I want is to import that database to replace existing Sqlite database, but I am getting some strange errors. Anyways, here is the code:
class DbHelper {
static const currentDatabaseVersion = 1;
Future<void> init() async {
Database db;
final dbPath = await ExtStorage.getExternalStoragePublicDirectory(
ExtStorage.DIRECTORY_DOCUMENTS);
final newPath = join(dbPath, '/backup.db');
String localDatabasePath = newPath;
//Internal database path
var databasesPath = await getDatabasesPath();
var path = join(databasesPath, "foodDB.db");
db = await openDatabase(path);
if (await db.getVersion() < currentDatabaseVersion) {
db.close();
await deleteDatabase(path);
await _checkParentDirectoryExists(path);
ByteData data = await _getByteDataFromLocalDatabase(localDatabasePath);
List<int> bytes = _databaseByteDataToList(data);
await _writeBytesToInternalDatabase(path, bytes);
db = await openDatabase(path);
db.setVersion(currentDatabaseVersion);
}
}
static Future<void> _checkParentDirectoryExists(var path) async {
try {
await Directory(dirname(path)).create(recursive: true);
} catch (e) {
print(e);
}
}
static Future<ByteData> _getByteDataFromLocalDatabase(
String localDatabasePath) async {
return await rootBundle.load(join(localDatabasePath));
}
static List<int> _databaseByteDataToList(ByteData data) {
return data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes);
}
static Future<void> _writeBytesToInternalDatabase(
var path, List<int> bytes) async {
await File(path).writeAsBytes(bytes, flush: true);
}
}
As far as I got, it gets the 'documents' directory, merges the path with backup.db file that already exists in Documents, then it should just import it if it exists. Let me know if there is anything I am doing wrong. I am using the ext_storage library to locate the documents folder and sqlite of course to be able to import the database.
And in another screen I call this function like this:
DbHelper dbHelper = new DbHelper();
onTap: () => dbHelper.init(),
Here's the stacktrace:
ter (25910): [ERROR:flutter/lib/ui/ui_dart_state.cc(177)] Unhandled Exception: Unable to load asset: /backup.db
E/flutter (25910): #0 PlatformAssetBundle.load
package:flutter/…/services/asset_bundle.dart:225
E/flutter (25910): <asynchronous suspension>
E/flutter (25910): #1 DbHelper._getByteDataFromLocalDatabase
package:CWCFlutter/db/import_database.dart:72
E/flutter (25910): #2 DbHelper.init
package:CWCFlutter/db/import_database.dart:53
E/flutter (25910): <asynchronous suspension>
E/flutter (25910): #3 _FoodListState.build.<anonymous closure>
package:CWCFlutter/food_list.dart:919
E/flutter (25910): #4 GestureRecognizer.invokeCallback
package:flutter/…/gestures/recognizer.dart:183
E/flutter (25910): #5 TapGestureRecognizer.handleTapUp
package:flutter/…/gestures/tap.dart:598
E/flutter (25910): #6 BaseTapGestureRecognizer._checkUp
package:flutter/…/gestures/tap.dart:287
E/flutter (25910): #7 BaseTapGestureRecognizer.acceptGesture
package:flutter/…/gestures/tap.dart:259
E/flutter (25910): #8 GestureArenaManager.sweep
package:flutter/…/gestures/arena.dart:157
E/flutter (25910): #9 GestureBinding.handleEvent
package:flutter/…/gestures/binding.dart:362
E/flutter (25910): #10 GestureBinding.dispatchEvent
package:flutter/…/gestures/binding.dart:338
E/flutter (25910): #11 RendererBinding.dispatchEvent
package:flutter/…/rendering/binding.dart:267
E/flutter (25910): #12 GestureBinding._handlePointerEvent
package:flutter/…/gestures/binding.dart:295
E/flutter (25910): #13 GestureBinding._flushPointerEventQueue
package:flutter/…/gestures/binding.dart:240
E/flutter (25910): #14 GestureBinding._handlePointerDataPacket
package:flutter/…/gestures/binding.dart:213
E/flutter (25910): #15 _rootRunUnary (dart:async/zone.dart:1206:13)
E/flutter (25910): #16 _CustomZone.runUnary (dart:async/zone.dart:1100:19)
E/flutter (25910): #17 _CustomZone.runUnaryGuarded (dart:async/zone.dart:1005:7)
E/flutter (25910): #18 _invoke1 (dart:ui/hooks.dart:265:10)
E/flutter (25910): #19 _dispatchPointerDataPacket (dart:ui/hooks.dart:174:5)
When I tap on the button to import the database, it is completely unresponsive, not sure what the issue is.

Try the code below. It is similar to what you want but also with a versioning system. When you init the database, it will open the database in the phone directory and check the version of that. If it's outdated or doesn't exist it will delete the current one and create a new one (or just create a new one in the case that it doesn't exist).
static const currentDatabaseVersion = 1;
static Future<void> init() async {
Database db;
String localDatabasePath = 'your database path here';
//Internal database path
var databasesPath = await getDatabasesPath();
var path = join(databasesPath, "database.db");
db = await openDatabase(path);
if (await db.getVersion() < currentDatabaseVersion) {
db.close();
await deleteDatabase(path);
await _checkParentDirectoryExists(path);
ByteData data = await _getByteDataFromLocalDatabase(localDatabasePath);
List<int> bytes = _databaseByteDataToList(data);
await _writeBytesToInternalDatabase(path, bytes);
db = await openDatabase(path);
db.setVersion(currentDatabaseVersion);
}
}
static Future<void> _checkParentDirectoryExists(var path) async {
try {
await Directory(dirname(path)).create(recursive: true);
} catch (e) {
print(e);
}
}
static Future<ByteData> _getByteDataFromLocalDatabase(
String localDatabasePath) async {
return await rootBundle.load(join(localDatabasePath));
}
static List<int> _databaseByteDataToList(ByteData data) {
return data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes);
}
static Future<void> _writeBytesToInternalDatabase(
var path, List<int> bytes) async {
await File(path).writeAsBytes(bytes, flush: true);
}

Related

Importing Existing Sqlite Database in Flutter

I have this Sqlite database file inside the device's 'Documents' folder named backup.db. Now I have written function that should be able to import that database, but I am getting some weird asynchronous errors, not sure what I am doing wrong here, might need to add something. Here's the code for importing the existing file:
class DbHelper {
Future initDb() async {
final dbPath = await ExtStorage.getExternalStoragePublicDirectory(
ExtStorage.DIRECTORY_DOCUMENTS);
final path = join(dbPath, '/backup.db');
final exist = await databaseExists(path);
if (exist) {
print('db imported');
await openDatabase(path);
} else {
print('creating copy of database');
//try {
//await Directory(dirname(path)).create(recursive: true);
//} catch (_) {}
//ByteData data = await rootBundle.load(join(dbPath, '/backup.db'));
//List<int> bytes =
// data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes);
// await File(path).writeAsBytes(bytes, flush: true);
}
await openDatabase(path);
}
}
As far as I got, it gets the 'documents' directory, merges the path with backup.db file that already exists in Documents, then it should just import it if it exists. Let me know if there is anything I am doing wrong. I am using the ext_storage library to locate the documents folder and sqlite of course to be able to import the database.
And in another screen I call this function like this:
DbHelper dbHelper = new DbHelper();
onTap: () => dbHelper.initDb(),
Here's the stacktrace:
[ERROR:flutter/lib/ui/ui_dart_state.cc(177)] Unhandled Exception: Unable to load asset: /backup.db
E/flutter (22458): #0 PlatformAssetBundle.load
package:flutter/…/services/asset_bundle.dart:225
E/flutter (22458): <asynchronous suspension>
E/flutter (22458): #1 DbHelper.initDb
package:CWCFlutter/db/import_database.dart:21
E/flutter (22458): <asynchronous suspension>
E/flutter (22458): #2 _FoodListState.build.<anonymous closure>
package:CWCFlutter/food_list.dart:919
E/flutter (22458): #3 GestureRecognizer.invokeCallback
package:flutter/…/gestures/recognizer.dart:183
E/flutter (22458): #4 TapGestureRecognizer.handleTapUp
package:flutter/…/gestures/tap.dart:598
E/flutter (22458): #5 BaseTapGestureRecognizer._checkUp
package:flutter/…/gestures/tap.dart:287
E/flutter (22458): #6 BaseTapGestureRecognizer.acceptGesture
package:flutter/…/gestures/tap.dart:259
E/flutter (22458): #7 GestureArenaManager.sweep
package:flutter/…/gestures/arena.dart:157
E/flutter (22458): #8 GestureBinding.handleEvent
package:flutter/…/gestures/binding.dart:362
E/flutter (22458): #9 GestureBinding.dispatchEvent
package:flutter/…/gestures/binding.dart:338
E/flutter (22458): #10 RendererBinding.dispatchEvent
package:flutter/…/rendering/binding.dart:267
E/flutter (22458): #11 GestureBinding._handlePointerEvent
package:flutter/…/gestures/binding.dart:295
E/flutter (22458): #12 GestureBinding._flushPointerEventQueue
package:flutter/…/gestures/binding.dart:240
E/flutter (22458): #13 GestureBinding._handlePointerDataPacket
package:flutter/…/gestures/binding.dart:213
E/flutter (22458): #14 _rootRunUnary (dart:async/zone.dart:1206:13)
E/flutter (22458): #15 _CustomZone.runUnary (dart:async/zone.dart:1100:19)
E/flutter (22458): #16 _CustomZone.runUnaryGuarded (dart:async/zone.dart:1005:7)
E/flutter (22458): #17 _invoke1 (dart:ui/hooks.dart:265:10)
E/flutter (22458): #18 _dispatchPointerDataPacket (dart:ui/hooks.dart:174:5)
updated code
class DbHelper {
Future<void> initDB() async {
Directory documentsDirectory = await getApplicationDocumentsDirectory();
String newPath = join(documentsDirectory.path, '/backup.db');
final exist = await databaseExists(newPath);
print("This print works.");
if (!exist) {
try {
final dbPath = await ExtStorage.getExternalStoragePublicDirectory(
ExtStorage.DIRECTORY_DOCUMENTS);
final path = join(dbPath, '/backup.db');
File(path).copySync(newPath);
print("This print doesn't work.");
} catch (_) {}
}
}
Future<Database> openDB() async {
Directory documentsDirectory = await getApplicationDocumentsDirectory();
String path = join(documentsDirectory.path, '/backup.db');
await initDB();
Database db = await openDatabase(path);
return db;
}
}
ByteData data = await rootBundle.load(join(dbPath, '/backup.db'));
You are trying to load "/backup.db" from the asset bundle adding dbPath to the key.
You need to add the backup.db to the assets folder if you want to load the database this way.
If you know the location of the .db file and it's not in the assets then why do you need to import it? Just open it.
You can copy the database to another location using File(path).copy(String newPath)
Future<void> initDB() async {
Directory documentsDirectory = await getApplicationDocumentsDirectory();
String newPath = join(documentsDirectory.path, '/backup.db');
final exist = await databaseExists(newPath);
if (!exists) {
try {
final dbPath = await ExtStorage.getExternalStoragePublicDirectory(
ExtStorage.DIRECTORY_DOCUMENTS);
final path = join(dbPath, '/backup.db');
File(path).copySync(newPath);
} catch (_) {}
}
}
Future<Database> openDB() async {
Directory documentsDirectory = await getApplicationDocumentsDirectory();
String path = join(documentsDirectory.path, '/backup.db');
await initDB();
Database db = await openDatabase(path);
return db;
}

Error database_closed when using flutter's sqflite

I try to use sqflite to save some data like my class Movie but when i try to insert or query on the database, see this message:
[ERROR:flutter/lib/ui/ui_dart_state.cc(166)] Unhandled Exception: DatabaseException(error database_closed)
my MovieBloc class:
List<Movie> _movies = new List<Movie> ();
class MoviesBloc implements BaseBloc {
final _moviesController = StreamController<List<Movie>>();
Database moviesDB;
String _moviesPath;
Stream<List<Movie>> get moviesStream => _moviesController.stream;
List<Movie> get movies => _movies;
int getLengthMovieList() {
return _movies.length;
}
clearMovieList() {
_movies.clear();
}
Future<void> openMovieDB({String dbName:'movies.db'}) async {
var databasesPath = await getDatabasesPath();
_moviesPath = join(databasesPath, dbName);
moviesDB = await openDatabase(_moviesPath, version: 1,
onCreate: (Database db, int version) async {
await db.execute(
'CREATE TABLE movies (Id INTEGER PRIMARY KEY AUTOINCREMENT, Title TEXT NOT NULL, imdbRating TEXT, Poster TEXT, Plot TEXT, Saved INTEGER DEFAULT 0)');
});
}
Future<void> closeMovieDB() async => moviesDB.close();
void dispose() {
_moviesController.close();
}
}
my MovieProvider class:
class MovieProvider extends MoviesBloc {
String _tableName = 'movies';
Future<Movie> insert(Movie movie, {conflictAlgorithm: ConflictAlgorithm.ignore}) async {
await moviesDB.insert(_tableName, movie.toMap(), conflictAlgorithm: conflictAlgorithm);
return movie;
}
Future<bool> insertAll(List<Movie> movies) async {
await Future.wait(movies.map((movie) async {
await this.insert(movie);
}
));
return true;
}
Future<List<Movie>> paginate(int page, {int limit: 15}) async {
print('in Paginate to SQLite');
List<Map> maps = await moviesDB.query(_tableName,
columns: ['Id', 'Title', 'imdbRating', 'Poster', 'Plot', 'Saved'],
limit: limit,
offset: page == 1 ? 0 : ((page -1) * limit)
);
List<Movie> movies = new List<Movie> ();
if (maps.length > 0) {
maps.map((movie) {
movies.add(Movie.fromJson(movie));
}
);
}
return movies;
}
}
my save method to sqflite:
static Future<bool> saveAllMoviesIntoSqlite(List<Movie> movies) async {
var db = new MovieProvider();
await db.openMovieDB();
await db.insertAll(movies);
await db.closeMovieDB();
return true;
}
my load method from sqflite:
static Future<Map> getAllMoviesFromSqlite(int page) async {
var db = new MovieProvider();
await db.openMovieDB();
List<Movie> movies = new List<Movie> ();
movies = await db.paginate(page);
await db.closeMovieDB();
return {
"currentPage": page,
"movies": movies
};
}
I observe that in insertAll method from MovieProvider class for insert any movie from the movies list to sq with await and async:
Future<bool> insertAll(List<Movie> movies) async {
await Future.wait(movies.map((movie) async {
await this.insert(movie);
}
));
return true;
}
I try to close and open database sometimes sequential and other plays with close and open database ...
my log:
I/flutter ( 1617): in Paginate to SQLite
E/flutter ( 1617): [ERROR:flutter/lib/ui/ui_dart_state.cc(166)] Unhandled Exception: DatabaseException(error database_closed)
E/flutter ( 1617): #0 SqfliteDatabaseMixin.checkNotClosed (package:sqflite_common/src/database_mixin.dart:282:7)
E/flutter ( 1617): #1 SqfliteDatabaseExecutorMixin._rawQuery (package:sqflite_common/src/database_mixin.dart:125:8)
E/flutter ( 1617): #2 SqfliteDatabaseExecutorMixin.query (package:sqflite_common/src/database_mixin.dart:110:12)
E/flutter ( 1617): #3 MovieProvider.paginate (package:umdb/bloc/movie_provider.dart:24:38)
E/flutter ( 1617): #4 MovieService.getAllMoviesFromSqlite (package:umdb/services/movie_service.dart:54:23)
E/flutter ( 1617): <asynchronous suspension>
E/flutter ( 1617): #5 MovieService.getSavedFromSQ (package:umdb/services/movie_service.dart:95:26)
E/flutter ( 1617): #6 MovieService.fetchSavedMovies (package:umdb/services/movie_service.dart:105:5)
E/flutter ( 1617): <asynchronous suspension>
E/flutter ( 1617): #7 MovieDetailState.build.<anonymous closure>.<anonymous closure> (package:umdb/ui_widgets/movie_detail.dart:168:42)
E/flutter ( 1617): #8 State.setState (package:flutter/src/widgets/framework.dart:1240:30)
E/flutter ( 1617): #9 MovieDetailState.build.<anonymous closure> (package:umdb/ui_widgets/movie_detail.dart:142:27)
E/flutter ( 1617): #10 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:184:24)
E/flutter ( 1617): #11 TapGestureRecognizer.handleTapUp (package:flutter/src/gestures/tap.dart:524:11)
E/flutter ( 1617): #12 BaseTapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:284:5)
E/flutter ( 1617): #13 BaseTapGestureRecognizer.acceptGesture (package:flutter/src/gestures/tap.dart:256:7)
E/flutter ( 1617): #14 GestureArenaManager.sweep (package:flutter/src/gestures/arena.dart:158:27)
E/flutter ( 1617): #15 GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:224:20)
E/flutter ( 1617): #16 GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:200:22)
E/flutter ( 1617): #17 GestureBinding._handlePointerEvent (package:flutter/src/gestures/binding.dart:158:7)
E/flutter ( 1617): #18 GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:104:7)
E/flutter ( 1617): #19 GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:88:7)
E/flutter ( 1617): #20 _rootRunUnary (dart:async/zone.dart:1206:13)
E/flutter ( 1617): #21 _CustomZone.runUnary (dart:async/zone.dart:1100:19)
E/flutter ( 1617): #22 _CustomZone.runUnaryGuarded (dart:async/zone.dart:1005:7)
E/flutter ( 1617): #23 _invoke1 (dart:ui/hooks.dart:267:10)
E/flutter ( 1617): #24 _dispatchPointerDataPacket (dart:ui/hooks.dart:176:5)
please help me to fix it
It is very hard to deal with opening/closing the database. There are indeed many cases where you might access a closed database and even worse, if you open twice the same database, you get the same instance so the first close will close the database.
Personally I recommend keeping the database open.
If you need to handle multiple databases and open/close them for short actions, make sure you have the proper mutex/lock mechanism so that you control how your database is opened/accessed/closed without any other method trying to access the same database at the same time (ok that part is not clear sorry).
Something like that (although I really don't recommend opening/closing each time):
import 'package:synchronized/synchronized.dart';
final static _lock = Lock();
static Future<bool> saveAllMoviesIntoSqlite(List<Movie> movies) async {
return _lock.synchronized(() {
var db = new MovieProvider();
await db.openMovieDB();
await db.insertAll(movies);
await db.closeMovieDB();
return true;
});
}
static Future<Map> getAllMoviesFromSqlite(int page) async {
return _lock.synchronized(() {
var db = new MovieProvider();
await db.openMovieDB();
List<Movie> movies = new List<Movie> ();
movies = await db.paginate(page);
await db.closeMovieDB();
return {
"currentPage": page,
"movies": movies
};
});
}
You need to set null for your database instance when you close your database. Otherwise it will always return the old instance.
Future close() async {
final db = await instance.database;
_database = null;
return db.close();
}
Keeping database without close, and using Singleton pattern is a good choice to solve this problem.
1- Create new file sql_lite_service.dart, this will contains SqlLiteService class with this full code:
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
class SqlLiteService {
String dBName = 'my_db_name';
int dBVersion = 1;
// Singleton pattern
static final SqlLiteService _databaseService = SqlLiteService._internal();
factory SqlLiteService() => _databaseService;
SqlLiteService._internal();
static Database? _database;
Future<Database> get database async {
if (_database != null) return _database!;
_database = await _initDatabase();
return _database!;
}
Future<Database> _initDatabase() async {
// Set the version. This executes the onCreate function and provides a
// path to perform database upgrades and downgrades.
Database db = await _getDB();
return db;
}
Future<Database> _getDB() async{
final path = await _getPath(); // Get a location using getDatabasesPath
return await openDatabase(
path,
onCreate: _onCreate,
version: dBVersion,
onConfigure: (db) async => await db.execute('PRAGMA foreign_keys = ON'),
);
}
// create tables
Future<void> _onCreate(Database db, int version) async {
// Run the CREATE {users} TABLE statement on the database.
await db.execute(
'CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, created_at datetime default current_timestamp, updated_at datetime default current_timestamp)'
);
// todo: Add your code here ...
}
Future<String> _getPath() async {
String databasesPath = await getDatabasesPath();
String path = join(databasesPath, dBName);
return path;
}
}
2- Now you can get Database instance from any other class by:
final Database database = await SqlLiteService().database;
// usage example
await database.rawDelete(
'DELETE FROM users WHERE id = ?',
[1]
);
Using this way will solve database_closed error...

I was trying to upload video on firebase but it gives me an error

The method 'existsSync' was called on null.
Receiver: null
Tried calling: existsSync()
#0 Object.noSuchMethod (dart:core-patch/object_patch.dart:53:5)
#1 StorageReference.putFile (package:firebase_storage/src/storage_reference.dart:62:17)
#2 _UploadState.uploadImage (package:focal/pages/upload.dart:402:10)
#3 _UploadState.handleVideoSubmit (package:focal/pages/upload.dart:507:30)
<asynchronous suspension>
#4 _UploadState.buildUploadForm.<anonymous closure> (package:focal/pages/upload.dart:613:27)
#5 _InkResponseState._handleTap (package:flutter/src/material/ink_well.dart:779:19)
#6 _InkResponseState.build.<anonymous closure> (package:flutter/src/material/ink_well.dart:862:36)
#7 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:182:24)
#8 TapGestureRecognizer.handleTapUp (package:flutter/src/gestures/tap.dart:504:11)
#9 BaseTapGestureRecognizer._chec<…>
What does this error means
I was just uploading a video file and it gives me this error.
This is my code below for uploading video
final StorageReference storageRef = FirebaseStorage.instance.ref();
Future<String> uploadVideo(videoFile, String postId) async {
StorageUploadTask uploadTask = storageRef
.child(
'post_/$postId/${widget.currentuser.username}',
)
.putFile(videoFile);
String thumbnail = 'post_/$postId/${widget.currentuser.username}';
setState(() {
thumbnailLocation = thumbnail;
});
StorageTaskSnapshot storageSnap = await uploadTask.onComplete;
String downloadUrl = await storageSnap.ref.getDownloadURL();
return downloadUrl;
}
Check that you have initialized storageRef

type 'String' is not a subtype of type 'File'

So far in my app everything is working except that one error I keep getting:
type 'String' is not a subtype of type 'File'
I tried many ways to try and fix the Issue but nothing has yet been resolved.
I can understand where the issue is, but I'm unable to fix it with countless attempts.
The problem is that im passing an Image using ImagePicker gallery im passing that image data to firebase as image: image.toString() and it works fine. Firebase takes the path but as an error i get: _file != null since the image is indeed a File image I cant fetch the data from firebase and pass the string path as an argument. therefore getting this error type 'String' is not a subtype of type 'File'. I display the image on the app like the following Image.file(image) Since its the only way to display a File image and use the ImagePicker. Is there a solution for this? or is it a bad way of doing the idea im trying to achieve?
here is the code:
image picker:
String img;
static Future<String> fileToB64(File f) async {
List<int> imageBytes = f.readAsBytesSync();
return base64Encode(
imageBytes,
);
}
Future<void> _takePicture() async {
final imageFile = await ImagePicker.pickImage(
source: ImageSource.gallery,
);
setState(() {
data.image = imageFile;
});
fileToB64(imageFile).then((d) {
setState(() {
img = d; //base64Decode(d);
});
});
}
the provider:
import: 'dart:io';
class AddCar {
// other data
File image;
AddCar({
this.// other data
this.image,
});
}
firebase data:
Future<void> fetchAndSetCars() async {
const url = 'https://mylink.firebaseio.com/cars.json';
try {
final response = await http.get(url);
final extractedData = json.decode(response.body) as Map<String, dynamic>;
final List<AddCar> loadedCars = [];
extractedData.forEach((carId, carData) {
loadedCars.add(AddCar(
// other data
image: carData['image'],
));
});
_cars = loadedCars;
notifyListeners();
} catch (error) {
throw (error);
}
}
AddCar findById(String id) {
return _cars.firstWhere((carProd) => carProd.id == id);
}
void addCar(AddCar car) {
const url = 'https://mylink.firebaseio.com/cars.json';
http.post(
url,
body: json.encode({
// other data
'image': car.image.toString(),
}),
);
final newCar = AddCar(
// other data
image: car.image,
);
_cars.insert(0, newCar); // add car at the top of the list
notifyListeners();
}
how im displaying the fetch data from firebase:
#override
void initState() {
Future.delayed(Duration.zero).then((_) {
Provider.of<Cars>(context).fetchAndSetCars();
});
super.initState();
}
how im calling the data to be displayed in the app:
Container(
width: MediaQuery.of(context).size.width * 0.35,
height: MediaQuery.of(context).size.width * 0.35,
child: GestureDetector(
child: Image.file(
image,
fit: BoxFit.fill,
),
onTap: () {
Navigator.of(context).pushNamed(
MyCarDetails.routeName,
arguments: id,
);
},
),
),
What I get when I run the app:
Restarted application in 6,085ms.
E/flutter ( 3497): [ERROR:flutter/lib/ui/ui_dart_state.cc(148)] Unhandled Exception: type 'String' is not a subtype of type 'File'
E/flutter ( 3497): #0 Cars.fetchAndSetCars
package:flutter_app/providers/car_provider.dart:54
E/flutter ( 3497): <asynchronous suspension>
E/flutter ( 3497): #1 _CarAreaState.initState.<anonymous closure>
package:flutter_app/home_parts/cars_area.dart:28
E/flutter ( 3497): #2 _rootRunUnary (dart:async/zone.dart:1132:38)
E/flutter ( 3497): #3 _CustomZone.runUnary (dart:async/zone.dart:1029:19)
E/flutter ( 3497): #4 _FutureListener.handleValue (dart:async/future_impl.dart:137:18)
E/flutter ( 3497): #5 Future._propagateToListeners.handleValueCallback (dart:async/future_impl.dart:678:45)
E/flutter ( 3497): #6 Future._propagateToListeners (dart:async/future_impl.dart:707:32)
E/flutter ( 3497): #7 Future._complete (dart:async/future_impl.dart:512:7)
E/flutter ( 3497): #9 _rootRun (dart:async/zone.dart:1120:38)
E/flutter ( 3497): #10 _CustomZone.run (dart:async/zone.dart:1021:19)
E/flutter ( 3497): #11 _CustomZone.runGuarded (dart:async/zone.dart:923:7)
E/flutter ( 3497): #12 _CustomZone.bindCallbackGuarded.<anonymous closure> (dart:async/zone.dart:963:23)
E/flutter ( 3497): #13 _rootRun (dart:async/zone.dart:1124:13)
E/flutter ( 3497): #14 _CustomZone.run (dart:async/zone.dart:1021:19)
E/flutter ( 3497): #15 _CustomZone.bindCallback.<anonymous closure> (dart:async/zone.dart:947:23)
E/flutter ( 3497): #16 Timer._createTimer.<anonymous closure> (dart:async-patch/timer_patch.dart:21:15)
E/flutter ( 3497): #17 _Timer._runTimers (dart:isolate-patch/timer_impl.dart:382:19)
E/flutter ( 3497): #18 _Timer._handleMessage (dart:isolate-patch/timer_impl.dart:416:5)
E/flutter ( 3497): #19 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:172:12)
It seems that instead of doing this:
image: carData['image']
You should do this:
image: File(carData['image'])
Because carData['image'] is a String, not a File, and AddCar expects a File.
sometimes it's because of using ! null safety you should run like flutter run --no-sound-null-safety

Trouble with Dart Futures in Flutter : Failed assertion: line 146: '<optimized out>': is not true

I'm building a user authentication module for my app and I am running into trouble with some asynchronous code.
Firstly, here is the error that is thrown:
E/flutter (17162): [ERROR:flutter/shell/common/shell.cc(188)] Dart Error: Unhandled exception:
E/flutter (17162): 'dart:async/future_impl.dart': Failed assertion: line 146: 'optimized out': is not true.
E/flutter (17162): #0 _AssertionError._doThrowNew (dart:core/runtime/liberrors_patch.dart:40:39)
E/flutter (17162): #1 _AssertionError._throwNew (dart:core/runtime/liberrors_patch.dart:36:5)
E/flutter (17162): #2 _FutureListener.handleError (dart:async/future_impl.dart:146:14)
E/flutter (17162): #3 Future._propagateToListeners.handleError (dart:async/future_impl.dart:654:47)
E/flutter (17162): #4 Future._propagateToListeners (dart:async/future_impl.dart:675:24)
E/flutter (17162): #5 Future._completeError (dart:async/future_impl.dart:494:5)
E/flutter (17162): #6 _SyncCompleter._completeError (dart:async/future_impl.dart:55:12)
E/flutter (17162): #7 _Completer.completeError (dart:async/future_impl.dart:27:5)
E/flutter (17162): #8 _AsyncAwaitCompleter.completeError (dart:async/runtime/libasync_patch.dart:40:18)
E/flutter (17162): #9 FirebaseAuth.signInWithEmailAndPassword (package:firebase_auth/firebase_auth.dart)
E/flutter (17162):
E/flutter (17162): #10 Session.login. (package:mood_map/utilities/session.dart:31:24)
E/flutter (17162): #11 _RootZone.runUnary (dart:async/zone.dart:1379:54)
E/flutter (17162): #12 _FutureListener.handleValue (dart:async/future_impl.dart:129:18)
E/flutter (17162): #13 Future._propagateToListeners.handleValueCallback (dart:async/future_impl.dart:642:45)
E/flutter (17162): #14 Future._propagateToListeners (dart:async/future_impl.dart:671:32)
E/flutter (17162): #15 Future._complete (dart:async/future_impl.dart:476:7)
E/flutter (17162): #16 _SyncCompleter.complete (dart:async/future_impl.dart:51:12)
E/flutter (17162): #17 _AsyncAwaitCompleter.complete (dart:async/runtime/libasync_patch.dart:28:18)
E/flutter (17162): #18 _completeOnAsyncReturn (dart:async/runtime/libasync_patch.dart:295:13)
E/flutter (17162): #19 Session._checkUserAlreadyExists (package:mood_map/utilities/session.dart)
E/flutter (17162):
E/flutter (17162): #20 Session.login (package:mood_map/utilities/session.dart:27:11)
And here are the functions that are involved:
static final FirebaseAuth _authenticator = FirebaseAuth.instance;
static void login(BuildContext context, String email, String password) async {
email = email.trim();
password = password.trim();
//Check if the user already exists
await _checkUserAlreadyExists(email).then((exists) {
if(exists) {
_authenticator.signInWithEmailAndPassword(email: email, password: password)
.then((FirebaseUser user) { _loginSuccess(); })
.catchError((Error e) { _loginFailure(context); });
} else {
Utilities.showMessageDialog(context, "That user doesn't exist. Please create an account below.");
}
});
}
----------------------------------------------------------------------
static Future createUserAccount(BuildContext context, email, String password) async {
//Check if the user already exists
await _checkUserAlreadyExists(email).then((exists) {
if(exists) {
Utilities.showMessageDialog(context, "That user already exists. Please login or select another account.");
AppNavigator.navigateToLoginScreen();
} else {
_authenticator.createUserWithEmailAndPassword(email: email, password: password)
.then((FirebaseUser user) { _createUserSuccess(); })
.catchError((Error e) { _createUserFailure(context); });
}
});
}
In short, the call to _authenticator.signonWithEmailAndPassword() is failing. I know that the _authenticator instance is working with other functions so I know it isnt a problem with Firebase itself.
I am worried that I am doing something incorrectly by calling another asynchronous function, _authenticator.signonWithEmailAndPassword() from within another asynchronous function, _checkIfUserAlreadyExists(). It seems that this should be okay to do from within a .then() block from what I've read but the error message seems pretty insistent that it is something to do with the setup of the asynchronous nature of the function calls.
Thoughts?
If you use .then() clauses don't use await.
.then() and await are two different ways to handle Future's but shouldn't be used for the same Future instance.
Consider using async - await to catch the errors in the 'final step'. This answer https://github.com/flutter/flutter/issues/22734 helped me a lot.
Below is a code snippet I got from a source I can't remember but it helped me understand how to properly work with Futures. I modified it a little bit to test for my exact situation (added main4() and divideFullAsyncNested() functions). I hope it helps.
// SO 29378453
import 'dart:async';
import 'package:login_app/constants.dart';
main() {
// fails
main1();
main2();
main3();
main4();
}
Future<double> divide(int a, b) {
// this part is still sync
if (b == 0) {
throw new Exception('Division by zero divide non-async');
}
// here starts the async part
return new Future.value(a / b);
}
Future<double> divideFullAsync(int a, b) {
return new Future(() {
if (b == 0) {
throw new Exception('Division by zero full async');
}
return new Future.value(a / b);
// or just
// return a / b;
});
}
Future<double> divideFullAsyncNested() {
return divideFullAsync(7, 8).then(
(val) {
return divideFullAsync(5, 0).then(
(val2) {
return Future(() {
if (val2 == 1) {
throw Exception('Innermost: Result not accepted exception.');
}
return val2;
});
},
).catchError((err) => throw Exception('Inner: $err'));
},
).catchError((err) => throw Exception('Outter: $err'));
}
//Future<double> divideFullAsyncNested() {
// return divideFullAsync(9, 9).then(
// (val) {
// return Future(
// () {
// if (val == 1) {
// throw Exception('Result not accepted exception.');
// }
// return val;
// },
// );
// },
// ).catchError((err) => throw Exception(err.toString()));
//}
// async error handling doesn't catch sync exceptions
void main1() async {
try {
// divide(1, 0).then((result) => print('(1) 1 / 0 = $result')).catchError(
// (error) => print('(1)Error occured during division: $error'));
var result = await divide(1, 0);
print('(1) 1 / 0 = $result');
} catch (ex) {
print('(1.1)Error occured during division: ${ex.toString()}');
}
}
// async error handling catches async exceptions
void main2() {
divideFullAsync(1, 0)
.then((result) => print('(2) 1 / 0 = $result'))
.catchError(
(error) => print('(2) Error occured during division: $error'));
}
// async/await allows to use try/catch for async exceptions
main3() async {
try {
await divideFullAsync(1, 0);
print('3');
} catch (error) {
print('(3) Error occured during division: $error');
}
}
main4() async {
// try {
// await divideFullAsyncNested();
// } on Exception catch (e) {
// print("(4) ${e.toString().replaceAll('Exception:', '').trimLeft().trimRight()}");
// }
try {
divideFullAsyncNested()
.then((v) => print(v))
.catchError((err) => print(Constants.refinedExceptionMessage(err)));
} on Exception catch (e) {
print("(4) ${Constants.refinedExceptionMessage(e)}");
}
}

Resources