Reading data from Firebase - Flutter - firebase

I would like to change String to something else in order to read data from firebase.
I have the following code:
enum Answers{GS,FB}
String _value = ''; //Probably, String should be changed.
void _setValue(String value) => setState(() => _value = value); ////Probably, String should be changed.
Future kaleciSec(BuildContext context) async {
switch(
await showDialog(
...
...
StreamBuilder(
stream: Firestore.instance.collection('GS').snapshots(),
builder: (context, snapshot){
if(!snapshot.hasData) return Center (child: CircularProgressIndicator());
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
ListTile(
onTap: (){Navigator.pop(context, Answers.GS);}, //selecting GS.
title: Text(snapshot.data.documents[0]['kaleci1'], //showing data from DB
),
),
Divider(
color: Colors.grey,),
ListTile(
onTap: (){Navigator.pop(context, Answers.FB);},
title: Text(snapshot.data.documents[0]['kaleci2'],
),
],
);
},
),
],
),
)
)
{
case Answers.GS:
_setValue('GS'); //I would like connect database here.
break;
case Answers.FB:
_setValue('FB'); //I would like connect database here.
break;
}
My code works without an error. But, I need to show what is selected. Basically, I would like to change _setValue('GS'); to something like _setValue(snapshot.data.documents[0]['kaleci1']); this. Please help me!
I stick on this issue for a few days.

It seems that this is showing "kaleci1", because the _setValue(selected), is only reading the value of the variable and not the valeu from the database.
For you to return the value there, you would need to return the value of the database and add it to a new (or the same variable), so you can use in the _setValue() function.
I believe the code should be something like this:
enum Answers{GS,FB}
String _value = ''; //Probably, String should be changed.
var selected = 'kaleci1';
var valueDB;
void _setValue(String value) => setState(() => _value = value); ////Probably, String should be changed.
Future kaleciSec(BuildContext context) async {
switch(
await showDialog(
...
...
StreamBuilder(
stream: Firestore.instance.collection('GS').snapshots(),
builder: (context, snapshot){
if(!snapshot.hasData) return Center (child: CircularProgressIndicator());
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
ListTile(
onTap: (){Navigator.pop(context, Answers.GS);}, //selecting GS.
title: Text(snapshot.data.documents[0][selected], //showing data from DB
valueDB = snapshot.data.documents[0][selected]
),
),
Divider(
color: Colors.grey,),
ListTile(
onTap: (){Navigator.pop(context, Answers.FB);},
title: Text(snapshot.data.documents[0][selected],
valueDB = snapshot.data.documents[0][selected]
),
],
);
},
),
],
),
)
)
{
case Answers.GS:
_setValue(valueDB); //I would like connect database here.
break;
case Answers.FB:
_setValue(valueDB); //I would like connect database here.
break;
}
Please, let me know how that goes!

Related

How to invisible widget when there is no data in firebase?

i am new to flutter, i'm trying to invisible button when there is no data in Firebase.
To get data i'm using StreamBuilder, if snapshot.data!.docs is null i want to invisible CustomButton which is outside of StreamBuilder.
StreamBuilder:
bool _isVisible = true; //variable
#override
Widget build(BuildContext context) {
final size = MediaQuery.of(context).size;
return Scaffold(
key: _scaffoldKey,
appBar: _appBar(context),
body: CommonRefreshIndicator(
child: StreamBuilder(
stream: FirebaseFirestore.instance
.collection('users')
.doc(_currentUser!.uid)
.collection('favourites')
.snapshots(),
builder:
(BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return const CustomProgressIndicator();
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const CustomProgressIndicator();
}
final data = snapshot.data!.docs;
allData = snapshot.data!.docs;
if (data.isNotEmpty) { //update based on data
_isVisible = true;
} else {
_isVisible = false;
}
return data.isNotEmpty
? _favItemListView(data)
: const Center(
child: Text('No data found'),
);
},
),
),
bottomNavigationBar: _addAllToFavButton(size),
);
}
CustomButton:
Padding _addAllToFavButton(Size size) => Padding(
padding: kSymmetricPaddingHor,
child: Visibility(
visible: _isVisible,
child: CustomButton(
label: 'Add all to my cart',
onPressed: () {},
),
),
);
i have tried with Visibility widget and its work but whenever i'm deleting all data CustomButton is still visible, to invisivle CustomButton every time need to do hot reload.
NOTE: setState is also not working its giving me error.
if any one can help me! Thanks.
If you want to hide your CustomButton when there is no data you can try this:
Put your _favItemListView(data) & _addAllToFavButton(size) inside Stack and give Positioned to your CustomButton with its bottom : 1 property.
StreamBuilder
return data.isNotEmpty
? Stack(
children: [
_favItemListView(data),
_addAllToFavButton(size),
],
)
: const Center(
child: Text('No data found'),
);
CustomButton:
Positioned _addAllToFavButton(Size size) => Positioned(
width: size.width,
bottom: 1, //bottom property
child: Padding(
padding: kSymmetricPaddingHor,
child: CustomButton(
label: 'Add all to my cart',
onPressed: () {}
},
),
),
);
You can check if the snapshot has any data by using snapshot.data!.data()!.isNotEmpty
then
if(snapshot.data!.data()!.isNotEmpty){
//show your data widget
// using a variable might require you to call a setstate but since
//the widget is building here you might get some errors, its safe to just show your widget if data exists
}else{
///no data here
}
Also to get the .data() you need to tell your stream that its a of type <DocumentSnapshot<Map<String, dynamic>>> as . .snapshots() returns Stream<DocumentSnapshot<Map<String, dynamic>>>
StreamBuilder<DocumentSnapshot<Map<String, dynamic>>>(
stream: FirebaseFirestore.instance..

how to listen to a change in a stream from firebase

I am getting data from this stream builder and showing it inside a stateful widget.
Widget chatRoomsLists() {
return StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection('chatrooms').orderBy("lastMessageSendTs", descending: true)
.where("users", arrayContains:myUserName)
.snapshots(),
builder: (context, snapshot){
return snapshot.hasData ? ListView.builder(
shrinkWrap: true,
itemCount: snapshot.data!.docs.length,
itemBuilder: (context,index){
DocumentSnapshot ds = snapshot.data!.docs[index];
return ChatRoomListTile(ds['lastMessage'], ds.id,myUserName! );
}) : Center(child: CircularProgressIndicator());
}) ;
}
and showing it here
class ChatRoomListTile extends StatefulWidget {
final String lastMessage , chatRoomId, myUsername;
ChatRoomListTile(this.lastMessage, this.chatRoomId, this.myUsername,);
#override
_ChatRoomListTileState createState() => _ChatRoomListTileState();
}
class _ChatRoomListTileState extends State<ChatRoomListTile> {
String profilePicUrl = 'https://miro.medium.com/max/875/0*H3jZONKqRuAAeHnG.jpg' , name = '' ,username = "";
getThisUserInfo() async {
print('userinfo called');
username = widget.chatRoomId.replaceAll(widget.myUsername, "").replaceAll("_", "");
QuerySnapshot querySnapshot = await DatabaseMethods().getUserInfo(username);
// print("something bla bla ${querySnapshot.docs[0].id} ${querySnapshot.docs[0]["name"]} ${querySnapshot.docs[0]["profileURL"]}");
name = "${querySnapshot.docs[0]["name"]}";
profilePicUrl = "${querySnapshot.docs[0]["profileURL"]}";
setState(() {});
}
#override
void initState() {
getThisUserInfo();
super.initState();
}
#override
Widget build(BuildContext context) {
print('BUILDING IS CALLED');
return GestureDetector(
onTap: () {
print('name is $username and ${widget.myUsername}');
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Chatting(username, widget.myUsername)));
},
child: Column(
children: [
Container(
margin: EdgeInsets.symmetric(vertical: 8),
child: Row(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(30),
child: Image.network(profilePicUrl,
height: 40,
width: 40,
),
),
SizedBox(width: 12),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
name,
style: TextStyle(fontSize: 16),
),
SizedBox(height: 3),
SizedBox(width: 220,
child: Text(widget.lastMessage,overflow: TextOverflow.ellipsis)),
],
)
],
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: Divider(thickness: 0.5 ,),
)
],
),
);
}
}
I want to call get this future getThisUserInfo every time there is change in widget.lastmessage . How can i do that ?
When i am calling this future inside the build , It is getting called again and again non stop .So how can i only call this when there is a new message only ?
I would suggest you to review the construction of your builder and check the snapshot().listen() method, you can refer to this similar issue for that.
To your follow-up question, it is hard to say what is causing this duplicated update, as it can be a number of things and nothing in your code indicates that, so that being said, I would think of some logic that allows you to check if the update you want to make already happened, perhaps spending a read, which is less expensive than a write.

The method 'contains' was called on null

Hey guys this is my code: Im trying to display available time slots for my booking app.
Expanded(
child: FutureBuilder(
future: getTimeSlotOfCourt(
courtModel,
DateFormat('dd_MM_yyyy').format(context.read(selectedDate).state),
),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
} else {
var listTimeSlot = snapshot.data as List<int>;
return GridView.builder(
itemCount: TIME_SLOT.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3),
itemBuilder: (context, index) => GestureDetector(
onTap: listTimeSlot.contains(index)
? null
: () {
context.read(selectedTime).state =
TIME_SLOT.elementAt(index);
context.read(selectedTimeSlot).state = index;
},
child: Card(
color: listTimeSlot.contains(index)
? Colors.white10
: context.read(selectedTime).state ==
TIME_SLOT.elementAt(index)
? Colors.white54
: Colors.white,
child: GridTile(
child: Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('${TIME_SLOT.elementAt(index)}'),
Text(listTimeSlot.contains(index)
? 'Full'
: 'Available')
],
),
),
header: context.read(selectedTime).state ==
TIME_SLOT.elementAt(index)
? Icon(Icons.check)
: null,
),
),
));
}
},
),
)
],
);
}
I'm getting this error which says the method 'contains' was called on null.
Future<List<int>> getTimeSlotOfCourt(CourtModel courtModel, String date) async {
List<int> result = new List<int>.empty(growable: true);
// var ref = CourtModel().refer;
// var bookingRef = ref.collection(date);
var bookingRef = CourtModel().reference.collection(date);
QuerySnapshot snapshot = await bookingRef.get();
snapshot.docs.forEach((element) {
result.add(int.parse(element.id));
});
return result;
}
This is the function that I have used.
Please help me understand why I'm getting this error which says The method 'contains' was called on null.
The snapshot does not always contain data. The Future builder builds once before the future is completed. Null check the snapshot.data and return a spinner or something to fix this problem
The future builder only brings data when the future is completed and the snapshot may not contain data all the time. And you are only checking for the waiting state, a better solution would be to check whether the snapshot has data or not.
Something like this would be the preferred solution.
FutureBuilder(
future: _getTimeSlotOfCourt(),
builder:(context, AsyncSnapshot snapshot) {
if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator());
} else {
// place you code here
}
}
)

The method '[]' can't be unconditionally invoked because the receiver can be 'null'

I'm new to Flutter. I am trying to develop an application.
I want to show the staff list in the Firebase database. However, I am getting the following error.
Error :
The method '[]' can't be unconditionally invoked because the receiver
can be 'null'. Try making the call conditional (using '?.') or adding
a null check to the target ('!').
Kodlarım :
`import 'package:calendar/page/mainPage.dart';
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class Staff extends StatefulWidget {
#override
_StaffState createState() => _StaffState();
}
class _StaffState extends State<Staff> {
final _firestore = FirebaseFirestore.instance;
#override
Widget build(BuildContext context) {
// ignore: unused_local_variable
CollectionReference staffRef = _firestore.collection('staff');
return Scaffold(
appBar: AppBar(
title: Text("Personel Listesi"),
backgroundColor: Colors.redAccent[400],
actions: <Widget>[
IconButton(
icon: Icon(Icons.home),
onPressed: () {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (_) => MainPage()),
(route) => true);
},
),
],
),
body: Container(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Center(
child: Column(
children: [
StreamBuilder<QuerySnapshot>(
stream: staffRef.snapshots(),
builder: (BuildContext context, AsyncSnapshot asyncSnapshot) {
if (asyncSnapshot.hasError) {
return Center(
child: Text(
"Bir hata oluştu, lütfen tekrar deneyiniz."));
} else {
if (asyncSnapshot.hasData) {
List<DocumentSnapshot> listStaff =
asyncSnapshot.data.docs;
return Flexible(
child: ListView.builder(
itemBuilder: (context, index) {
return Card(
elevation: 20,
color: Colors.greenAccent[200],
child: ListTile(
trailing: IconButton(
icon: Icon(Icons.delete),
onPressed: () async {
await listStaff[index]
.reference
.delete();
},
),
title: Text(
'${listStaff[index].data['nameSurname']}',
style: TextStyle(fontSize: 20),
),
subtitle: Column(
children: [
Row(
mainAxisAlignment:
MainAxisAlignment.start,
children: [
Text(
'${listStaff[index].data['tip']}',
style: TextStyle(fontSize: 14),
),
],
),
Row(
mainAxisAlignment:
MainAxisAlignment.start,
children: [
Text(
'${listStaff[index].data['mail']}',
style: TextStyle(fontSize: 14),
),
],
),
Row(
mainAxisAlignment:
MainAxisAlignment.start,
children: [
Text(
'${listStaff[index].data['phone']}',
style: TextStyle(fontSize: 14),
),
],
),
],
),
),
);
},
itemCount: listStaff.length),
);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
}
},
),
],
),
),
),
),
);
}
}
`
In the new flutter update, we don't need to add .data()
my codes below
title: Text(
**'${listStaff[index].data['nameSurname']}',**
style: TextStyle(fontSize: 20),
),
Changing it like this fixed the error.
title: Text(
**'${listPersonel[index]['nameSurname']}'**,
style: TextStyle(fontSize: 20),
),
Problem:
You get this error if you're accessing an element on a nullable List or Map. Let's understand it for a List and you can apply the same solutions for your Map.
For example:
List<int>? someList;
void main() {
int a = someList[0]; // Error
}
Solutions:
Use a local variable:
var list = someList;
if (list != null) {
int a = list[0]; // No error
}
Use ? and ??:
int a = someList?[0] ?? -1; // -1 is the default value if List was null
Use ! bang operator only if you're sure that the List isn't null.
int a = someList![0];
For those who are using FutureBuilder/StreamBuilder:
You can solve the error in two ways:
Specify a type to your FutureBuilder/StreamBuilder
FutureBuilder<List<int>>( // <-- type 'List<int>' is specified.
future: _listOfInt(),
builder: (_, snapshot) {
if (snapshot.hasData) {
List<int> myList = snapshot.data!; // <-- Your data
}
return Container();
},
)
Use as to downcast Object to your type, say a List or Map.
FutureBuilder(
future: _listOfInt(),
builder: (_, snapshot) {
if (snapshot.hasData) {
var myList = snapshot.data! as List<int>; // <-- Your data using 'as'
}
return Container();
},
)
I was having the same problem as the questioner and the solution came from this post. I leave it here in case anyone else has this problem.
https://fileidea.com/2021/05/05/method-cant-be-unconditionally-invoked-because-the-receiver-can-be-null-with-firestore/
before:
final mySnapStream = messagesCollection
.orderBy('date', descending: true)
.limit(100)
.snapshots()
.map((obj) => obj.docs
.map((e) => new MyItem(
e.data()['myFieldOne'],
e.data()['myFieldThree'],
e.data()['myFieldFour']))
.toList());
after:
final mySnapStream = messagesCollection
.orderBy('date', descending: true)
.limit(100)
.snapshots()
.map((obj) => obj.docs
.map((e) => new MyItem(
(e.data() as dynamic)['myFieldOne'],
(e.data() as dynamic)['myFieldThree'],
(e.data() as dynamic)['myFieldFour']))
.toList());
It is a typical null-safety related error. I did not try the code, by I guess that probably assigning asyncSnapshot.data.docs to listStaff possibly may return null, but your declared type List<DocumentSnapshot> doesn't allow for that. If you are 100% sure that this assignment won't return null value, you can add '!' to ensure compiler, that it will be List, so it will let you use methods. Although if you want this list to be nullabe, you can simply add '?' to show it, and than use '?.' to use methods. It works like: Check if the object is null and execute method on this method only if its not.

Flutter Future <String > cant be assigned to parameter type string

I have a future which gives the a return leadid which is of type string.
Future<String> getleader() async {
final DocumentSnapshot data = await Firestore.instance
.collection('groups')
.document(widget.detailDocument.data['groupId']).get();
String leadid = data.data['leader'];
return leadid;
}
I want to use that value returend here.
ListTile(
title: Text(getleader()),
leading: Text('Leader :'),
),
It says future string cant be assigned to parameter string.
Also i have tried adding a a function to await result as follows
getdata2() async {
String lead1= await getleader();
but it too shows the error Future dynamcic is not a subtype of type string
This is where i want the to use the future value
Widget _memebrprofile() {
return FutureBuilder(
future: getleader(),
builder: (context, snapshot) {
if (snapshot.hasData) {
// store the value of the Future in your string variable
storeValue = snapshot.data;
return storeValue;
}
return Scaffold(
drawer: newdrawer(),
appBar: AppBar(
title: Text('User Details'),
),
body: SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(),
child: Column(
children: <Widget>[
ListTile(
title: SelectableText(
widget.detailDocument.data["groupId"] ?? '',
),
leading: Text('Group Id :'),
),
ListTile(
title: Text(storeValue),//this is where i want to display the string
leading: Text('Leader :'),
),
Row(
children: <Widget>[
Flexible(
child: RaisedButton(
onPressed: () {
//this is where i want to use it as a string value to check a certain bool. if (storeValue == _uid()) {
Firestore.instance
.collection('users')
.document(widget.detailDocument.documentID)
.updateData({
'groupId': "",
});
Navigator.of(context).pop();
Navigator.pushNamed(context, assignedTask.id);
} else {}
},
child: Text('Remove user'),
),
),
/* Flexible(
child:RaisedButton(
onPressed: () {
},
child: Text('Changerole to user'),
),),
Flexible(
child: RaisedButton(
onPressed: () {
},
child: Text('Changerole to Admin'),
),
),*/
Flexible(
child: RaisedButton(
onPressed: () async {
FirebaseAuth auth = FirebaseAuth.instance;
final FirebaseUser user =
await auth.currentUser();
final userid = user.uid;
if (widget.detailDocument.documentID == userid) {
Navigator.pushNamed(context, MyProfile.id);
} else {}
},
child: Text('Edit Profile'),
),
),
],
),
],
),
),
),
);
});
}
}
Try the following:
FutureBuilder(
future: getleader(),
builder: (context, AsyncSnapshot<String> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return ListView.builder(
shrinkWrap: true,
itemCount: 1,
itemBuilder: (BuildContext context, int index) {
return ListTile(
contentPadding: EdgeInsets.all(8.0),
title:
Text(snapshot.data),
);
});
} else if (snapshot.connectionState == ConnectionState.none) {
return Text("No data");
}
return CircularProgressIndicator();
},
),
Future<String> getleader() async {
final DocumentSnapshot data = await Firestore.instance
.collection('groups')
.document(widget.detailDocument.data['groupId']).get();
String leadid = data.data['leader'];
return leadid;
}
The reason you are getting the above error, is because getleader() returns a Future<String> and Text widget takes a value of type String, therefore using FutureBuilder then you can get the value of the Future and use it inside the Text widget.
You are getting the error because you are not using a FutureBuilder.
Try using a FutureBuilder.
You can solve it by wrapping your widget in a FutureBuilder.
Check the code below: It works perfectly fine.
// use a future builder
return FutureBuilder<String>(
// assign a function to it (your getLeader method)
future: getleader(),
builder: (context, snapshot) {
if(snapshot.hasData){
// print your string value
print(snapshot.data);
return new ListTile(
leading: Text('Leader'),
title: Text(snapshot.data),
onTap: () {
}
);
} else {
return Text(snapshot.error.toString());
}
}
);
I hope this helps.
UPDATED
As requested to store the value(String) into a variable, check the code below:
// declare your variable
String storeValue;
return FutureBuilder<String>(
// assign a function to it (your getLeader method)
future: getleader(),
builder: (context, snapshot) {
if(snapshot.hasData){
// store the value of the Future in your string variable
storeValue = snapshot.data;
return new ListTile(
leading: Text('Leader'),
title: Text(snapshot.data),
onTap: () {
}
);
} else {
return Text(snapshot.error.toString());
}
}
);
You can create another function in your StatefulWidget that updates your lead1 using setState()
String lead1 = "";
getLeadID() {
getLeader().then((val) => setState(() {
lead1 = val;
}));
}
.then(val) waits for getLeader() to finish, then allows you to use the returned value val.
Edit:
Set the text in your ListTile to the lead1 variable, like
ListTile( title: Text(lead1), leading: Text('Leader :'), ),
Then call the getLeadID() funciton in initState(), like this;
class _MyHomePageState extends State<MyHomePage> {
String lead1 = "";
#override
void initState() {
super.initState();
getLeadID();
}
#override
Widget build(BuildContext context) {
//rest of code

Resources