How to access an array of DocumentReference in flutter? - android

I want to access all the reg_events for the currently logged in user. I have the following code right now
stream: Firestore.instance.collection('users').document(email).snapshots(),
builder: (context, snapshot){
if(!snapshot.hasData){
return Text("Loading..");
}
return Center(
child: new Container(
child: new PageView.builder(
onPageChanged: (value) {
setState(() {
currentpage = value;
});
},
controller: controller,
itemCount: snapshot.data['reg_events'].length,
itemBuilder: (context, index) => builder(index, snapshot.data)),
),
);
}
),
The 'builder' is:
builder(int index, DocumentSnapshot document) {
return new AnimatedBuilder(
animation: controller,
builder: (context, child) {
double value = 1.0;
if (controller.position.haveDimensions) {
value = controller.page - index;
value = (1 - (value.abs() * .5)).clamp(0.0, 1.0);
}
return new Center(
child: new SizedBox(
height: Curves.easeOut.transform(value) * 200,
width: Curves.easeOut.transform(value) * 1000,
child: child,
),
);
},
child: new Card(
child: Text(document.data['reg_events'][0].toString(),
textAlign: TextAlign.center,
style: TextStyle(fontSize: 15, color: Colors.white),),
margin: const EdgeInsets.all(10.0),
color: index % 2 == 0 ? Colors.blue : Colors.red,
),
);
}
But it outputs "Instance of 'DocumentReference'". How do I access this Document Reference?

Maybe it not understand reg_event as List so try this,
stream: Firestore.instance.collection('users').document(email).snapshots(),
builder: (context, snapshot){
List regEvent = new List();
if(snapshot.hasData){
regEvent = snapshot.data['reg_events'];
}
if(!snapshot.hasData){
return Text("Loading..");
}
return Center(
child: new Container(
child: new PageView.builder(
onPageChanged: (value) {
setState(() {
currentpage = value;
});
},
controller: controller,
itemCount: regEvent.length,
itemBuilder: (context, index) {
print(regEvent[index]);
return builder(index, snapshot.data)),}
),
);
}
),

DocumentReference is like a pointer to one document. You can get the single document using .get method which returns Future<DocumentSnapshot>. Since you have an array of them, you can then use Streams to get a bunch of Futures.
List<DocumentReference> references = [ref, ref, ref];
var myStream = Stream.fromFutures(references.map((ref) => ref.get()).toList());
StreamBuilder(builder: ..., stream: myStream);
But...
Firestore has querying, so it should be better if you actually use it. You should be able to reference your reg_events like that:
Firestore.instance.collection('users').document("$email/reg_events").snapshots();

In this example a User object is created which contains a list of references of the entities (or events). This list is then passed to the DatabaseService class which returns a list of EntityDetails stream objects.
DatabaseService Class:
final CollectionReference entityCollection =
Firestore.instance.collection('entities');
final CollectionReference userCollection =
Firestore.instance.collection('user');
Stream<UserDetails> get userDetails {
return userCollection
.document(uid)
.snapshots()
.map(_userDetailsFromSnapshot);
}
UserDetails _userDetailsFromSnapshot(DocumentSnapshot snapshot) {
return UserDetails(
email: snapshot.data['email'],
phone: snapshot.data['phone'],
fname: snapshot.data['fname'],
lname: snapshot.data['lname'],
streetNr: snapshot.data['street_nr'],
city: snapshot.data['city'],
entities: List.from(snapshot.data['entities']));
}
List<Stream<EntityDetails>> getEntitiesFromDRList(
List<DocumentReference> entities) {
List<Stream<EntityDetails>> elist = new List();
entities.forEach((element) {
elist.add(element.snapshots().map((_entityDetailsFromSnapshot)));
});
return elist;
}
EntityDetails _entityDetailsFromSnapshot(DocumentSnapshot snapshot) {
return EntityDetails(
uid: uid,
name: snapshot.data['name'],
description: snapshot.data['description'],
type: snapshot.data['type'],
geoPoint: snapshot.data['geopoint'],
adressString: snapshot.data['adress_string'],
email: snapshot.data['email'],
phone: snapshot.data['phone'],
);}
Widget
stream: DatabaseService(uid: uid).userDetails,
builder: (context, snapshot) {
if (snapshot.hasData) {
UserDetails userDetails = snapshot.data;
//Get the Streams
DatabaseService(uid: uid)
.getEntitiesFromDRList(userDetails.entities)
.forEach((element) {
element.listen((data) {
print("DataReceived: " + data.name);
}, onDone: () {
print("Task Done");
}, onError: (error) {
print("Some Error");
});
});
User-Object
class UserDetails {
final String email;
final String phone;
final String fname;
final String lname;
final String streetNr;
final String city;
final List<DocumentReference> entities;
UserDetails(
{this.email,
this.phone,
this.fname,
this.lname,
this.streetNr,
this.city,
this.entities});
}

Related

I'm creating an grocery app in Flutter with firebase but i am unable show the updated cart value to text widget

I'm creating an grocery app in Flutter with firebase but i am unable show the updated cart value to text widget.
This is my List view
var fire_storedb = FirebaseFirestore.instance.collection("vegetables").snapshots();
Container(
margin: const EdgeInsets.only(top: 2),
alignment: Alignment.center,
child: StreamBuilder(
stream: fire_storedb,
builder: ((context, snapshot) {
if (!snapshot.hasData) return const CircularProgressIndicator();
return (ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.vertical,
itemCount: snapshot.data!.docs.length,
itemBuilder: (context, index) {
return (grocery_list(snapshot, index, context,values45));
},
));
})),
),
Below is my grocery_list function which is called from ListView ........
Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
InkWell(
child: Icon(FontAwesomeIcons.minus),
onTap: () async {
String? grocery_id =
snapshot.data?.docs[index].reference.id;
FirebaseFirestore.instance
.collection("Cart")
.where("grocery_id", isEqualTo: grocery_id)
.get()
.then((value) {
value.docs.forEach((element) {
FirebaseFirestore.instance
.collection("Cart")
.doc(element.id)
.delete()
.then((value) {
print("Success!");
});
});
});
}), //Inkwell for delete item from cart
VerticalDivider(width: 10,), ////// Vertical Divider
VerticalDivider(width: 10,), ////// Vertical Divider
InkWell(
child: Icon(FontAwesomeIcons.plus),
onTap: () async {
SharedPreferences sharedPreferences =
await SharedPreferences.getInstance();
var email =
sharedPreferences.getString("email").toString();
String? docid =
snapshot.data?.docs[index].reference.id;
Map<String, String?> data_to_save = {
"grocery_id": docid,
"quantity": "1",
"email": email,
"name": snapshot.data!.docs[index]['name'],
"price": snapshot.data!.docs[index]['price'],
"si": snapshot.data!.docs[index]['si'],
"image": snapshot.data!.docs[index]['image'],
};
var collectionRef = await FirebaseFirestore.instance
.collection("Cart");
collectionRef.add(data_to_save);
},
), // Inkwell for add item to cart
],
),
I want to place the below code between the two vertical divider as a text wideget to show the no of items added to cart. Can someone help.? I'm able to get the cart value in cart_value but unable to display it to Text widget.
FirebaseFirestore.instance.collection("Cart").get().then((value) {
value.docs.forEach((element) {
FirebaseFirestore.instance.collection("Cart").doc(element.id).get().then((value2) => {
if(value2.data()!['grocery_id']==docid)
cart_value = (value2.data()['quantity'])
});
});
});
You should be using a Future method to fetch data from Firestore and return an integer or double value of cart_value like this :
int cart_value = 0;
Future<int> cart() async {
var cart = await FirebaseFirestore.instance.collection("Cart").get();
for (var element in cart.docs) {
FirebaseFirestore.instance.collection("Cart").doc(element.id).get().then((value2) => {
if(value2.data()!['grocery_id']==docid){
cart_value = (value2.data()!['quantity'])
}
});
}
return cart_value;
}
and put the Future method cart in the future of your FutureBuilder widget:
FutureBuilder(
future: cart(),
builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data!.toString());
}})

how can use future in the listview flutter?

I have future function and I want show this in the listview.seprator, but the listview do not get the future value. how can i fix this?
this is my code:
my hive class:
#HiveType(typeId: 3)
class TaskCat extends HiveObject{
#HiveField(0)
String catName;
#HiveField(1)
int userId;
#HiveField(2)
User? user;
TaskCat(this.catName,this.user,this.userId);
}
This is my function:
Future<List> showCategoryInHome() async {
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
var taskCatBox = await Hive.openBox('taskCat');
var filtertaskCat = taskCatBox.values
.where(
(TaskCat) => TaskCat.userId == sharedPreferences.getInt('key'))
.toList();
return filtertaskCat;
}
and this is my listview code:
FutureBuilder(
future: controller.showCategoryInHome(),
builder: (context, snapshot) {
Future<List> test = controller.showCategoryInHome();
return ListView.separated(
scrollDirection: Axis.horizontal,
shrinkWrap: true,
itemCount: 11 , // here currently I set the fix value but I want the length of my list
itemBuilder: (context, index) {
return TextButton(
onPressed: () {
},
child: Text(
test[index].[catName], // and here not working too bucouse the list is future
style: normalTextForCategory,
),
);
},
separatorBuilder:
(BuildContext context, int index) {
return const VerticalDivider(
width: 15,
color: Colors.transparent,
);
},
);
},
)
Try this:
FutureBuilder<List>(
future: controller.showCategoryInHome(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Text('Loading....');
default:
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
List data = snapshot.data ?? [];
return ListView.separated(
scrollDirection: Axis.horizontal,
shrinkWrap: true,
itemCount:data.length,
itemBuilder: (context, index) {
return TextButton(
onPressed: () {},
child: Text(
data[index]['catName'],
style: normalTextForCategory,
),
);
},
separatorBuilder: (BuildContext context, int index) {
return const VerticalDivider(
width: 15,
color: Colors.transparent,
);
},
);
}
}
},
),
Inside your Future you have builder, where you can access to your future when it ready via snapshot.
I've added the precise type where to make the use of Futurebuilder easier.
Future<List<TaskCat>> showCategoryInHome() async {
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
var taskCatBox = await Hive.openBox('taskCat');
var filtertaskCat = taskCatBox.values
.where(
(TaskCat) => TaskCat.userId == sharedPreferences.getInt('key'))
.toList();
return filtertaskCat;
}
And adjust your Futurebuilder to the following:
FutureBuilder<List<TaskCat>>(
future: controller.showCategoryInHome(),
builder: (context, snapshot) {
// Check if your future has finished
if(snapshot.hasData) {
// Here you have the result of your future, which is a List<TaskCat>
final tasks = snapshot.data;
return ListView.separated(
scrollDirection: Axis.horizontal,
shrinkWrap: true,
itemCount: tasks.length,
itemBuilder: (context, index) {
return TextButton(
onPressed: () {
},
child: Text(
tasks[index].catName,
style: normalTextForCategory,
),
);
},
separatorBuilder:
(BuildContext context, int index) {
return const VerticalDivider(
width: 15,
color: Colors.transparent,
);
},
);
} else {
// Here you can show e.g. a CircularProgressIndicator
return SizedBox();
}
},
)

How Can i get only this text? Without " DocumentReference<Map<String, dynamic>> "

enter image description here
enter image description here
``
#override
Widget build(BuildContext context) {
return GestureDetector(
child: Scaffold(
appBar: AppBar(automaticallyImplyLeading: false),
backgroundColor: Colors.black,
body: StreamBuilder<DocumentSnapshot<Map<String, dynamic>>>(
stream: FirebaseFirestore.instance.collection('History').doc(userId).snapshots(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.active) {
final DocumentSnapshot<Map<String, dynamic>>? getDocument = snapshot.data;
final Map<String, dynamic>? map = getDocument?.data();
var setList = map!['userchat'];
return ListView.builder(
itemCount: setList.length,
itemBuilder: (context, index) {
final get = setList[index];
print(get);
return const Text('data');
},
);
}
return const Text('data');
},
),
),
);
}
``
For getting the required text, i.e., the text in index = 3, you can use a conditional check in itemBuilder.
if(index==3){
final get = setList[index];
print(get);
return const Text('data');
}else{
return const SizedBox.shrink();
}
SizedBox.shrink() helps in avoiding to return a null value, but in turn returns a widget which doesn't show up, or simply nothing.
You can use it if you want, or can go with Text('data').

Parsing Json in flutter - Does not show data

I am developing an android application with flutter, what I am doing is displaying a json in an application page. When I run the application it doesn't give me an error but it doesn't show me the data, the json I want to read is the following:
[
{
"deviceid": 27,
"f2cuid": "Estacion1_A",
"devicedata": {
"isRunning": 0,
"color": "w3-red",
"colorNoW3": "red",
"device_name": "Estacion1_A"
}
},
{
"deviceid": 20,
"f2cuid": "B19",
"devicedata": {
"isRunning": 1,
"color": "w3-green",
"colorNoW3": "green",
"device_name": "B19"
}
}
]
It's in my model class:
class Stations {
Int? isRunning;
String? color;
String? colorNoW3;
String? devicename;
Stations(
{this.isRunning,
this.color,
this.colorNoW3,
this.devicename,
});
factory Stations.fromJson(Map<String, dynamic> json) {
return Stations(
isRunning: json['isRunning'],
color: json['color'],
colorNoW3: json['colorNoW3'],
devicename: json['device_name'],
);
}
}
This is my service:
Future<List<Stations>> getStations() async {
Uri url = Uri.parse('URL');
final response = await http.get(url);
var data = jsonDecode(response.body);
print('data: $data');
List<Stations> stations = data.map((e) => Stations.fromJson(e)).toList();
return stations;
}
and this is the way I display it:
return Scaffold(
appBar: AppBar(
title: const Text('Sistema Escolar Administrativo'),
),
drawer: DrawerWidgetMenu(),
body: Container(
child: FutureBuilder(
future: stationSvc.getStations(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (!snapshot.hasData) {
return Container(
child: Center(
child: Text('No hay datos que mostrar'),
),
);
}
return snapshot.data.length > 0
? ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return Card(
child: InkWell(
onTap: () {
Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (context) => HomePage(),
),
);
},
child: ListTile(
leading: Text(snapshot.data[index].devicename!),
title: Text(snapshot.data[index].color!),
subtitle: Text(snapshot.data[index].colorNoW3!),
),
));
})
: Center(
child: Text('No hay datos, registra un grupo primero'));
}),
),
);
You forgot to specify nested map:
factory Stations.fromJson(Map<String, dynamic> json) {
return Stations(
isRunning: json['devicedata']?['isRunning'],
color: json['devicedata']?['color'],
colorNoW3: json['devicedata']?['colorNoW3'],
devicename: json['devicedata']?['device_name'],
);
}
I am sharing a complete class named SampleModel below which can help to parse JSON in flutter:
class SampleModel {
String? _myName;
bool? _isActive;
SampleModel({String? myName, bool? isActive}) {
if (myName != null) {
_myName = myName;
}
if (isActive != null) {
_isActive = isActive;
}
}
String? get myName => _myName;
set myName(String? myName) => _myName = myName;
bool? get isActive => _isActive;
set isActive(bool? isActive) => _isActive = isActive;
SampleModel.fromJson(Map<String, dynamic> json) {
_myName = json['myName'];
_isActive = json['isActive'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['myName'] = _myName;
data['isActive'] = _isActive;
return data;
}
}
Sample JSON for the same class:
{
"myName" : "Your Name",
"isActive" : true
}
Check if this can help in your case.
your json type is an array, not a map.
look at [ ] syntax on json file.
to deal with an Array, you should make it to a list first:
List<Map<String,dynamic>> mylistofMapformJson = json.decode(receivedJson);
//you should get your list stations like this:
List<Stations> listStationsFromJson = List.generate(
mylistofMapformJson.length,
(index) => Stations.fromJson(mylistofMapformJson));

Check if key exists in Firebase Firestore

I want to make a to-do list with task due date as an optional field, so I need to check if some tasks have dueDate and add it as a subtitle based on that. How can I check if a field exists inside a doc in a StreamBuilder?
class _TaskListState extends State<TaskList> {
var myStream;
#override
void initState() {
myStream = FirebaseFirestore.instance
.collection('tasks')
.doc(widget.uid)
.collection('mytasks')
.snapshots();
super.initState();
}
...
void _updateTaskDesc(
dynamic currTask, String newDesc, DateTime newDate, TimeOfDay newTime) {
FirebaseFirestore.instance
.collection('tasks')
.doc(widget.uid)
.collection('mytasks')
.doc(currTask['id'])
.update({
'desc': newDesc,
'dueDate': newDate.toString(),
'dueTime': newTime.toString(),
});
}
#override
Widget build(BuildContext context) {
return StreamBuilder(
stream: myStream,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: SizedBox(
height: 100, width: 100, child: CircularProgressIndicator()),
);
} else {
final docs = snapshot.data.docs;
bool hasDateTime = ????? <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
return ListView.builder(
itemCount: docs.length,
itemBuilder: (ctx, index) {
final currTask = docs[index];
return InkWell(
highlightColor: Theme.of(context).secondaryHeaderColor,
splashColor: Theme.of(context).secondaryHeaderColor,
onLongPress: () {
showModalBottomSheet<dynamic>(
isScrollControlled: true,
context: context,
builder: (bCtx) {
FocusManager.instance.primaryFocus?.unfocus();
return TaskOptions(_updateTaskDesc,
() => _updateHasImage(docs[index]), currTask);
},
);
},
child: Dismissible(
direction: DismissDirection.startToEnd,
key: UniqueKey(),
onDismissed: (_) async {
FirebaseFirestore.instance
.collection('tasks')
.doc(widget.uid)
.collection('mytasks')
.doc(currTask['id'])
.delete();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text("${currTask['desc']} dismissed"),
action: SnackBarAction(
label: 'Undo',
onPressed: () {
FirebaseFirestore.instance
.collection("tasks")
.doc(widget.uid)
.collection("mytasks")
.doc(currTask['id'])
.set({
"desc": currTask['desc'],
"id": currTask['id'],
"isDone": currTask['isDone'],
"hasImage": currTask['hasImage'],
});
try {
FirebaseFirestore.instance
.collection("tasks")
.doc(widget.uid)
.collection("mytasks")
.doc(currTask['id'])
.update({
"dueDate": currTask['dueDate'],
"dueTime": currTask['dueTime'],
});
} catch (e) {}
},
),
),
);
},
child: ListTile(
...
subtitle: Text(hasDateTime
? DateFormat('dd/MM')
.format(DateTime.parse(currTask['dueDate']))
: ''),
...
I saw that a containsKey('key') method works for some people but I get NoSuchMethod when I try that. What can I do?
The single document is just a normal Dart Map, so you can check if a key exists or not using containsKey method.
So you condition becomes the following:
bool hasDateTime = currTask.containsKey('dueDate`);
NOTE: In the question I can see that you are defining the condition in the wrong place which is outside the itemBuilder method in the ListView so that it is not item based and well not work because it does not make sense.
You can have it in this place:
...
itemBuilder: (ctx, index) {
final currTask = docs[index];
bool hasDateTime = currTask.containsKey('dueDate`);
return InkWell(
...

Categories

Resources