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

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.

Related

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'],
);
}
}

The argumnet type 'Null Funcion(DataSnapshot)' cnt be assigned to the parameter type 'Future Or <dynamic> Function(DataBaseEvent)'

I have this function that is giving me an error.
the getCurrentOnLineUserInfo function is trying to get read data from the Firebase Database of the current user that is logged in.
The argument type 'Null Funcion(DataSnapshot)' can't be assigned to the parameter of type 'Future Or Function(DataBaseEvent)'
I am following a year-old tutorial, so the issue might be the code is old. I might need new syntax or something.
static void getCurrentOnLineUserInfo() async {
firebaseUser = await FirebaseAuth.instance.currentUser;
String userId = firebaseUser!.uid;
DatabaseReference reference =
FirebaseDatabase.instance.ref().child("user").child(userId);
print("getCurrentOnLineUser info executed!");
print('${firebaseUser!.email}${firebaseUser!.displayName}');
// errors below this
reference.once().then((DataSnapshot dataSnapshot) {
if (dataSnapShot!.value != null) {
userCurrentInfo = Users.fromSnapshot(dataSnapshot);
}
});
}
}
and here is my class that is assigning data. This class is giving no errors
class Users {
String? id;
String? email;
String? phone;
String? name;
Users({this.id, this.email, this.phone, this.name});
Users.fromSnapshot(DataSnapshot dataSnapshot) {
id = dataSnapshot.key!;
var data = dataSnapshot.value as Map?;
if (data != null) {
email = data?["email"];
name = data?["name"];
phone = data?["phone"];
}
}
}
The once method returns a DatabaseEvent, not a DataSnapshot. DatabaseEvent is a class that encapsulates a DataSnapshot AND a DatabaseEventType, to extract the snapshot, you must use DatabaseEvent.snapshot:
reference.once().then((event) {
final dataSnapshot = event.snapshot;
if (dataSnapShot!.value != null) {
userCurrentInfo = Users.fromSnapshot(dataSnapshot);
}
});
Here is another solution I think might do what you want:
// async methods should return a future
static Future<void> getCurrentOnLineUserInfo() async {
firebaseUser = await FirebaseAuth.instance.currentUser;
String userId = firebaseUser!.uid;
DatabaseReference reference =
FirebaseDatabase.instance.ref().child("user").child(userId);
final snapshot = await reference.get(); // you should use await on async methods
if (snapshot!.value != null) {
userCurrentInfo = Users.fromSnapshot(snapshot);
}
}
}
I was following the same old tutorial you mentioned, the #mobdev991 answer is correct and i think the reason why you don't receive data is the class where you are assigning data try this
class Users {
String? id;
String? email;
String? name;
String? phone;
Users({this.id, this.email, this.name, this.phone});
Users.fromSnapshot(DataSnapshot dataSnapshot) {
id = dataSnapshot.key;
email = (dataSnapshot.child("email").value.toString());
name = (dataSnapshot.child("name").value.toString());
phone = (dataSnapshot.child("phone").value.toString());
}
}

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 insert POJO Objects and Lists in SQFlite

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

How to convert a sub collection to list with flutter and firestore?

I have an object Dish who containes a list of ingredients and I want to get them. How can I do?
In Firebase, Dish is a Document and Ingredient is a sub collection. I tried this but it doesn't work.
class Dish{
String name;
DocumentReference reference;
List<Ingredient> ingredients;
Dish.fromMap(Map<String, dynamic> map, {this.reference}){
this.name = map['name'];
this
.reference
.collection("ingredients")
.snapshots()
.listen((QuerySnapshot snap) {
final List<DocumentSnapshot> ingredientsDocuments = snap.documents;
List<Ingredient> ing = [];
for (var i = 0; i < ingredientsDocuments.length; i++) {
ing.add(Ingredient.fromSnapshot(ingredientsDocuments[i]));
}
this.ingredients = ing;
});
}
Dish.fromSnapshot(DocumentSnapshot snapshot)
: this.fromMap(snapshot.data, reference: snapshot.reference);
#override
String toString() => "Dish<$String>";
}
class Ingredient{
final String name;
final DocumentReference reference;
Ingredient.fromMap(Map<String, dynamic> map, {this.reference})
: assert(map['name'] != null),
name = map['name'];
Ingredient.fromSnapshot(DocumentSnapshot snapshot)
: this.fromMap(snapshot.data, reference: snapshot.reference);
#override
String toString() => "Ingredient<$String>";
}
How are you trying to fetch data from Firestore using Dish class? Were you using any asynchronous task(i.e. Future)? What's not working in your implementation? Any errors that you received?
Since I'm unable to run the repro you've provided, here's a sample code that you can try.
List<Ingredient> ingredients;
// call getDocuments() to fetch data from Firestore and add it to the List
Future<void> getDocuments() async {
ingredients = List();
var collection = FirebaseFirestore.instance
.collection('ingredients');
collection.get().then((value) {
value.docs.forEach((element) {
setState(() {
// add the object to the List
ingredients.add(Ingredient(Ingredient.fromMap(element.data())));
});
});
});
}
As for the Object, it can be as simple as this. No need to pass DocumentReference since we'll only be using it to map the data to the Object and be able to add it in the List.
class Ingredients {
var name;
Ingredients(Ingredients document) {
this.documentName = document.getName();
}
dynamic getName() => name;
Ingredients.fromMap(Map<dynamic, dynamic> document)
: name = document['name'];
}
You can check a working sample I've posted in here. It has pagination added, but it should have a similar approach with the code snippets I've shared here.

Resources