Firebase - i got empty widget and my circularprogress always running - flutter

help me to show my amount around my widget
i got the data when i print(nominal.data()) but i cant get any data from result when i print it
this is my code
myService
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:limbong/model/amount_model.dart';
class AmountService {
FirebaseFirestore firebaseFirestore = FirebaseFirestore.instance;
Stream\<List\<AmountModel\>\> getAmount() {
try {
return firebaseFirestore
.collection('amount')
.orderBy('createdAt', descending: true)
.limit(1)
.snapshots()
.map((QuerySnapshot nominal) {
var result = nominal.docs.map\<AmountModel\>((DocumentSnapshot nominal) {
print(nominal.data()); // i got my data here
return AmountModel.fromJson(nominal.data() as Map\<String, dynamic\>);
}).toList();
print(result); but i havent see the data here
return result;
});
} catch (e) {
throw Exception(e);
}
}
} `
on this model i think i havent any problem yet but i wanna some advice to make it better
MyModel
class AmountModel {
late DateTime createdAt;
late double currentBalance;
late double executableBalance;
AmountModel(
{required this.createdAt,
required this.currentBalance,
required this.executableBalance});
AmountModel.fromJson(Map\<String, dynamic\> json) {
createdAt = DateTime.parse(json\['createdAt'\]);
currentBalance = json\['current_balance'\];
executableBalance = json\['executable_balance'\];
}
Map\<String, dynamic\> toJson() {
return {
'createdAt': createdAt.toString(),
'current_balance': currentBalance,
'executable_balance': executableBalance,
};
}
}
when call the service, i got nothing and the circularprogress always run
MyWidget
Widget balanceWidget() {
return StreamBuilder\<List\<AmountModel\>\>(
stream: AmountService().getAmount(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return BalanceWidget(
amount: snapshot.data!\[snapshot.data!.length - 1\]);
}
return const Center(child: CircularProgressIndicator());
});
}`
help me
i want to show the the nominal.data instead blank result or maybe i got the real result
the real result to make my firebase database show in my widget

Related

How to solve value of type 'Map<String, dynamic>', but got one of type 'List<dynamic>'

I want to get an image from an api and I get the error mentioned in the title.
class _ApiState extends State<Api> {
Future<CatData> fetchcat() async {
final response =
await http.get(Uri.parse('https://api.thecatapi.com/v1/images/search'));
// Appropriate action depending upon the
// server response
if (response.statusCode == 200) {
return CatData.fromJson(json.decode(response.body));
//return CatData.fromJson(jsonDecode(response.body) as Map<String, dynamic>);
} else {
throw Exception('Failed to load album');
}
}
late Future<CatData> futureAlbum;
#override
void initState() {
super.initState();
futureAlbum = fetchcat();
}
#override
Widget build(BuildContext context) {
return FutureBuilder<CatData>(
future: fetchcat(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Image.network(snapshot.data!.imagen);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
return CircularProgressIndicator();
},
);
}
}
here the class model:
class CatData {
String imagen;
CatData({required this.imagen});
factory CatData.fromJson(Map<String, dynamic> json) {
return CatData(
imagen: json['url'],
);
}
}
If I get an answer please, I would like you to explain to me the reason for the problem. because I always get this kind of errors when I consume API's.
"receives one value but expects another"
https://api.thecatapi.com/v1/images/search
Well, json.decode(response.body) gives you back a List<dynamic>, but you declared the method fromJson to accept one argument of type Map<String, dynamic>, thus the incompatibility.
You can change the signature of the method fromJson and set it to List<dynamic>. Then you could access it with json[0].url, json[0]['url'] or {url} = json[0].
I tested the following code in https://dartpad.dev and works like a charm now.
import 'dart:convert';
import 'package:http/http.dart' as http;
Future<CatData> fetchcat() async {
final response =
await http.get(Uri.parse('https://api.thecatapi.com/v1/images/search'));
// Appropriate action depending upon the
// server response
if (response.statusCode == 200) {
return CatData.fromJson(json.decode(response.body));
//return CatData.fromJson(jsonDecode(response.body) as Map<String, dynamic>);
} else {
throw Exception('Failed to load album');
}
}
class CatData {
String imagen;
CatData({required this.imagen});
factory CatData.fromJson(List<dynamic> json) {
return CatData(
imagen: json[0]['url']
);
}
}
void main() async {
CatData catData = await fetchcat();
print(catData.imagen);
}
You probably making mistake on casting. first make sure what kind of data you are retrieving means is it key-value pair { "url" : "www...." } or List [{"url" :"www...} , { "url": " www..."}]
if its key-value pairs then decode it as follows:
final decoded = json.decode(response.body) as Map<String, dynamic>;
final _catData = CataData.fromJson(decoded);
or if its list of urls then do it as follows:
final _decoded = json.decode(response.body) as List<dynamic>;
final _catsData = _decoded.map((e) => CatData.fromJson(e as Map<String, dynamic>)).toList();

how to retrive value from a firestore flutter where query

I started flutter recently, and I try to retrieve the data from a query I made using 'where' , but the only thing I got back is "Instance of '_JsonQueryDocumentSnapshot'".
I tried different thing , but nothing work or i do it badly
this is my code :
CollectionReference users =
FirebaseFirestore.instance.collection('users');
final documents =
await users.where("username", isEqualTo: "username").get();
documents.docs.forEach((element) {
print(element);
});
I have also tried to use Future but without success :
class finduser extends StatelessWidget {
final String username;
finduser(this.username);
#override
Widget build(BuildContext context) {
CollectionReference users = FirebaseFirestore.instance.collection('users');
return FutureBuilder(
future: users.where('username', isEqualTo: '${username}').get(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasError) {
print("wrong");
return Text("Something went wrong");
}
if (snapshot.hasData && !snapshot.data!.exists) {
print("doesnt exist");
return Text("User does not exist");
}
if (snapshot.connectionState == ConnectionState.done) {
Map<String, dynamic> data = snapshot.data! as Map<String, dynamic>;
print(snapshot.data!);
return Text("${data}");
}
return Text("loading");
},
);
}
}
for the moment, all usernames are just "username"
Thank you for the help
When you get your documents like this :
CollectionReference users =
FirebaseFirestore.instance.collection('users');
final documents =
await users.where("username", isEqualTo: "username").get();
documents.docs.forEach((element) {
print(element);
});
You are trying to print an instance of a QueryDocumentSnapshot
This QueryDocumentSnapshot has a method .data() which returns a Map<String,dynamic> aka JSON.
So in order to print the content of your Document, do this :
documents.docs.forEach((element) {
print(MyClass.fromJson(element.data()));
});
This data by itself will not be very useful so I recommend creating a factory method for your class :
class MyClass {
final String username;
const MyClass({required this.username});
factory MyClass.fromJson(Map<String, dynamic> json) =>
MyClass(username: json['username'] as String);
}
Now you can call MyClass.fromJson(element.data()); and get a new instance of your class this way.
I have searched a lot but i see you have written code right.
The only thing that came to my mind is that you didn't initialize your firebase to your flutter project (you should do it in any flutter project to be able to use flutter).
link of the document:
https://firebase.flutter.dev/docs/overview#initializing-flutterfire
In your first code snippet you are printing element, which are instances of the QueryDocumentSnapshot class. Since you're not accessing specific data of the document snapshot, you get its default toString implementation, which apparently just shows the class name.
A bit more meaningful be to print the document id:
documents.docs.forEach((doc) {
print(doc.id);
});
Or a field from the document, like the username:
documents.docs.forEach((doc) {
print(doc.get("username"));
});
Run this code, it will work.
I also faced this similar problem, so I used this work around.
Map<String, dynamic> data = {};
FirebaseFirestore.instance.collection('users').where("username", isEqualTo: "username").get().then((QuerySnapshot querySnapshot) {
querySnapshot.docs.forEach((value){
data = value.data()!;
print('printing uid ${data['uid']}');
print('printing username--${data['username']}');
print('printing all data--$data');
});
});

Pagination for Flutter ListView.builder [duplicate]

I'm trying to paginate by using Firestore and I read the document and it implement like this in Swift
let first = db.collection("cities")
.order(by: "population")
.limit(to: 25)
first.addSnapshotListener { (snapshot, error) in
guard let snapshot = snapshot else {
print("Error retrieving cities: \(error.debugDescription)")
return
}
guard let lastSnapshot = snapshot.documents.last else {
// The collection is empty.
return
}
// Construct a new query starting after this document,
// retrieving the next 25 cities.
let next = db.collection("cities")
.order(by: "population")
.start(afterDocument: lastSnapshot)
// Use the query for pagination.
// ...
}
Just for practice, I tried fetched three documents and if button tapped, fetch one more document.
Firestore.instance.collection('user').where('name', isEqualTo: 'Tom').orderBy('age').limit(3).getDocuments().then((snapshot) {
_lastDocument = snapshot.documents.last;
snapshot.documents.forEach((snap) {
print(snap.data);
});
});
After button tapped tried like this.
Firestore.instance.collection('user').where('name', isEqualTo: 'Tom').orderBy('age').startAfter(_lastDocument).limit(1).getDocuments().then((snapshot) {
snapshot.documents.forEach((snap) {
print(snap.data);
});
});
But console says this.
The following assertion was thrown while handling a gesture: type
'DocumentSnapshot' is not a subtype of type 'List[dynamic]'
Why do I have to pass list?
Does anyone know how to fix this?
UPDATE
I was able to paginate like so.
class PaginationExample extends StatefulWidget {
#override
_PaginationExampleState createState() => _PaginationExampleState();
}
class _PaginationExampleState extends State<PaginationExample> {
var _restaurants = <Restaurant>[];
var _nomore = false;
var _isFetching = false;
DocumentSnapshot _lastDocument;
ScrollController _controller;
void _fetchDocuments() async {
final QuerySnapshot querySnapshot = await Firestore.instance.collection('restaurants').orderBy('likes').limit(8).getDocuments();
// your logic here
}
Future<Null> _fetchFromLast() async {
final QuerySnapshot querySnapshot = await Firestore.instance.collection('restaurants').orderBy('likes').startAfter([_lastDocument['likes']]).limit(4).getDocuments();
if (querySnapshot.documents.length < 4) {
_nomore = true;
return;
}
_lastDocument = querySnapshot.documents.last;
for (final DocumentSnapshot snapshot in querySnapshot.documents) {
final Restaurant re = Restaurant(snapshot);
_restaurants.add(re);
}
setState(() {});
}
void _scrollListener() async {
if (_nomore) return;
if (_controller.position.pixels == _controller.position.maxScrollExtent && _isFetching == false) {
_isFetching = true;
await _fetchFromLast();
_isFetching = false;
}
}
#override
void initState() {
_fetchDocuments();
_controller = new ScrollController()..addListener(_scrollListener);
super.initState();
}
#override
Widget build(BuildContext context) {
return Container(
);
}
}
There is an error here:
Firestore.instance.collection('user').where('name', isEqualTo: 'Tom').orderBy('age').startAfter(_lastDocument).limit(1).getDocuments().then((snapshot) {
snapshot.documents.forEach((snap) {
print(snap.data);
});
});
startAfter method expects a List value params and you are passing a DocumentSnapshot.
Takes a list of [values], creates and returns a new [Query] that
starts after the provided fields relative to the order of the query.
You could try something like this:
Firestore.instance.collection('user').where('name', isEqualTo: 'Tom').orderBy('age').startAfter([{'name': 'Tom'}]).limit(1).getDocuments().then((snapshot) {
snapshot.documents.forEach((snap) {
print(snap.data);
});
});
Paginate just with 2 attrubutes, itemBuilder and query using this package - paginate_firestore
For example,
PaginateFirestore(
itemBuilder: (context, documentSnapshot) => ListTile(
leading: CircleAvatar(child: Icon(Icons.person)),
title: Text(documentSnapshot.data['name']),
subtitle: Text(documentSnapshot.documentID),
),
// orderBy is compulsary to enable pagination
query: Firestore.instance.collection('users').orderBy('name'),
)
This works for me giving realtime pagination
defining functions to fetch data
import 'package:cloud_firestore/cloud_firestore.dart';
import '../../../core/constants/firebase_constants.dart';
class FirebaseProvider {
final FirebaseFirestore _firestore;
FirebaseProvider({required FirebaseFirestore firestore})
: _firestore = firestore;
CollectionReference get _posts =>
_firestore.collection(FirebaseConstants.postsCollection);
Future<List<DocumentSnapshot>> fetchFirstList(
String fromgst, String postType) async {
return (await _posts
.where("fromgst", isEqualTo: fromgst)
.where("postType", isEqualTo: postType)
.orderBy("date", descending: true)
.limit(5)
.get())
.docs;
}
Future<List<DocumentSnapshot>> fetchNextList(String fromgst, String postType,
List<DocumentSnapshot> documentList) async {
return (await _posts
.where("fromgst", isEqualTo: fromgst)
.where("postType", isEqualTo: postType)
.orderBy("date", descending: true)
.startAfterDocument(documentList[documentList.length - 1])
.limit(5)
.get())
.docs;
}
}
separate class to handle pagination
import 'dart:async';
import 'dart:io';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:growmore/features/home/repository/firebase_provider.dart';
import 'package:rxdart/rxdart.dart';
class PostListBloc {
List<DocumentSnapshot>? documentList;
bool showIndicator = false;
FirebaseProvider? firebaseProvider;
BehaviorSubject<List<DocumentSnapshot>>? postController;
BehaviorSubject<bool>? showIndicatorController;
PostListBloc() {
postController = BehaviorSubject<List<DocumentSnapshot>>();
showIndicatorController = BehaviorSubject<bool>();
firebaseProvider = FirebaseProvider(firestore: FirebaseFirestore.instance);
}
Stream get getShowIndicatorStream => showIndicatorController!.stream;
Stream<List<DocumentSnapshot>> get postStream => postController!.stream;
// This method will automatically fetch first 10 elements from the document list
Future fetchFirstList(String fromgst, String postType) async {
try {
documentList = await firebaseProvider?.fetchFirstList(fromgst, postType);
print("documentList$documentList");
postController?.sink.add(documentList!);
try {
if (documentList!.isEmpty) {
postController?.sink.addError("No Data Available");
}
} catch (e) {
print(e);
}
} on SocketException {
postController?.sink.addError(SocketException("No Internet Connection"));
} catch (e) {
print(e.toString());
postController?.sink.addError(e);
}
}
//This will automatically fetch the next 10 elements from the list
fetchNextPosts(String fromgst, String postType) async {
try {
updateIndicator(true);
List<DocumentSnapshot> newDocumentList = await firebaseProvider!
.fetchNextList(fromgst, postType, documentList!);
print('asca$newDocumentList');
documentList!.addAll(newDocumentList);
postController!.sink.add(documentList!);
try {
if (documentList!.isEmpty) {
postController!.sink.addError("No Data Available");
updateIndicator(false);
}
} catch (e) {
updateIndicator(false);
}
} on SocketException {
postController!.sink.addError(SocketException("No Internet Connection"));
updateIndicator(false);
} catch (e) {
updateIndicator(false);
print(e.toString());
postController!.sink.addError(e);
}
}
//For updating the indicator below every list and paginate*
updateIndicator(bool value) async {
showIndicator = value;
showIndicatorController!.sink.add(value);
}
void dispose() {
postController!.close();
showIndicatorController!.close();
}
}
the ui part
ScrollController controller = ScrollController();
#override
void initState() {
super.initState();
postListBloc = PostListBloc();
print("dvvfe${widget.fromgst}");
postListBloc!.fetchFirstList(widget.fromgst, widget.postType);
controller.addListener(_scrollListener);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: StreamBuilder<List<DocumentSnapshot>>(
stream: postListBloc!.postStream,
builder: (context, snapshot) {
if (snapshot.data != null) {
return ListView.builder(
itemCount: snapshot.data?.length,
shrinkWrap: true,
controller: controller,
itemBuilder: (context, index) {
return Card(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: ListTile(
title: Text(snapshot.data![index]['description']),
),
),
);
},
);
} else {
return const CircularProgressIndicator();
}
},
),
);
}
void _scrollListener() {
if (controller.offset >= controller.position.maxScrollExtent &&
!controller.position.outOfRange) {
print("Cavc$controller");
print("at the end of list");
postListBloc!.fetchNextPosts(widget.fromgst, widget.postType);
}
}
}
I found it not open source github repo

unable to use .toList with Data from MongoDB using flutter

Sorry if it's a stupid question I am beginner in Flutter and MongoDB Here is my code to return collection data btw this is the only time I use Mongo_Dart all other operations done using JS on heroku
class Azkar {
getAzkar() async {
var db = await Db.create(
'mongodb+srv://Adham:<password>#cluster0.nm0lg.mongodb.net/<db>retryWrites=true&w=majority');
await db.open();
print('Connected to database');
DbCollection coll = db.collection('zekrs');
return await coll.find().toList();
}
}
It is working and I am able to print returned data from another class it is List<Map<String, dynamic>> I want to know how should I use it to generate ListTile with all data.
This package is not worth it. I solved this issue by moving out this part of code on the backend side (NodeJS) in the cloud and just getting what I need with an HTTP request.
Instead of returning data in List<Map<String, dynamic>>, create a class for your data. Suppose your data gives us a list of users. Then
class User {
User({
this.id,
this.name,
});
int id;
String name;
}
This would be your Azkar class
class Azkar {
getAzkar() async {
final db = await Db.create(
'mongodb+srv://Adham:<password>#cluster0.nm0lg.mongodb.net/<db>retryWrites=true&w=majority');
await db.open();
print('Connected to database');
final coll = db.collection('zekrs');
final zekrsList = await coll.find().toList();
List<User> users = [];
for (var item in zekrsList) {
final user = User(
id: item['id'],
name: item['name'],
);
users.add(user);
}
return users;
}
}
You should do something like this.
FutureBuilder(
future: getAzkar(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return Container(
margin: EdgeInsets.all(8),
child: Column(
children: [
Text("Name = ${snapshot.data[index].name}"),
Text("Id = ${snapshot.data[index].id}"),
],
),
);
});
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
// By default, show a loading spinner.
return CircularProgressIndicator();
},
),
if anyone still have this issue,
I solved it by setting this:
final zekrsList = await coll.find().toList();
to
final zekrsList = await coll.find(where.sortBy('_id')).toList();

How can I update a DateTime?

I'm developing a realtime chat app with fisebase. The problem is that the time of a sent message does not update and I don't know what else can I do.
Here's some part of my code:
import 'package:flutter/material.dart';
import 'package:heyou/helper/constants.dart';
import 'package:heyou/screens/conversation_screen/send_menu_items.dart';
import 'package:heyou/services/database.dart';
import 'package:intl/intl.dart';
class ConversationScreen extends StatefulWidget {
final String chatScreenId;
ConversationScreen(this.chatScreenId);
#override
_ConversationScreenState createState() => _ConversationScreenState();
}
class _ConversationScreenState extends State<ConversationScreen> {
DateTime _currentDate = new DateTime.now();
DatabaseMethods databaseMethods = new DatabaseMethods();
TextEditingController messageController = new TextEditingController();
Stream chatMessageStream;
Widget chatMessageList() {
return StreamBuilder(
stream: chatMessageStream,
builder: (context, snapshot) {
return snapshot.hasData ? ListView.builder(
padding: EdgeInsets.only(bottom: 70.0),
itemCount: snapshot.data.documents.length,
reverse: true,
itemBuilder: (context, index) {
return MessageTile(
snapshot.data.documents[index].data['message'],
snapshot.data.documents[index].data['sendBy'] == Constants.myName,
snapshot.data.documents[index].data['time'],
snapshot.data.documents[index].data['messageTimeTile'],
);
}
) : Container();
},
);
}
sendMessage() {
if(messageController.text.isNotEmpty) {
Map<String, dynamic> messageMap = {
'message': messageController.text,
'sendBy': Constants.myName,
'time': DateTime.now().toString(),
'messageTimeTile': new DateFormat.Hms().format(_currentDate).toString(),
};
databaseMethods.addConversationMessages(widget.chatScreenId, messageMap);
messageController.text = '';
}
}
#override
void initState(){
databaseMethods.getConversationMessages(widget.chatScreenId).then((value) {
setState(() {
chatMessageStream = value;
});
});
super.initState();
}
}
And here's my database code:
import 'package:cloud_firestore/cloud_firestore.dart';
class DatabaseMethods {
getUserByUsername(String username) async {
return await Firestore.instance.collection('users').where('name', isEqualTo: username).getDocuments();
}
getUserByUserEmail(String userEmail) async {
return await Firestore.instance.collection('users').where('email', isEqualTo: userEmail).getDocuments();
}
uploadUserInfo(userMap) {
Firestore.instance.collection('users').add(userMap);
}
createChatScreen(String chatScreenId, chatScreenMap) {
Firestore.instance.collection('ChatScreen').document(chatScreenId).setData(chatScreenMap).catchError((e){
print(e.toString());
});
}
addConversationMessages(String chatScreenId, messageMap) {
Firestore.instance.collection('ChatScreen').document(chatScreenId).collection('chats').add(messageMap)
.catchError((e){
print(e.toString());
});
}
getConversationMessages(String chatScreenId) async {
return Firestore.instance.collection('ChatScreen').document(chatScreenId).collection('chats')
.orderBy('time', descending: true).snapshots();
}
getHomeScreen(String userName) async {
return Firestore.instance.collection('ChatScreen').where('users', arrayContains: userName).snapshots();
}
}
I'm trying to fix it by myself but I can't, that's why I'm here.
When you create _currentDate, it is never updated. Consequently, it will always be the time at which the State object was created. You can either use Midhun MP's suggestion in the comments, or replacing DateTime _currentDate = new DateTime.now(); with DateTime get _currentDate => DateTime.now(); to always get a copy of the current date, in case you use it in multiple places.