Error: Class 'QuerySnapshot' has no instance getter 'data' - flutter

I'm trying to creat a UI screen with 3 tabs in it. RecentItem, ReviewItem and Profile. however there's some backend problem in recentitem widget. With the error shown: Class 'QuerySnapshot' has no instance getter 'data'.
Ps: The whole code is quite big hence I have shared a doc for the whole code: https://docs.google.com/document/d/1qs4ajPJ0DBjserBJ3iBZmPXPz1zTP7tIYSh8vceVQn8/edit?usp=sharing
RecentItems():
Widget RecentItems() {
return Padding(
padding: const EdgeInsets.all(10.0),
child: StreamBuilder(
stream: Firestore.instance
.collection("users")
.document(uid)
.collection("recent")
.snapshots(),
builder: (context, snapshot) {
print(snapshot.data);
List orders = List.from(Map.from(snapshot.data.data)['orders']);
Map order;
for (int i = 0; i < orders.length; i++) {
if (orders[i]['orderId'] == widget.map['orderId'] &&
orders[i]['homemaker'] == widget.map['homemaker']) {
order = orders[i];
break;
}
}
if (snapshot.data.isEmpty) {
return Center(
child:
Text("OOPS, Looks like no one is serving!"));
}
print(order);
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
} else if (snapshot.hasData) {
print(snapshot.data.documents[0].data);
return Container(
height: 400,
child: ListView.builder(
itemCount: snapshot.data.documents.length,
itemBuilder: (BuildContext context, int index) {
return Container(
margin: EdgeInsets.all(10.0),
width: MediaQuery
.of(context)
.size
.width,
height: 85,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10.0),),
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Row(
children: <Widget>[
Expanded(child: Text(
"${snapshot.data.documents[index]
.data["dishname"]}", style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.bold),)),
//Icon: how to access if food is veg or not
],
),
// SizedBox(height:5),
Row(
children: <Widget>[
Expanded(child: Text(
"${snapshot.data.documents[index]
.data["homemaker"]}",
style: TextStyle(fontSize: 10),)),
Text("${snapshot.data.documents[index]
.data["rating"]}",
style: TextStyle(fontSize: 15)),
Icon(
Icons.star, color: Colors.yellow.shade800,
size: 20,)
],
),
SizedBox(height: 5),
//How to access order date
Text(
"Ordered ${DateTime
.parse(order['order_placed_at']
.toDate()
.toString())
.day}/${DateTime
.parse(order['order_placed_at']
.toDate()
.toString())
.month}/${DateTime
.parse(order['order_placed_at']
.toDate()
.toString())
.year}}",
style: TextStyle(fontSize: 15.0,
fontWeight: FontWeight.bold),
),
],
),
),
);
}),
);
} //
}),
);
}
The Error Message is:
The getter 'data' was called on null.
Receiver: null
Tried calling: data
The relevant error-causing widget was:
StreamBuilder<QuerySnapshot> file:///C:/Flutter/Naniz_eats/lib/UserProfilePage.dart:434:14
════════════════════════════════════════════════════════════════════════════════════════════════════
I/flutter (28940): Instance of 'QuerySnapshot'
════════ (3) Exception caught by widgets library ═══════════════════════════════════════════════════
Class 'QuerySnapshot' has no instance getter 'data'.
Receiver: Instance of 'QuerySnapshot'
Tried calling: data
The relevant error-causing widget was:
StreamBuilder<QuerySnapshot> file:///C:/Flutter/Naniz_eats/lib/UserProfilePage.dart:434:14

Several things or all might be causing this:
the print from the first line of builder. If snapshot was indeed empty you would already be calling data without first checking if it is empty.
snapshot.data.data which I think is a typo in the second line of builder
The fact that you are doing operations on snapshot without first checking for snapshot.hasData, snapshot.data.documents.length != 0 to ensure that you are not doing operations on null snapshots.
You should also be able to check specifically which line is causing the error by pressing on the error messages, one of the error messages should contain a link to the specific line (not shown in your question, should be somewhere between the long stacks of error messages)

This code:
Firestore.instance
.collection("users")
.document(uid)
.collection("recent")
.snapshots()
returns a Stream of type QuerySnapshot, the problem is here:
List orders = List.from(Map.from(snapshot.data.data)['orders']);
the code snapshot.data will return an instance of QuerySnapshot and QuerySnapshot does not contain a instance variable called data. So, if you want to a list of documents then you have to do the following:
List<DocumentSnapshot> orders = snapshot.data.documents;
https://github.com/FirebaseExtended/flutterfire/blob/master/packages/cloud_firestore/cloud_firestore/lib/src/query_snapshot.dart#L17

Related

When I access the first character from the list of names the list view gives me error in flutter

I have below code in flutter which shows the list of the names with rank.
All are working fine but I want to access the first character of the lastName. When I do this, throws the error Bad State: No element.
class ThisMonthTab extends StatelessWidget {
const ThisMonthTab({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 10),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Expanded(
child: StreamBuilder<List<UserModel>>(
stream: PlayersRepo.fetchThisMonthPlayersList(),
builder: (context, snapshot) {
List<UserModel>? list = snapshot.data;
if (snapshot.connectionState == ConnectionState.waiting) {
return getLoader();
}
if (snapshot.data!.isEmpty) {
return emptyPageMessage(
LocaleKeys.noProgressThisMonth.tr());
}
if (snapshot.hasData) {
log(snapshot.data!.length.toString());
return ListView.builder(
shrinkWrap: true,
itemCount: list?.length,
padding: const EdgeInsets.only(top: 10),
itemBuilder: (context, index) => LeaderboardTileWidget(
imageUrl: list![index].avatarUrl,
title:
'${list[index].firstName} ${list[index].lastName.characters.first}.', //Error is here in this line for accessing the first character of last name
subtitle:
'${LocaleKeys.level.tr()} ${list[index].currentLevel} (${list[index].currentRankName.characters.first}), ${list[index].monthlyXP.gainedXP} XP',
isUser: list[index].id ==
context.read<UserModelProvider>().user.email,
rankNo: index + 1,
),
);
} else {
return emptyPageMessage(LocaleKeys.fetchError.tr());
}
}),
),
const SizedBox(height: 10),
Text(
LocaleKeys.leaderboardDescription.tr(),
style: Theme.of(context).textTheme.subtitle1?.copyWith(
fontSize: 9, color: Colors.grey.shade600, letterSpacing: 0.4),
),
const SizedBox(height: 70)
],
),
);
}
}
Can anyone figure out what is the mistake here because directly if I am displaying the list[index].lastName it works totally fine.
If you just want the first character of the last name, why not go the JS way, just change
list[index].lastName.characters.first
to
list[index].lastName[0]
For safe bet, you may want to convert it to String first as
list[index].lastName.toString()[0]
The reasoning is, at the EOD, a String is just an Array of Characters and by [0], we're picking its first item. You can run and check this at the Dart Playground.
I have just solved the error by checking if lastName is Empty or Not. In the list of the names it was broken where the names does not have last name.
I have changed list[index].lastName.characters.first to list[index].lastName.isEmpty ? " " : list[index].lastName.characters.first
Hope this helps someone.

Flutter QR how to pass QR data to the next screen

How do I make it so when the user scan a QR code, the result will then be passed to the next screen.
Here is my code so far,
Widget build(BuildContext context) => SafeArea(
child: Scaffold(
body: Stack(
alignment: Alignment.center,
children: <Widget>[
buildQrView(context),
Positioned(top: 10, child: buildControlButtons()),
Positioned(bottom: 30, child: buildResult()),
],
),
),
The buildResult is this
Widget buildResult() => Container(
padding: EdgeInsets.all(12),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8), color: Colors.white24),
child: Text(
barcode != null ? _dataFetch() : 'Scan a code!',
maxLines: 3,
),
Then the function _dataFetch is as below
_dataFetch() async {
if (barcode == null) {
print('error');
} else {
var route = new MaterialPageRoute(
builder: (BuildContext context) =>
new TransferProcessQR(
value: PassdataQR(
email: barcode!.code.toString(),
)
)
);
Navigator.of(context).push(route);
}
I have another class for PassdataQR but its pretty self explanatory. With this code everytime I run it will give me an error
The following _TypeError was thrown building QRScanPage(dirty, dependencies: [MediaQuery], state: _QRScanPageState#720ae):
type 'Future' is not a subtype of type 'String'
and the Navigator functions will be messed up.
Is there another approach I can do, so after a QR code is scanned, the result will be passed to the next screen without errors?
It seems to me that your _dataFetch method returns a futureand in your buildResult method you're using it like so:
Text(
barcode != null ? _dataFetch() : 'Scan a code!',
maxLines: 3,
)
You can use a futurebuilder to retrieve the async data:
Widget buildResult() => Container(
padding: EdgeInsets.all(12),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8), color: Colors.white24),
child: FutureBuilder<string>(
future: _dataFetch,
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
if (snapshot.HasData) {
return Text(snapshot.data, maxLines: 3);
} else return Text('Scan a code!', maxLines: 3);
},
),
);
According to your repository you could just modify line 150:
controller.scannedDataStream
.listen((barcode) => {
setState(() => this.barcode = barcode));
Get.to(new TransferProcessQR(
value: PassdataQR(
email: barcode!.code.toString(),
)
));
}
Notice that in order for this to work you'll have to use the Get Package Route Management to navigate to another page. That's because you don't have access to the build context in this code snipped. Normally you would call Navigator.of(context).push(...) but that's not possible without a build context.

StreamBuilder<QuerySnapshot>(dirty, state: _StreamBuilderBaseState<QuerySnapshot, AsyncSnapshot<QuerySnapshot>):

I cant able to retrive the data from Firestore and getting Error as below,
════════ Exception caught by widgets library
═══════════════════════════════════════════════════════ The following
assertion was thrown building StreamBuilder(dirty,
state: _StreamBuilderBaseState<QuerySnapshot,
AsyncSnapshot>#e568b): A build function returned null.
The offending widget is: StreamBuilder Build functions
must never return null.
To return an empty space that causes the building widget to fill
available room, return "Container()". To return an empty space that
takes as little room as possible, return "Container(width: 0.0,
height: 0.0)".
The relevant error-causing widget was: StreamBuilder
file:...dart:140:15 When the exception was thrown, this was the stack:
#0 debugWidgetBuilderValue. (package:flutter/src/widgets/debug.dart:300:7)
#1 _Closure.call (dart:core-patch/function.dart)
#2 debugWidgetBuilderValue (package:flutter/src/widgets/debug.dart:321:4)
#3 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4569:7)
#4 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4737:11) ...
Below is my code.
StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection("currency").snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData){
print('test pharse');
Text("Loading.....");}
else {
List<DropdownMenuItem> currencyItems = [];
for (int i = 0; i < snapshot.data.documents.length; i++) {
DocumentSnapshot snap = snapshot.data.documents[i];
currencyItems.add(
DropdownMenuItem(
child: Text(
snap.documentID,
style: TextStyle(color: Color(0xff11b719)),
),
value: "${snap.documentID}",
),
);
}
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(Icons.mail,
size: 25.0, color: Color(0xff11b719)),
SizedBox(width: 50.0),
DropdownButton(
items: currencyItems,
onChanged: (currencyValue) {
final snackBar = SnackBar(
content: Text(
'Selected Currency value is $currencyValue',
style: TextStyle(color: Color(0xff11b719)),
),
);
Scaffold.of(context).showSnackBar(snackBar);
setState(() {
selectedCurrency = currencyValue;
});
},
value: selectedCurrency,
isExpanded: false,
hint: new Text(
"Choose Currency Type",
style: TextStyle(color: Color(0xff11b719)),
),
),
],
);
}
}),
You need to add a return before the Text widget in the !snapshot.hasData section of the StreamBuilder
if (!snapshot.hasData){
print('test phrase');
return Text("Loading.....");
}

Flutter: Retrieve associated object from Future in FutureBuilder widget

I am fetching the user 'event manager id' data coming from a future of the object 'event'. I would like now to fetch a user using that id to display his name next the event. However, my FutureBuilder widget only takes into account one future (Event) and I am not able to retrieve that user's name based on that event since my fetchUser method will only return Future objects.
Any help is greatly appreciated.
Here's the FutureBuilder widget:
body: new FutureBuilder(
future: events,
builder: (BuildContext context, AsyncSnapshot<List> snapshot) {
List<Event> availableEvents = snapshot.data;
if (!snapshot.hasData) return CircularProgressIndicator();
return new ListView.builder(
scrollDirection: Axis.vertical,
padding: new EdgeInsets.all(6.0),
itemCount: availableEvents.length,
itemBuilder: (BuildContext context, int index) {
user = fetchUserbyId( // Here, user is of type Future<user> and I cannot retrieve info such as the name of that user
(availableEvents[index].managerId).toString());
return new Container(
margin: new EdgeInsets.only(bottom: 6.0),
padding: new EdgeInsets.all(6.0),
color: Colors.white,
child: Column(
children: <Widget>[
new Text('${availableEvents[index].name}',
style: TextStyle(
fontWeight: FontWeight.bold,
height: _height,
fontSize: 18)),
new Text('${availableEvents[index].description}',
style: TextStyle(height: _height)),
new Text('${availableEvents[index].address}',
style: TextStyle(height: _height)),
new Text('${availableEvents[index].datetime}',
style: TextStyle(height: _height)),
//new Text('${availableEvents[index].managerId}', style: TextStyle(height: _height)),
new FlatButton(
onPressed: null,
// Simply call joinEvent for event 'availableEvents[index]'
color: Colors.redAccent,
textColor: Colors.white,
disabledColor: Colors.red,
disabledTextColor: Colors.white,
padding: EdgeInsets.all(8.0),
splashColor: Colors.redAccent,
child: Text('Join!'),
)
],
));
},
);
}));
Here is the fetchUserByID method:
Future<User> fetchUserbyId(String id) async {
final response =
await http.get('https://url-here.com' + id);
//print("response : " + response.body);
if (response.statusCode == 200) {
// If the call to the server was successful, parse the JSON.
return User.fromJson(json.decode(response.body));
} else {
// If that call was not successful, throw an error.
throw Exception('Failed to load post');
}
}
If what I'm understanding is you have two asynchronous calls, where the second one needs the results of the first call to execute. The best way to go around this is to create a helper method, i.e. getData(). In this method you make your call to events and then use that to fetchUserbyId. This would result in your FutureBuilder looking something like this:
FutureBuilder(
future: getData()
builder: ... // get the results the same why you got your results from events in the given example.
);
Then in you getData() method it would look something like this:
Future<User> getData() async {
var availableEvents= await events; // not sure what your events data/method is
return fetchUserbyId((availableEvents[index].managerId).toString());
}
I think I answered your question, but if I missed it please comment.
Note: On a completely unrelated topic, you don't need the new keyword in Flutter anymore to instantiate objects. Hope that speeds up your development process!

Another exception was thrown: FormatException: Invalid number (at character 1)

Why does the error Another exception was thrown: FormatException: Invalid number (at character 1) occur on my screen for a few microseconds before all is back to normal. Sometimes it doesn't even occur. Below is my StreamBuilder function:
_delivered() {
print('In the delivered function:${resId},${customerId}, ');
return StreamBuilder<QuerySnapshot>(
stream: Firestore.instance
.collection('restaurants')
.document(resId)
.collection('customers')
.document(customer)
.collection('orders')
.where('deliveryTime', isGreaterThan: '')
.snapshots(),
builder: (context, snapshot) {
print('Does snapshop have data? ${snapshot.hasData}');
if (!snapshot.hasData) return Container();
List deliveredListFromServer = snapshot.data.documents;
return Expanded(
child: ListView(
shrinkWrap: true,
children: deliveredListFromServer.map((item) {
print('document id: ${item.documentID}');
return InkWell(
child: SizedBox(
height: 50,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
SizedBox(
width: 80,
child: Text(
item['orderBy']['username'],
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontWeight: FontWeight.bold),
),
),
SizedBox(
width: 5,
),
Expanded(
child: ListView(
scrollDirection: Axis.horizontal,
children: item['order'].map<Widget>((item) {
return SizedBox(
width: 80,
child: Align(
alignment: Alignment.centerLeft,
child: Text(
'${item['qty']} ${item['drinkName']}',
overflow: TextOverflow.ellipsis,
),
),
);
}).toList(),
), //
),
SizedBox(
width: 5,
),
SizedBox(
width: 60,
child: Text(DateFormat('h:mm a').format(
DateTime.fromMillisecondsSinceEpoch(
int.parse(item['deliveryTime'])))),
)
],
),
),
onTap: () {
_deliveredDetail(item);
},
);
}).toList(),
),
);
});
}
This is my console:
I/flutter (11506): In the delivered function:XufIsxA8a24lLhO6gTr1,zMrQmcoQwci9bVVRo6tx,
I/flutter (11506): Does snapshop have data? true
I/flutter (11506): document id: 1579534059562
I/flutter (11506): document id: 1579595374166
I/flutter (11506): Another exception was thrown: FormatException: Invalid number (at character 1)
I/flutter (11506): Does snapshop have data? true
I/flutter (11506): document id: 1579534059562
From the console, I don't even understand why it's bringing document id: 1579595374166 from the database. Only document id: 1579534059562 has a deliveryTime set. The database has 6 records, only one has a deliveryTime set. Others are empty "" strings.
So after a few milliseconds, everything works as expected i.e. the proper UI with only one item for the database showing on the screen. It looks like everything is back to normal the second time the stream returns only one document. In fact, the only time it doesn't bring the red screen is when the console looks like this:
I/flutter (11506): In the delivered function:XufIsxA8a24lLhO6gTr1,zMrQmcoQwci9bVVRo6tx,
I/flutter (11506): Does snapshop have data? false
I/flutter (11506): Does snapshop have data? true
I/flutter (11506): document id: 1579534059562
This also means the streamBuilder is passing incorrect data to the list (and probable source of error). Why is the query returning the wrong results sometimes?!
This error occurs when you are fetching a data which is null, I encountered the same issue and was able to solve it by removing that null data from my firestore database.
I would suggest you check the data in the collection from where you are fetching the list, one of the fields must be null there.
My answer may not be applicable to this instance, but I have also got the same error "Invalid number (at character 1)", the place where I get the error points me to where I have used the variable name of my TextEditingController
The problem with my case was that I have already used a TextEditingController with the same name (also not as a private variable) in another point of my application and have not disposed it after using.
After I disposed all of my TextEditingControllers my problem got solved !
if an error occurs in the parameter, you can add an isEmpty condition for your parameter value.
ex :
// define currency
final _controller = TextEditingController(); static const _locale = 'id'; static const _symbol = 'Rp. '; String _formatNumber(String s) =>
NumberFormat.decimalPattern(
_locale,
).format(s.isEmpty ? 0 : int.parse(s)); String get _currency =>
NumberFormat.compactSimpleCurrency( locale: _locale, name: _symbol).currencySymbol;
//textfield
TextfieldWidget( prefixText: Text(_currency, style: Theme.of(context).textTheme.subtitle1),
controller: _controller,
onChanged: (string) {
string =
'${_formatNumber(string.replaceAll(',', ''))}';
_controller.value = TextEditingValue(
text: string,
selection: TextSelection.collapsed(
offset: string.length ?? null),
);
}, ),
Ensure that you check that the value is not equal to "" or null
if (value != "")