The getter 'iterator' was called on null. Flutter & FireBase - firebase

i have a problem and dont know how to solve... I want to get data through a stream from Firebase
i have UserData in FireBase and now want to get in another script by using a Stream of this UserData(Cutom Class) but the stream is throwing a error. I already proofed the basic lines, if i am using an iterator on null. But i think i dont. There has to be something wrong with the provider. Here is the error :
════════ Exception caught by provider ══════════════════════════════════════════════════════════════
The following assertion was thrown:
An exception was throw by _MapStream<DocumentSnapshot, UserData> listened by
StreamProvider<UserData>, but no `catchError` was provided.
Exception:
NoSuchMethodError: The getter 'iterator' was called on null.
Receiver: null
Tried calling: iterator
════════════════════════════════════════════════════════════════════════════════════════════════════
This is the basic stream:
final String uid;
DatabaseService({this.uid});
final CollectionReference userCollection = Firestore.instance.collection("user");
Stream<UserData> get userData{
if(userCollection.document(uid).snapshots() != null){
return userCollection.document(uid).snapshots().map(_userDataFromSnapshot);
}
else{
return null;
}
}
UserData _userDataFromSnapshot(DocumentSnapshot snapshot){
List<Map<String, dynamic>> daysMaps = List<Map<String, dynamic>>.from(snapshot.data["days"]);
List<Day> days = [];
//List<dynamic> _daysMaps = snapshot.data["days"];
if(daysMaps.length > 1){
days = daysMaps.map((day) => Day.fromMap(day)).toList();
}
else{
days.add(Day.fromMap(daysMaps[0]));
}
Map<String,dynamic> todayMap = Map<String,dynamic>.from(snapshot.data["today"]);
Day today = Day.fromMap(todayMap);
return UserData(
uid: uid,
name: snapshot.data["name"],
days: days,
today: today,
);
}
and this is where i make the StreamProvider (the user stream above is another one):
class Wrapper extends StatelessWidget {
#override
Widget build(BuildContext context) {
final user = Provider.of<User>(context);
if(user == null){
return Authenticate();
}
else{
return StreamProvider<UserData>.value(
value: DatabaseService(uid: user.uid).userData,
child: Home()
);
}
}
}
i dont know if in here is the error but this is the Home Widget:
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
int _currentIndex = 1;
#override
Widget build(BuildContext context) {
//getting other streams
final userdata = Provider.of<UserData>(context);
final user = Provider.of<User>(context);
print(userdata);
final AuthService _auth = AuthService();
//_auth.signOut();
List<dynamic> tabs = [
//TrainingTab
Center(child: Text("Coming Soon")),
//HomeTab
Padding(
padding: const EdgeInsets.all(10.0),
child: Column(
children: <Widget>[
DayStats(),
DayOverview(),
],
),
),
//
Center(child: FloatingActionButton(
onPressed: (){
DatabaseService(uid: user.uid).updateUserData([], Day(
burnedCalories: 300,
targetCalories: 0,
foodCalories: 0,
date: DateTime(DateTime.now().year,DateTime.now().month,DateTime.now().day,0,0,0,0,0)));
},
))
];
return userdata != null ? Scaffold(
backgroundColor: Color.fromRGBO(193, 214, 233, 1),
appBar: AppBar(
title: Text("MyApp"),
centerTitle: true,
elevation: 0.0,
actions: <Widget>[
FlatButton.icon(
onPressed: () async {
await _auth.signOut();
Navigator.pushReplacementNamed(context, "/Wrapper");
},
icon: Icon(Icons.person),
label: Text("logout")
)
],
),
body: tabs[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
backgroundColor: Colors.white,
currentIndex: _currentIndex,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.fitness_center),
title: Text("Workout")
),
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text("Home")
),
BottomNavigationBarItem(
icon: Icon(Icons.fastfood),
title: Text("Ernährung")
)
],
onTap: (index) {
setState(() {
_currentIndex = index;
});
},
),
) : Loading();
}
}

In my case i forgot to check Whether item in a list is null or not. Below code help me to recover from same error.
if (filterRestaurantList[index].category != null) {
for (var category
in filterRestaurantList[index].category) {
if (category.categoryDetail != null) {
categoryList.add(category.categoryDetail.categoryName);
}
}

I used List.from, without checking for null.

Related

Why calling an async function whose defination is given in another program returns null or Future<type> instance for the first time as output?

Hello Im very to the flutter framework, so please let me know if im going wrong anywhere and the appropriate way of doing the things.
this is a drawerPage.dar file
In this file im trying to call a function getData for retrieving the data from firebase,this fucntion is in Database.dart file.
Database.dart
In the Database.dart file i wrote the getData function inside which im retrieving a particular record from the firebase and storing in a global variable. And then im trying to print the global variable in the drawerPage.dart file.But here when ever i run the program, for the first time the variable is having a null value and upon hot reload the actual value is getting stored in the variable.Please let me know how can i get rid of this problem.
output
drawerPageOutput
drawerPage.dart
import 'package:attendee/constants.dart';
import 'package:attendee/models/userdeails.dart';
import 'package:attendee/pages/profile.dart';
import 'package:attendee/services/authentication_service.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:attendee/services/database.dart';
import 'package:provider/provider.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:attendee/pages/userdetails.dart';
class StudentDashboard extends StatefulWidget {
#override
_StudentDashboardState createState() => _StudentDashboardState();
}
class _StudentDashboardState extends State<StudentDashboard> {
userdetails userdetail;
final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
final AuthenticationService _auth = AuthenticationService();
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
DatabaseService().getData('email');
final drawerHeader = UserAccountsDrawerHeader(
accountName: Text(userName),
accountEmail: Text('${result}'),
currentAccountPicture
: CircleAvatar(
child: FlutterLogo(size: 42.0),
backgroundColor: Colors.white,
);
final drawerItems = ListView(
children: <Widget>[
drawerHeader,
ListTile(
title: Row(
children: <Widget>[
Icon(Icons.perm_identity_outlined),
Text(' Profile'),
],
),
onTap: () => Navigator.of(context).push(MaterialPageRoute(builder: (context)=>Profile())),
),
ListTile(
title: Text('To page 2'),
onTap: () => Navigator.of(context).push(_NewPage(2)),
),
ListTile(
title:Row(
children: <Widget>[
Icon(Icons.exit_to_app_rounded),
Text(' Logout'),
],
),
onTap: () async {
await _auth.signOut();
Navigator.of(context).pushNamed('/homepage');
},
),
],
);
return StreamProvider<List<userdetails>>.value(
value: DatabaseService().students,
initialData: [],
child: SafeArea(
child: Scaffold(
appBar: AppBar(
backgroundColor: Colors.lightGreen,
title: Text('Student Welcome'),
actions: <Widget>[
TextButton.icon(
onPressed: () async {
await _auth.signOut();
Navigator.of(context).pushNamed('/homepage');
},
icon: Icon(Icons.person),
label: Text('Logout'))
],
),
body:
UserDetails(),
drawer: GestureDetector(
onTap: display,
child: Drawer(
child: drawerItems,
),
),
),
),
);
}
display() async{
await DatabaseService().getData('email');
}
}
// <Null> means this route returns nothing.
class _NewPage extends MaterialPageRoute<Null> {
_NewPage(int id)
: super(builder: (BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Page $id'),
elevation: 1.0,
),
body: Center(
child: Text('Page $id'),
),
);
});
}
database.dart
import 'package:attendee/models/userdeails.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:flutter_icons/flutter_icons.dart';
import '../constants.dart';
class DatabaseService{
final String uid;
DatabaseService({this.uid});
//collection reference
final CollectionReference user_details=FirebaseFirestore.instance.collection('users');`
final CollectionReference tutor_details` `=FirebaseFirestore.instance.collection("tutors");`
Future updateStudentData(String fullname,String mobilenumber,String `email,String rollno,String tutorid,String role) async {`
return await user_details.doc(uid).set({
'fullname' : fullname,
'mobilenumber': mobilenumber,
'email' : email,
'rollno': rollno,
'tutorid': tutorid,
'role' : role,//FMERT series
});
}
Future updateTutorData(String fullname,String mobilenumber,String `email,String rollno,String tutorid,String role) async {`
return await tutor_details.doc(uid).set({
'fullname' : fullname,
'mobilenumber': mobilenumber,
'email' : email,
'rollno': rollno,
'tutorid': tutorid,
'role' : role,//FMERT series
});
}
//studentDetails from snapshot
List<userdetails> _studentDetailsFromSnapshot(QuerySnapshot snapshot){
return snapshot.docs.map((doc){
return userdetails(
fullname: doc.data()['fullname'] ?? '',
mobilenumber: doc.data()['mobilenumber'] ?? '',
email: doc.data()['email'] ?? '',
rollno: doc.data()['rollno'] ?? '',
tutorid: doc.data()['tutorid'] ?? '',
//role: doc.data()['role'] ?? '',
);
}).toList();
}
//get students stream
Stream<List<userdetails>> get students {
return user_details.snapshots()
.map(_studentDetailsFromSnapshot);
}
//tutorsDetails from snapshot
List<userdetails> _tutorDetailsFromSnapshot(QuerySnapshot snapshot){
return snapshot.docs.map((doc){
return userdetails(
fullname: doc.data()['fullname'] ?? '',
mobilenumber: doc.data()['mobilenumber'] ?? '',
email: doc.data()['email'] ?? '',
rollno: doc.data()['rollno'] ?? '',
tutorid: doc.data()['tutorid'] ?? '',
);
}).toList();
}
//get tutors stream
Stream<List<userdetails>> get tutors {
return user_details.snapshots()
.map(_studentDetailsFromSnapshot);
}
void display() {
tutor_details.get().then((querySnapshot) {
querySnapshot.docs.forEach((result) {
print(result.data());
});
});
}
getData (String string) async{
String userId = await FirebaseAuth.instance.currentUser.uid;
final document = isTutor ? `FirebaseFirestore.instance.doc('tutors/$userId') :`
await FirebaseFirestore.instance.doc('users/$userId');
document.get().then((DocumentSnapshot) async {
if(string =='role') {
checkRole = DocumentSnapshot.data()[string].toString();
print('$checkRole inside getData Function');
//return checkRole;
print(checkRole);
}
else {
print(result);
result = await DocumentSnapshot.data()[string].toString();
print('${DocumentSnapshot.data()[string].toString()} in the `database else block');`
//return result;
}
//print(document("name"));
});
}
}
After changes
terminaloutput
draweroutput
""when ever i run the program, for the first time the variable is having a null value and upon hot reload the actual value is getting stored in the variable""
When we try to get data from http / https request, it takes some time. Meanwhile the page gets loaded and you get null values.
You can use Provider package to resolve this issue, or try the below code. Please add the below code in your drawerPage.dart.
What I have done below is made getData() return type. Only on receiving a value from this function, _loadOnce will change to false & final screen will be shown.
Database.dart
Future<bool> getData (String string) async{
String userId = await FirebaseAuth.instance.currentUser.uid;
final document = isTutor ? `FirebaseFirestore.instance.doc('tutors/$userId') :`
await FirebaseFirestore.instance.doc('users/$userId');
document.get().then((DocumentSnapshot) async {
if(string =='role') {
checkRole = DocumentSnapshot.data()[string].toString();
print('$checkRole inside getData Function');
//return checkRole;
print(checkRole);
return true;
}
else {
print(result);
result = await DocumentSnapshot.data()[string].toString();
print('${DocumentSnapshot.data()[string].toString()} in the `database else block');`
//return result;
return false;
}
//print(document("name"));
});
}
}
/// create a new variable.
bool _loadOnce = true;
/// shift your code `DatabaseService().getData('email');`
#override
void didChangeDependencies() {
if(_loadOnce == true) {
DatabaseService().getData('email').then((value) {
if(value == true){
setState(() {
_loadOnce = false;
});
} else {
/// you can write your code here
setState(() {
_loadOnce = false;
});
}
)}
}
super.didChangeDependencies();
}
Below code will show a spinner till the time all the code gets executed and values are retreived.
/// in your main page under Scaffold
body: _loadOnce == true
? Center(
child: CircularProgressIndicator(
backgroundColor: Theme.of(context).primaryColor,
),
)
: UserDetails(),

Exception: Bad state: cannot get a field on a DocumentSnapshotPlatform which does not exist

The method mentioned in this thread https://stackoverflow.com/a/50867881/13153574 I am trying to fetch data from Firestore. But getting the following exception. The 'name' field is a String and 'overview' field is a List of Strings.
Bad state: cannot get a field on a DocumentSnapshotPlatform which does not exist
My code is as below:
import 'package:firebaseAuth/firebaseAuthDemo.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class FindDiseases extends StatefulWidget {
final User user;
const FindDiseases({Key key, this.user}) : super(key: key);
#override
_FindDiseasesState createState() => _FindDiseasesState();
}
class _FindDiseasesState extends State<FindDiseases> {
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
FirebaseAuth _auth = FirebaseAuth.instance;
List diseasesList = [];
//dynamic data;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.teal,
automaticallyImplyLeading: false,
title: Text(
"Diseases List",
),
),
key: _scaffoldKey,
body: Center(
child: FlatButton(
color: Colors.white,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text("get Disease Record"),
StreamBuilder<DiseaseRecord>(
stream: getDisease(),
builder: (BuildContext c, AsyncSnapshot<DiseaseRecord> data) {
if (data?.data == null) return Text("Error");
DiseaseRecord r = data.data;
return Text("${r.name}");
},
),
],
),
onPressed: () {
getDisease();
},
),
),
);
}
Future _signOut() async {
await _auth.signOut();
}
}
Stream<DiseaseRecord> getDisease() {
return FirebaseFirestore.instance.collection("diseases").doc().get().then(
(snapshot) {
try {
return DiseaseRecord.fromSnapshot(snapshot);
} catch (e) {
print(">>> Error:"+e.toString());
return null;
}
},
).asStream();
}
class DiseaseRecord {
String name;
List<String> overview = new List<String>();
DiseaseRecord.fromSnapshot(DocumentSnapshot snapshot)
: name = snapshot['name'],
overview = List.from(snapshot['overview']);
}
Data is something like as below:
name: "name--"
overview: "['a', 'b', 'c']"
The problem is here:
return FirebaseFirestore.instance.collection("diseases").doc().get()
Calling doc() without any arguments creates a reference to a new, non-existing document. Then calling get() on that, returns a DocumentSnapshot for a non-existing document, and trying to get fields from that is an invalid operation.
Most likely you'll need to know the ID of the disease document you're trying to load, and pass that in to the call to doc(id).

onTap method for Flutter to open longitude and latitude stored in Firestore

I am trying to create a search engine for electoral sections, once it finds the electoral
section by clicking on the item it should send me to a longitude and latitude that I have stored
in firestore and display it on Google maps as markers with flutter, but I cannot create the
method, what will be the most efficient way to do this?
class SearchPage extends StatefulWidget {
#override
_SearchPageState createState() => _SearchPageState();
}
class _SearchPageState extends State<SearchPage> {
TextEditingController textEditingController = TextEditingController();
final database = Firestore.instance;
String searchString;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: <Widget>[
Expanded(
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(30.0),
child: Container(
child: TextField(
onChanged: (val) {
setState(() {
searchString = val.toLowerCase();
});
},
controller: textEditingController,
decoration: InputDecoration(
suffixIcon: IconButton(
icon: Icon(Icons.clear),
onPressed: () => textEditingController.clear()),
hintText: 'Buscar seccion',
hintStyle: TextStyle(
fontFamily: 'Antra', color: Colors.blueGrey)),
),
),
),
Expanded(
child: StreamBuilder<QuerySnapshot>(
stream: (searchString == null || searchString.trim() == ' ')
? Firestore.instance.collection('secciones').snapshots()
: Firestore.instance
.collection('secciones')
.where('searchIndex', arrayContains: searchString)
.snapshots(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return Text('We got an error ${snapshot.error}');
}
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Text('Cargando');
case ConnectionState.none:
return Text('Error de conexion');
case ConnectionState.done:
return Text('We are done!');
default:
return new ListView(
children: snapshot.data.documents
.map((DocumentSnapshot document) {
return new ListTile(
title: Text(document['estado']),
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(builder: (context) {
return MapsScreen(
);
}),
);
});
}).toList());
}
},
),
)
],
),
)
],
));
}
}
This is the screen where you should send the position stored in firestore,
but I can't find out how to do it and I took the method from a video
tutorial in which they taught you how to show and store your current
location in Google maps.
class MapsScreen extends StatefulWidget{
final String partyNumber;
final String userId;
const MapsScreen({Key key, this.userId, this.partyNumber}) : super(key: key);
#override
_MapsScreenState createState() => _MapsScreenState();
}
class _MapsScreenState extends State<MapsScreen>{
GoogleMapController _mapController;
Location _location = Location();
StreamSubscription<LocationData> subscription;
#override
void initState(){
super.initState();
_initLocation();
}
_initLocation() async{
var _serviceEnabled = await _location.serviceEnabled();
if(!_serviceEnabled) {
_serviceEnabled = await _location.requestService();
if(!_serviceEnabled){
return;
}
}
var _permissionGranted = await _location.hasPermission();
if(_permissionGranted == PermissionStatus.DENIED){
_permissionGranted = await _location.requestPermission();
if(_permissionGranted != PermissionStatus.GRANTED){
print("Sin permisos de GPS");
return;
}
}
subscription = _location.onLocationChanged().listen((LocationData event) {
if(_mapController != null){
_mapController.animateCamera(
CameraUpdate.newLatLng(
LatLng(event.latitude, event.longitude),
),
);
}
Firestore.instance
.collection('seccion')
.document(widget.partyNumber)
.collection('people')
.document(widget.userId)
.setData({
'lat': event.latitude,
'lng': event.longitude,
});
print("${event.latitude}, ${event.longitude}");
});
}
#override
void dispose(){
if(subscription != null){
subscription.cancel();
}
Firestore.instance
.collection('seccion')
.document(widget.partyNumber)
.collection('people')
.document(widget.userId)
.delete();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Instituto Nacional Electoral"),
),
body: GoogleMap(
initialCameraPosition: CameraPosition(
target: LatLng(16.879860202903764, -99.9013661857768),
zoom: 15,
),
zoomGesturesEnabled: true,
myLocationEnabled: true,
myLocationButtonEnabled: true,
onMapCreated: (controller) => _mapController = controller,
),
);
}
}
I am not quite sure what exactly you are trying to accomplish.
I initially thought you had latitudes and longitudes stored somewhere in Firebase and wanted to display the marker in those locations.
I you wanted to do that, you would need to get the location data from Firebase and pass it into the GoogleMap. I am not familiar with the widget itself, but from the documentation as you can see here: https://github.com/flutter/plugins/blob/f3024731b090659edaa92d01416549c690f65678/packages/google_maps_flutter/google_maps_flutter/lib/src/google_map.dart#L112
the widget accepts a Set of Markers.
If you did a little in the repository you can see how to build a Marker. And then you can construct one or more from the location data in Firebase and pass them to the GoogleMap widget.
If that is what you want to accomplish. The code you posted saves the current user location to Firebase, so I am unsure what exactly your goal is.

null an the Display in Flutter but with the right Value in Future

I am pretty new in Flutter and i can not solve this problem.
i have a really simple application. it is just a login with google an a User is created in Firebase, that user have a counter and a button this button increased the counter (int _user.count++).
Then my problem: after the login in the next window, it is not visible the count variable until I click "plus" button. the variable is right and the query with fireStore work great I got the variable but if I do not click the button I got an the display in the place of the variable "null".
Thanks a lot for you Help and Time, I really hope that you can help me. maybe it is a tiny problem I have not found information about it but it is happen when some start to learn.
MyDAO: Hier the Method Future it is the responsable of make the Query to FireStore.
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:gauth/user_model.dart';
import 'package:rxdart/rxdart.dart';
final UserDAO userDAO = UserDAO();
class UserDAO {
final Firestore _db = Firestore.instance;
PublishSubject loading;
Observable<Future<QuerySnapshot>> profile;
void updateData(UserModel user) async {
DocumentReference documentReference =
_db.collection("users").document(user.uid);
return documentReference.setData({
"uid": user.uid,
"userName": user.name,
"email": user.email,
"photoUrl": user.photoUrl,
"count": user.count,
"lastIn": DateTime.now()
}, merge: true);
}
Future<QuerySnapshot> readDateFutur(String email) async {
// loading.add(true);
QuerySnapshot querySnapshot = await (_db
.collection("users")
.where("email", isEqualTo: email)
.getDocuments());
// loading.add(false);
return querySnapshot;
}
}
hier in the method "void initState()" I hold the variable _user.couner, that works.
class PlusCounter extends StatefulWidget {
UserModel user;
PlusCounter(this.user);
#override
_PlusCounterState createState() => _PlusCounterState();
}
class _PlusCounterState extends State<PlusCounter> {
UserModel _user;
PublishSubject loading;
#override
void initState() {
// TODO: implement initState
super.initState();
setState(() {
_user = widget.user;
//loading.add(false);
userDAO.readDateFutur(_user.email).then((QuerySnapshot docs) {
if (docs.documents.isNotEmpty) {
print("::::::::::NOESTOY VACIO:::::::::::::::::::::");
print(docs.documents.last.data["count"]);
if (docs.documents.last.data["count"] != null) {
_user.count = docs.documents.last.data["count"];
} else {
_user.count = 0;
}
} else {
print(":::::::::::ESTOY VACIO:::::::::::::::::");
_user.count = 0;
}
});
});
}
void _plus() {
setState(() {
_user.count++;
});
}
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Text("Cuantas veces te has \n lavado las manos:"),
Text("${_user.count}"),
MaterialButton(
onPressed: () {
_plus();
},
child: Text("Plus"),
textColor: Colors.white,
color: Colors.blue,
),
MaterialButton(
onPressed: () => userDAO.updateData(_user),
child: Text("Guardar"),
textColor: Colors.white,
color: Colors.blue,
),
],
);
}
}
WelcomePage code is this one.
class userDataWelcome extends StatelessWidget {
UserModel _userModel;
userDataWelcome(this._userModel);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Welcome"),
),
body: Center(
child: Column(
children: <Widget>[
Center(
child: Container(
height: 100.0,
width: 100.0,
decoration: BoxDecoration(
shape: BoxShape.circle,
image: DecorationImage(
image: NetworkImage(_userModel.photoUrl),
),
),
),
),
Text("${_userModel.name}"),
PlusCounter(_userModel),
MaterialButton(
onPressed: () => authService.SingOut(),
child: Text("Logout"),
textColor: Colors.white,
color: Colors.deepOrange,
)
],
),
),
);
}
}
Then I really do not why I need to click "plus" button before I can see the Value of _user.count, because I just see null in otherwise. just again I want to say Thanks for your help.
Try wrapping this line in initStat() _user.count = docs.documents.last.data["count"]; in setState((){}); like this
setState((){
_user.count = docs.documents.last.data["count"];
)};

Firebase Query not working with Flutter, How to solve this?

I am trying to add a search functionality to my Flutter App, Everything works fine in the code, no errors are shown but when I search for username on my app screen no results are showing but sometime it throws an error saying "failed assertion: line 269 pos 10: 'data != null' The relevant error-causing widget was FutureBuilder" Especially when I use capital letter and has an empty space in the text.
I have attached the code for search.dart and user.dart file. Please help me out, I'm stuck here.
search.dart File
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:social_share/models/user.dart';
import 'package:social_share/pages/home.dart';
import 'package:social_share/widgets/progress.dart';
class Search extends StatefulWidget {
#override
_SearchState createState() => _SearchState();
}
class _SearchState extends State<Search> {
TextEditingController searchController = TextEditingController();
Future<QuerySnapshot> searchResultsFuture;
handleSearch(String query) {
Future<QuerySnapshot> users = usersRef
.where("displayName", isGreaterThanOrEqualTo: query)
.getDocuments();
setState(() {
searchResultsFuture = users;
});
}
clearSearch() {
searchController.clear();
}
AppBar buildSearchField() {
return AppBar(
backgroundColor: Colors.white,
title: TextFormField(
controller: searchController,
decoration: InputDecoration(
hintText: "Search for a user...",
filled: true,
prefixIcon: Icon(
Icons.account_box,
size: 28.0,
),
suffixIcon: IconButton(
icon: Icon(Icons.clear),
onPressed: clearSearch,
),
),
onFieldSubmitted: handleSearch,
),
);
}
Container buildNoContent() {
final Orientation orientation = MediaQuery.of(context).orientation;
return Container(
child: Center(
child: ListView(
shrinkWrap: true,
children: <Widget>[
SvgPicture.asset(
'assets/images/search.svg',
height: orientation == Orientation.portrait ? 300.0 : 200.0,
),
Text(
"Find Users",
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontStyle: FontStyle.italic,
fontWeight: FontWeight.w600,
fontSize: 60.0,
),
),
],
),
),
);
}
buildSearchResults() {
return FutureBuilder(
future: searchResultsFuture,
builder: (context, snapshot) {
if (!snapshot.hasData) {
return circularProgress();
}
List<Text> searchResults = [];
snapshot.data.documents.forEach((doc) {
User user = User.fromDocument(doc);
searchResults.add(Text(user.username , style: TextStyle(color: Colors.black, fontSize: 20.0),));
});
return ListView(
children: searchResults,
);
},
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Theme.of(context).primaryColor.withOpacity(0.8),
appBar: buildSearchField(),
body:
searchResultsFuture == null ? buildNoContent() : buildSearchResults(),
);
}
}
class UserResult extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Text("User Result");
}
}
user.dart File
import 'package:cloud_firestore/cloud_firestore.dart';
class User {
final String id;
final String username;
final String email;
final String photoUrl;
final String displayName;
final String bio;
//Constructor
User({
this.id,
this.username,
this.email,
this.photoUrl,
this.displayName,
this.bio,
});
factory User.fromDocument(DocumentSnapshot doc){
return User(
id: doc['id'],
email: doc['email'],
username: doc['username'],
photoUrl: doc['photoUrl'],
displayName: doc['displayName'],
bio: doc['bio'],
);
}
}
Firebase Database Data ScreenShots
Firebase Users Data Screenshot
if looks like if the actual data is null you would get an error.
try:
if(!snapshot.hasData || snapshot.data?.documents == null)
instead of:
if (!snapshot.hasData)
reference - https://github.com/flutter/flutter/issues/22199
Solved
After I changed the if statement to the following line it's working.
if(!snapshot.hasData || snapshot.data.documents == null)

Resources