The method 'contains' was called on null - firebase

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
}
}
)

Related

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.

how do i replace CARD with a for loop in flutter

i want to replace the card with for loop. here's the body for my screen where i am showing the output
body: FutureBuilder<List>
(
future: db.getAllRecords("EMPLOYEE"),
initialData: List(),
builder: (context,snapshot){
return snapshot.hasData
? ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (_, int position){
final item =snapshot.data[position];
return Card(
child:ListTile(
title: Text(
snapshot.data[position].row[1]
),
),
);
},
)
: Center(
child:CircularProgressIndicator() ,
);
Actually, the way you wrote it is the standard way of implementation in Flutter. You could call the default ListView constructor instead of ListView.builder one and then call the for loop but that's not the best practice.
List<Widget> _buildListItems(BuildContext context, List<...> list) {
final output = List<Widget>();
for (var item in list) {
output.add(
Card(
child: ListTile(
title: Text(item.row[1]),
),
),
);
}
return output;
}
//... back to your build(context) body
body: FutureBuilder<List>(
future: db.getAllRecords("EMPLOYEE"),
builder: (context, snapshot) {
return snapshot.hasData
? ListView(
children: [
..._buildListItems(context, snapshot.data),
],
)
: Center(
child: CircularProgressIndicator(),
);
}
)

How can i get all the user documents from a collection in flutter

I have a collection named flpProduct in firestore. I want to access all the documents stored in the flpProduct collection. I tried it with many Futurebuilder and Streambuilder but none of them worked.
I tried this
FutureBuilder<QuerySnapshot> (
future: flpProductFuture,
builder: (context, snapshot) {
if (snapshot.hasError) {
return Center(
child: Text('Something went Wrong'),
);
}
if (snapshot.connectionState == ConnectionState.done &&
snapshot.hasData) {
snapshot.data.documents.forEach((element) {
print(element.data['title']);
});
return Text('done');
}
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Spinner(),
Text(
'Fetching Data',
style: TextStyle(color: Colors.black54, fontSize: 18.0),
)
],
);
},
),
This seems to get me the data but how can i now display this in a widget.
I already have a class that returns a container to display the information in a seperate class. I just want to pass the data to the class and display it
Better to use StreamBuilder rather than FutureBuilder
because StreamBuilder is responsible for real-time changes
StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance.collection(Your collection Name).snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Text("Loading");
}
return new ListView(
children: snapshot.data.documents.map((DocumentSnapshot document) {
return new ListTile(
title: new Text(document.data()[key]),
subtitle: new Text(document.data()[key]),
);
}).toList(),
);
},
);

Flutter: Unable to read data from firebase. The method '[]' was called on null error

I am trying to read the data from firebase after scanning a barcode.
This is how it should appear, but instead of barcode, it should display name and price from the database
(https://m.imgur.com/gallery/lEFJZ0Q)
Code:
class ListTileModel {
String barcode;
ListTileModel(this.barcode);
}
the below code is inside the stateful widget
List<ListTileModel> _items = [];
String barcode = "";
void _add() {
_items.add(ListTileModel(barcode));
setState(() {});
}
#override
void initState(){
super.initState();
}
StreamBuiler Used:
new Container(
child: ListView(
children: _items
.map((item) => StreamBuilder(
stream: FirebaseDatabase.instance.reference().child('prd').child(item.barcode).onValue,
builder: (context, snap) {
print(item.barcode);
if(snap.hasData){
DataSnapshot snapshot = snap.data.snapshot;
Map<dynamic, dynamic> itm = snapshot.value;
return snap.data.snapshot.value == null
? SizedBox()
: ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.vertical,
itemCount: 1,
itemBuilder: (context, index) {
return Row(
children: <Widget>[
ConstrainedBox(
child: Container(
child: Text(itm[index]['name'],style: TextStyle(fontSize: 20),),
),
),
SizedBox(width: 100,),
ConstrainedBox(
child: Container(
child: Text(itm[index]['price'].toString(),style: TextStyle(fontSize: 20),),
),
),
],
);
},
);
}
else {
return Center(child: CircularProgressIndicator());
}
}
),
).toList()
),
),
The Barcode Scan code:
widget:
floatingActionButton: FloatingActionButton(
onPressed: scan,
child: Icon(Icons.add_shopping_cart),
),
scan() :
Future scan() async{
try{
String barcode = await BarcodeScanner.scan();
setState(() {
this.barcode = barcode;
});
_add();
}on PlatformException catch(e) {
if(e.code == BarcodeScanner.CameraAccessDenied){
setState(() {
this.barcode = 'Access Denied';
});
}
}
I'm am getting following Error:
The following NoSuchMethodError was thrown building:
The method '[]' was called on null.
Receiver: null
Tried calling:
Please try this and let me know if it fixes your problem you need to change
Text(itm[index]['name'],style: TextStyle(fontSize: 20),),
Text(itm[index]['price'].toString(),style: TextStyle(fontSize: 20),),
To
Text(itm['det']['name'],style: TextStyle(fontSize: 20),),
Text(itm['det']['price'].toString(),style: TextStyle(fontSize: 20),),
Let me know if this works for you. I believe the problem is the index also.
right now your saying.

Trying to implement loading spinner while loading data from Firestore with Flutter

I'm working on an app that display spinner when backend loading data from Firestore but it's not worked as intended and I'm having a struggle to find the flaw.
My Orders Page code
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:table_service/providers/order.dart';
import '../providers/session.dart';
class OrdersPage extends StatefulWidget {
bool isLoading = true;
#override
_OrdersPageState createState() => _OrdersPageState();
}
class _OrdersPageState extends State<OrdersPage> {
List<Order> _orders = [];
#override
Widget build(BuildContext context) {
final session = Provider.of<Session>(context, listen: false);
return Scaffold(
floatingActionButton: session.privilege == 'Administrator' ||
session.privilege == 'Waiter' ||
session.privilege == 'Customer'
? FloatingActionButton(
heroTag: 'OrdersPageFAB',
onPressed: () {},
child: Icon(Icons.add, color: Colors.white),
)
: null,
body: FutureBuilder(
future: session.fetchOrdersData(),
builder: (ctx, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
} else {
print(snapshot.data);
return GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 2 / 2,
),
itemCount: _orders.length,
itemBuilder: (_, i) {
return Padding(
padding: const EdgeInsets.all(5.0),
child: Card(
child: GridTile(
child: Icon(
Icons.library_books,
size: 100.0,
color: Colors.grey,
),
footer: GridTileBar(
backgroundColor: Colors.black54,
title: Text('Order by: ${_orders[i].name}'),
),
),
),
);
},
);
}
},
),
);
}
}
The fetchOrdersData() handler
final Auth auth = Auth();
final Firestore database = Firestore.instance;
String user_name;
String privilege;
List<Food> _foods = [];
List<Order> _orders = [];
List<TransactionModel.Transaction> _transactions = [];
...
...
Future fetchOrdersData() async {
_orders.clear();
return await database.collection('orders').getDocuments().then((documents) {
documents.documents.forEach((order) {
database
.collection('users')
.document(order.data['uid'])
.get()
.then((user) {
_orders.add(Order(
id: order.documentID,
tableNumber: order.data['tablenumber'],
orderDate: (order.data['orderdate'] as Timestamp).toDate(),
status: order.data['status'],
note: order.data['note'],
uid: order.data['uid'],
name: user.data['user_name'],
));
});
});
return _orders;
});
notifyListeners();
}
get getOrders {
return [..._orders];
}
I have tried many methods including StreamBuilder, setState() and recently FutureBuilder.
Did i just missing an important code?
Or did i use the wrong method?
The problem was Orders Page showing 0 data even though List on _fetchOrdersData() have 1 element.
for full source code
here on github
The other answers look reasonable. They are just missing data validation checks, which I find is required in all my apps. Because if I have a good connection, and hasData is true and hasError is false, there might be no documents at all though. This should be checked. Here is a snippet from my projects.
Checking connection state is the same as just checking snapshot.hasError.
Widget _getMyFriends() {
return StreamBuilder<QuerySnapshot>(
stream: Database.getFriendsByUserId(widget.loggedInUserId),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError)
return Center(child: Text("Error"));
else if (!snapshot.hasData)
return Center(child: Text("Loading..."));
else if (snapshot.data.documents.isEmpty) //also check if empty! show loader?
return Center(child: Text("No friends added yet."));
else
return ListView(
children: snapshot.data.documents.map((DocumentSnapshot document) {
return SimpleUserPanel(userId: document['friendid']);
}).toList(),
);
}
);
}
You should do the following:
else {
if(snapshot.hasData){
print(snapshot.data);
return GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 2 / 2,
),
itemCount: _orders.length,
itemBuilder: (_, i) {
return Padding(
padding: const EdgeInsets.all(5.0),
child: Card(
child: GridTile(
child: Icon(
Icons.library_books,
size: 100.0,
color: Colors.grey,
),
footer: GridTileBar(
backgroundColor: Colors.black54,
title: Text('Order by: ${_orders[i].name}'),
),
),
),
);
},
// By default, show a loading spinner.
return CircularProgressIndicator();
},
So first check if snapshot has data using the property hasData and since this is asynchronous it will first execute the return CircularProgressIndicator(); and then execute the if block.
Loking at your code, you have to check for ConnectionState.active also with your snapshot.connectionState == ConnectionState.waiting.
Actually you have more power and controll when using FutureBuilder or StreamBuilder. Below is a sample code snippet:
switch (snapshot.connectionState) {
case ConnectionState.none:
return Center(child: Text("Check Connection"));
case ConnectionState.active:
case ConnectionState.waiting:
return Center(child: CircularProgressIndicator(backgroundColor: Theme.of(context).primaryColorLight,));
case ConnectionState.done:
if (snapshot.hasError) {
return Center(child: Text("Error occured!"));
} else if (snapshot.hasData) {
return YourWidgetWithData();
} else {
debugPrint("What went wrong");
return SizedBox();
}
break;
default:
return SizedBox();
}

Resources