How to insert POJO Objects and Lists in SQFlite - sqlite

I am trying to store data from an API to sqflite , but I have to insert the Objects and the Lists, however during the process, am getting this error :
ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: DatabaseException(java.lang.String cannot be cast to java.lang.Integer) sql 'INSERT INTO articleTable (id, created_on, title, summary, details, tags, featured_image, author, category) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)' args [21, 2020-04-14 04:04:57, Singer Jose ...}]
This is the Model Article class :
#JsonSerializable(explicitToJson: true)
class Article {
Article(this.id, this.created_on, this.title, this.details,
this.featured_image, this.author, this.category, this.summary, this.tags);
int id;
String created_on;
String title;
String summary;
String details;
List<String> tags;
String featured_image;
Author author;
Category category;
factory Article.fromJson(Map<String, dynamic> json) =>
_$ArticleFromJson(json);
Map<String, dynamic> toJson() => _$ArticleToJson(this);
}
The automated generated file for Article model class :
/ GENERATED CODE - DO NOT MODIFY BY HAND
part of 'article.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Article _$ArticleFromJson(Map<String, dynamic> json) {
return Article(
json['id'] as int,
json['created_on'] as String,
json['title'] as String,
json['details'] as String,
json['featured_image'] as String,
json['author'] == null
? null
: Author.fromJson(json['author'] as Map<String, dynamic>),
json['category'] == null
? null
: Category.fromJson(json['category'] as Map<String, dynamic>),
json['summary'] as String,
(json['tags'] as List)?.map((e) => e as String)?.toList(),
);
}
Map<String, dynamic> _$ArticleToJson(Article instance) => <String, dynamic>{
'id': instance.id,
'created_on': instance.created_on,
'title': instance.title,
'summary': instance.summary,
'details': instance.details,
'tags': instance.tags,
'featured_image': instance.featured_image,
'author': instance.author?.toJson(),
'category': instance.category?.toJson(),
};
This is where am doing my Sqflite insertion :
class ArticleApiProvider {
Future<List<Article>> fetchArticles() async {
final response =
await http.get('https://api.xyxyxyxy');
if (response.statusCode == 200) {
List jsonResponse = jsonDecode(response.body);
return jsonResponse.map((article) {
articleBloc.addArticles(Article.fromJson(article));
}).toList();
} else {
throw Exception('Failed to retrieve articles');
}
}
}
My Bloc Class :
class ArticleBloc {
final _repository = Repository();
final _articleFetcher = PublishSubject<List<Article>>();
Stream<List<Article>> get allArticles => _articleFetcher.stream;
fetchArticles() async {
List<Article> articles = await _repository.fetchArticles();
_articleFetcher.sink.add(articles);
}
getArticlesCached() async {
// sink is a way of adding data reactively to the stream
// by registering a new event
_articleFetcher.sink.add(await _repository.getAllArticles());
}
void addArticles(Article article) async {
await _repository.insertArticle(article);
}
dispose() {
_articleFetcher.close();
}
}
final articleBloc = ArticleBloc();
This is the DatabaseProvider class
final articlesTable = "articleTable";
class DatabaseProvider {
static final DatabaseProvider dbProvider = DatabaseProvider();
Database _database;
String id = "id";
String createdOn = "created_on";
String title = "title";
String summary = "summary";
String details = "details";
String featuredImage = "featured_image";
String author = "author";
String category = "category";
String tags = "tags";
Future<Database> get database async {
if (_database != null) return _database;
_database = await createDatabase();
return _database;
}
createDatabase() async {
Directory documentsDirectory = await getApplicationDocumentsDirectory();
// NewVision.db is our database instance name
String path = join(documentsDirectory.path, "NewVision.db");
var database = await openDatabase(path,
version: 1, onCreate: initDB, onUpgrade: onUpgrade);
return database;
}
void initDB(Database db, int version) async {
await db.execute(
'CREATE TABLE $articlesTable($id INTEGER PRIMARY KEY AUTOINCREMENT, $createdOn TEXT, $title TEXT ,'
' $summary TEXT , $details TEXT , '
'$tags TEXT, $featuredImage TEXT , $author TEXT , $category TEXT )');
}
void onUpgrade(Database db, int oldVersion, int newVersion) {
if (newVersion > oldVersion) {}
}
}
String join(String path, String s) {
return path + s;
}
The rest of the files do follow the Bloc architecture, for the purposes of making the question precise, I think form what I have provided, I can get some help.
I will be grateful for your help.

Thanks for the detailed report. My assumption is that the error comes from your tags property. List of String is not a supported type in SQLite. sqflite supports List of int for compatibility reason (for blobs but Uint8List will likely be the only supported type in the future) so the casting error could come from this.
You should try to encode your tags (json or comma separated string) before doing another investigation.
This issue also applies to the author and category fields.
You have to flatten your model. See the supported types help section: https://github.com/tekartik/sqflite/blob/master/sqflite/doc/supported_types.md
Basically int, double, String and Uint8List(blob) are the only types supported. Unfortunately you have to convert your inner List and Map, json being one solution.
But i agree the error reported does not help in finding the issue, that could definitely be improved...

Related

Unhandled Exception: type 'List<dynamic>' is not a subtype of type 'List<imageObject>?' | How to input Array of Objects in Model firebase in flutter

I have been trying to actually get the data from firebase
So the actual data is :
How to get the imageUrl ?
My Try is :
class ProductModel {
String? name;
String? price;
String? discountPrice;
String? discountRate;
String? category;
String? description;
List<imageObject>? image;
ProductModel(
{required this.name,
required this.price,
this.category,
this.description,
this.discountPrice,
this.discountRate,
this.image});
ProductModel.fromMap(Map<String, dynamic> data) {
name = data['name'];
// image = data['imageUrls'][0]['url']; // To get single image i do this
image = data['imageUrls']; // but this is not working
category = data['category'];
description = data['Description'];
price = data['price'];
discountPrice = data['discountPrice'];
discountRate = data['discountRate'];
}
}
class imageObject {
final String public_id;
final String url;
imageObject({
required this.public_id,
required this.url,
});
}
It gives exception :Unhandled Exception: type 'List<dynamic>' is not a subtype of type 'List<imageObject>?'
And to access the first image i am doing,
product.image![0].url,
where product is of type ProductModel
But this is not working
You need to deserialize the imageObject json data as well. For that you need to add the factory imageObject.fromJson() constructor to your imageObject class just like you did for the ProductModel class.
Here's the code you need:
class ProductModel {
String? name;
String? price;
String? discountPrice;
String? discountRate;
String? category;
String? description;
List<imageObject>? image;
ProductModel({
required this.name,
required this.price,
this.category,
this.description,
this.discountPrice,
this.discountRate,
this.image,
});
factory ProductModel.fromJson(Map<String, dynamic> jsonData) => ProductModel(
name: jsonData['name'] as String?,
price: jsonData['price'] as String?,
category: jsonData['category'] as String?,
description: jsonData['Description'] as String?,
discountPrice: jsonData['discountPrice'] as String?,
discountRate: jsonData['discountRate'] as String?,
//Have a good look here to understand how nested list of maps are deserialized
image: (jsonData['imageUrls'] as List<dynamic>?)
?.map((e) => imageObject.fromJson(e as Map<String, dynamic>))
.toList(),
);
}
class imageObject {
final String public_id;
final String url;
imageObject({
required this.public_id,
required this.url,
});
factory imageObject.fromJson(Map<String, dynamic> jsonData) => imageObject(
public_id: jsonData['public_id'] as String,
url: jsonData['url'] as String,
);
}
What we did here is, take the data from imageUrls key as a List and map every individual element thru the json constructor of the imageObject method.
You have to map the recived list to an imageObjects list.
Create a fromMap constructor for your imageObject class
class imageObject {
final String public_id;
final String url;
imageObject({
required this.public_id,
required this.url,
});
imageObject.fromMap(Map<String, dynamic> map) => imageObject(public_id = map['public_id'], url = map['url'] );
}
Use it something like the following:
ProductModel.fromMap(Map<String, dynamic> data) {
name = data['name'];
image = data['imageUrls'].map((map) => imageObject.fromMap(map) );
category = data['category'];
description = data['Description'];
price = data['price'];
discountPrice = data['discountPrice'];
discountRate = data['discountRate'];
}
You may need to do add some casting or do some modifications to the previous code make it work, but this is the general idea.

How to pull snapshot key & values into list through Map<>?

I've been following the official Firebase tutorial for using a real-time database: https://www.youtube.com/watch?v=sXBJZD0fBa4
I am able to pull all the data from the firebase real-time database. However, the method below to do so, provides a list of the data, with no reference to the parent keys (snapshot.key). An ideal scenario would be to have a key property within the Item class (item.key), so I can call upon it directly from the list.
class DatabaseModel {
final itemsRef = FirebaseDatabase.instance.ref().child('/Contents');
Stream<List<Items>> getItemssStream() {
final itemsStream = itemsRef.onValue;
final streamToPublish = itemsStream.map((event) {
final itemsMap = Map<String, dynamic>.from(event.snapshot.value as Map<String, dynamic>);
final itemsList = itemsMap.entries.map((element) {
return Items.fromRTDB(Map<String, dynamic>.from(element.value));
}).toList();
return itemsList;
});
return streamToPublish;
}
}
class Items{
final String item;
final String expiryDate;
final String quantity;
final String user;
Items({required this.item, required this.expiryDate, required this.quantity, required this.user});
//Mapping from real-time database
factory Items.fromRTDB(Map<String, dynamic> data) {
return Items(
item: data['item'],
expiryDate: data['exp'],
quantity: data['qty'],
user: data['user'],
);
}
}
In this code you only use the element.value of each node in your results:
return Items.fromRTDB(Map<String, dynamic>.from(element.value));
If you also want to get the key of each item, you will have to also use element.key in there and pass that to your Items object.
Something like this:
Items.fromRTDB(element.key, Map<String, dynamic>.from(element.value));
...
class Items{
final String key;
final String item;
final String expiryDate;
final String quantity;
final String user;
Items({required this.key, required this.item, required this.expiryDate, required this.quantity, required this.user});
//Mapping from real-time database
factory Items.fromRTDB(String key, Map<String, dynamic> data) {
return Items(
key: key,
item: data['item'],
expiryDate: data['exp'],
quantity: data['qty'],
user: data['user'],
);
}
}

Is there a NoSQL alternative to SQLite for flutter?

I am building a standalone(no API calls to MongoDB or Firebase etc.) flutter app for which database management is crucial. Currently, I am storing it as JSON, but it's simply inefficient. I cannot use SQLite because the data are very nested, in fact, JSON is the only way to store my data. So, I am looking for a NoSQL alternative.
This is the data model I want to store.
lib/book_model.dart
#JsonSerializable()
class Book {
/// Do not change this. It's a primary key.
String bookLink;
String authors = null;
String thumbnail = null;
String bookName = null;
List<Chapter> totalChaptersList = [];
///todo: determine datatype
var currentChapter;
String summary = null;
double rating = 0.0;
List<String> genres = [];
Book({
this.bookLink,
this.authors,
this.thumbnail,
this.bookName,
this.totalChaptersList,
this.currentChapter,
this.summary,
this.rating,
this.genres,
});
Book.generateFromSearchBook(SearchBook searchBook) {
this.authors = searchBook.authors;
this.bookLink = searchBook.bookLink;
this.bookName = searchBook.bookName;
this.thumbnail = searchBook.thumbnail;
}
// #override
// String toString() {
// return "<$bookLink , $authors , $thumbnail , $bookName , $summary , $genres , $rating , $totalChaptersList , $currentChapter>";
// }
factory Book.fromJson(Map<String, dynamic> json) => _$BookFromJson(json);
Map<String, dynamic> toJson() => _$BookToJson(this);
}
#JsonSerializable()
class Chapter {
String name = null;
String date = null;
String chapterLink = null;
#JsonKey(defaultValue: false)
bool has_read = false;
List<Page> pages = [];
Chapter({
this.name,
this.date,
this.chapterLink,
this.has_read,
this.pages,
});
// #override
// String toString() {
// return "<$name , $date , $chapterLink , $has_read , $pages>";
// }
factory Chapter.fromJson(Map<String, dynamic> json) => _$ChapterFromJson(json);
Map<String, dynamic> toJson() => _$ChapterToJson(this);
}
#JsonSerializable()
class Page {
String pageLink = null;
int pageNumber = 0;
Page({
this.pageLink,
this.pageNumber,
});
// #override
// String toString() {
// return "<$pageLink , $pageNumber>";
// }
factory Page.fromJson(Map<String, dynamic> json) => _$PageFromJson(json);
Map<String, dynamic> toJson() => _$PageToJson(this);
}
I found these two
Hive - Hive is a lightweight and blazing fast key-value database written in pure Dart. Inspired by Bitcask.
ObjectBox - ObjectBox is a super-fast database storing Dart objects locally.
I use Hive. It's fast and easy to use.you can use hive_generator with json_serializable and freezed to create a HiveObject class.
example:
#HiveType(typeId: 0)
class Person extends HiveObject {
#HiveField(0)
String name;
#HiveField(1)
int age;
}
You can also use hive_box to turn your hive box into a listenable and listen to database changes.
example:
import 'package:hive/hive.dart';
import 'package:hive_flutter/hive_flutter.dart';
class SettingsPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ValueListenableBuilder(
valueListenable: Hive.box('settings').listenable(),
builder: (context, box, widget) {
return Switch(
value: box.get('darkMode'),
onChanged: (val) {
box.put('darkMode', val);
}
);
},
);
}
}

Storing thumnail to SqlLite from Flutter App

Here i want to store the thumbnail to My database which is sqlite, and i am not able to store it. i am able to store it in local directory but. i wana call it from my data model. please help
Here is my Code.
_getThumb(videoPathUrl) async {
var appDocDir = await getApplicationDocumentsDirectory();
final folderPath = appDocDir.path;
print(folderPath);
String thumb = await Thumbnails.getThumbnail(
thumbnailFolder: folderPath,
videoFile: videoPathUrl,
imageType: ThumbFormat.JPEG,
quality: 25,
);
print('thumbnail: $thumb');
return thumb = model.thumbnail;
}
}
I Have Found a Solution "the Problem is not with the plugin or the method it adopts to store the Image" The problem is that it is not clear that how should a Unit8List is to be stored in the database.
In My case. __
I am Using this code to Store the Image.
_getThumb(videoPathUrl) async {
final thumb = await VideoThumbnail.thumbnailData(
video: videoPathUrl,
imageFormat: ImageFormat.JPEG,
maxWidth: 100,
maxHeight: 100,
quality: 75,
);
}
Then I am Using this final thumb to be saved against my VideoModel Variable of thumbnail
_getThumb(videoPathUrl) async {
final thumb = await VideoThumbnail.thumbnailData(
video: videoPathUrl,
imageFormat: ImageFormat.JPEG,
maxWidth: 100,
maxHeight: 100,
quality: 75,
);
print('thumbnail $thumb'); //Add these lines
model.thumbnail = thumb; //Add these Lines
return thumb; //Add these lines
}
And Once i have saved it i can see the print result in my debug console. Once i have that i am sure that it is saved.
Now i have to save it as a Blob in my db not as a String.
and thumbnail should be a Unit8List variable not a String Variable.
See the example
and in database
I am Also adding the full Video Model and DbHelper files for Others to use.
VideoModel.dart
import 'dart:typed_data';
import 'model.dart';
class VideoModel extends Model {
static String table = 'videos';
int id;
String videoName;
String video;
Uint8List thumbnail;
VideoModel({
this.id,
this.videoName,
this.video,
this.thumbnail,
});
static VideoModel fromMap(Map<String, dynamic> map) {
return VideoModel(
id: map["id"],
videoName: map['videoName'].toString(),
video: map['video'],
thumbnail: map['thumbnail'],
);
}
Map<String, dynamic> toMap() {
Map<String, dynamic> map = {
'id': id,
'videoName': videoName,
'video': video,
'thumbnail': thumbnail,
};
if (id != null) {
map['id'] = id;
}
return map;
}
}
DbHelper.dart
abstract class DB {
static Database _db;
static int get _version => 1;
static Future<void> init() async {
if (_db != null) {
return;
}
try {
var databasesPath = await getDatabasesPath();
String _path = p.join(databasesPath, 'video.db');
_db = await openDatabase(_path, version: _version, onCreate: onCreate);
} catch (ex) {
print(ex);
}
}
static void onCreate(Database db, int version) async {
await db.execute(
'CREATE TABLE videos (id INTEGER PRIMARY KEY AUTOINCREMENT, videoName STRING, video STRING, thumbnail BLOB)');
}
static Future<List<Map<String, dynamic>>> query(String table) async =>
_db.query(table);
static Future<int> insert(String table, Model model) async =>
await _db.insert(table, model.toMap());
static Future<int> update(String table, Model model) async => await _db
.update(table, model.toMap(), where: 'id = ?', whereArgs: [model.id]);
static Future<int> delete(String table, Model model) async =>
await _db.delete(table, where: 'id = ?', whereArgs: [model.id]);
static Future<Batch> batch() async => _db.batch();
}
Now when you have done these edits You can just all your image using
Image.memory(add your Image Here);
see example:
I hope that this will help some of you if you need help Let me know. Thanks

how to display a custom list data on the app page in flutter

1[https://i.stack.imgur.com/yI2Cp.png">]1
i have tried this code to retrieve data from subcollection of the same name(userProducts) stored in many firebase documents.How do i display this data in a container on the app page ? I tried using listiew.builder but it doesnt work.
The first function
static List<products> finalProductsList = [] ;
productsList() async
{
List list_of_products = await Firestore.instance.collection("products")
.getDocuments()
.then((val) => val.documents);
for (int i=0; i<list_of_products.length; i++)
{
Firestore.instance.collection("products").document(
list_of_products[i].documentID.toString()).collection("userProducts").snapshots().listen(CreateListofProducts);
}
}
Second function
CreateListofProducts(QuerySnapshot snapshot)async
{
var docs = snapshot.documents;
for (var Doc in docs)
{
finalProductsList.add(products.fromFireStore(Doc));
}
}
CourseModel
class products {
final String prodId;
final String ownerId;
final String username;
final String price;
final String productname;
final String details;
final String color;
final String composition;
final String washandcare;
final String sizeandfit;
final String shopmediaUrl;
final String id;
final dynamic likes;
products({ this.prodId,
this.ownerId,
this.username,
this.price,
this.details,
this.productname,
this.color,
this.composition,
this.washandcare,
this.sizeandfit,
this.shopmediaUrl,
this.id,
this.likes,});
factory products.fromFireStore(DocumentSnapshot doc)
{
Map data = doc.data ;
return products(
prodId: doc['prodId'],
ownerId: doc['ownerId'],
username: doc['username'],
price: doc['price'],
productname: doc['productname'],
details: doc['details'],
shopmediaUrl: doc['shopmediaUrl'],
color:doc['color'],
composition:doc['composition'],
washandcare:doc['washandcare'],
sizeandfit:doc['sizeandfit'],
likes: doc['likes'],
);
SO if i understand you, you want to get data on multiple collections on multiple documents?
If so... you should use collecionGroup, you can learn how to use here, make sure to adjust your firestore rules.

Resources