The getter 'uid' was called on null error appears between screens - firebase

I am getting an error
The getter 'uid' was called on null.
Receiver: null
Tried calling: uid
this is the code I am using, the problem is auth.currentUser.uid which is returning null when first called, from what I understand the auth.currentUser is null at first then it return the current user
but I don't get how to handle this situation
the error show on the emulator when between the login screen and home screen after I log in, it appear for a second then it's gone and the home screen appears normally
class HomePage extends StatelessWidget {
final FirebaseAuth auth = FirebaseAuth.instance;
static int count;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: [
GestureDetector(
child: Icon(Icons.logout),
onTap: () async {
await auth.signOut();
Navigator.of(context).push(MaterialPageRoute(builder: (context) {
return LoginPage();
}));
},
),
],
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
print('add button pressed');
Navigator.of(context).push(MaterialPageRoute(builder: (context) {
return NewNote();
}));
},
),
body: SafeArea(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Notes',
style: TextStyle(
fontSize: 18,
),
),
// Text(count != null ? count.toString() : '0'),
NotesCount(
auth1: auth,
),
Text(auth.currentUser.uid != null ? auth.currentUser.uid : 'No user id'),
SizedBox(
height: 20,
),
NotesGrid(
auth1: auth,
),
],
),
),
),
);
}
}
class NotesCount extends StatelessWidget {
final FirebaseAuth auth1;
NotesCount({this.auth1});
#override
Widget build(BuildContext context) {
CollectionReference notes = FirebaseFirestore.instance.collection('users').doc(auth1.currentUser.uid).collection('notes');
return StreamBuilder<QuerySnapshot>(
stream: notes.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 Text(snapshot.data.docs.length.toString());
},
);
}
}
class NotesGrid extends StatelessWidget {
final FirebaseAuth auth1;
NotesGrid({this.auth1});
#override
Widget build(BuildContext context) {
CollectionReference notes = FirebaseFirestore.instance.collection('users').doc(auth1.currentUser.uid).collection('notes');
return StreamBuilder(
stream: notes.snapshots(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (!snapshot.hasData) {
return Center(child: const Text('Loading events...'));
}
if (snapshot.hasError) {
return Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Text("Loading");
}
return GridView.builder(
shrinkWrap: true,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2),
itemBuilder: (BuildContext context, int index) {
return NoteCard(
note: Note(
title: snapshot.data.documents[index]['title'],
content: snapshot.data.documents[index]['content'],
datetime: snapshot.data.documents[index]['datetime'].toDate(),
id: snapshot.data.documents[index].documentID,
),
);
},
itemCount: snapshot.data.documents.length,
);
},
);
}
}

It's not a good idea to use currentUser as your primary way to find the signed in user account. It will always be null when the app is first launched. The previously signed in user object doesn't become available until some time later. It's better to follow the instructions in the documentation and set up an auth state listener so you can find out when the user object first becomes available.
FirebaseAuth.instance
.authStateChanges()
.listen((User user) {
if (user == null) {
print('User is currently signed out!');
} else {
print('User is signed in!');
}
});
Use this listener to respond to changes in auth state and trigger an update of your UI accordingly.

I tried adding if else before accessing to current user and the error is gone and the user data shows normally after loading it
Text(auth.currentUser != null ? auth.currentUser.uid : 'Loading...')

Related

Exception has occurred. _CastError (Null check operator used on a null value)

I have problem with this code,
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
class HomePage extends StatefulWidget {
const HomePage({ Key? key }) : super(key: key);
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
FirebaseFirestore firestore = FirebaseFirestore.instance;
CollectionReference users = FirebaseFirestore.instance.collection('users');
Stream<QuerySnapshot<Map<String, dynamic>>> collectionStream = FirebaseFirestore.instance.collection('users').snapshots();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Home Page'),
),
body: StreamBuilder<QuerySnapshot<Map<String, dynamic>>>(
stream: collectionStream,
builder: (context, snapshot) {
return Container(
child: ListView.builder(
itemCount: snapshot.data!.docs.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(snapshot.data!.docs[index].data()['name']),
trailing: IconButton(
icon: const Icon(Icons.delete),
onPressed: () {
users.doc(snapshot.data!.docs[index].id).delete();
},
),
);
}
),
);
},
),
);
}
}
This line that makes this program error, operator (!):
snapshot.data!.docs.length
snapshot.data!.docs[index].data()['name']
snapshot.data!.docs[index].id
initially this code does not error, but when I rerun it appears : Exception has occurred. _CastError (Null check operator used on a null value). I've tried to fix it but still failed. Is there a way to solve this problem ?
This error means that the snapshot.data is null.
And you're using the null-check operator on it in the line snapshot.data!.
Solution:
You need to check if the data is null and display something like a loading screen while the app waits for snapshot.data to have a value like this:
body: StreamBuilder<QuerySnapshot<Map<String, dynamic>>>(
stream: collectionStream,
builder: (context, snapshot) {
if (snapshot.data == null) {
return Center(child: CircularProgressIndicator());
}
return Container(
child: ListView.builder(
itemCount: snapshot.data.docs.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(snapshot.data.docs[index].data()['name']),
trailing: IconButton(
icon: const Icon(Icons.delete),
onPressed: () {
users.doc(snapshot.data.docs[index].id).delete();
},
),
);
},
),
);
}
)
And since you're checking if snapshot.data is null, you can remove the null-check operator from its usage.
So snapshot.data! in snapshot.data!.docs.length becomes snapshot.data like snapshot.data.docs.length.

It seems that pthread_join() is not invoked or PTHREAD_ATTR_FLAG_DETACHED is not set Flutter Firebase

I've followed a tutorial regarding 'Flutter Firebase Authentication.
I faced many issues due to it being in an old version of firebase, but I migrated and fixed all problems related to the null safety and others. It showed 0 problems/errors.
But on execution, the following error message appears multiple times.
D/libGLESv2(15242): STS_GLApi : DTS is not allowed for Package : com.example.realone
W/libc (15242): It seems that pthread_join() is not invoked or PTHREAD_ATTR_FLAG_DETACHED is not set.
W/libc (15242): pthread tid : 15281
W/libc (15242): pthread start_routine: 0xdeeecce
In the phone, it gives a place where we can write the email and password but when we click on the sign in button it doesn't do anything.
my Sign_in code :
class _LoginPageState extends State<LoginPage> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
late String _email, _password;
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(),
body: Form(
key: _formKey,
child: Column(
children: <Widget>[
TextFormField(
validator: (input) {
if (input!.isEmpty) {
return 'Provide an email';
}
},
decoration: InputDecoration(labelText: 'Email'),
onSaved: (input) => _email = input!,
),
TextFormField(
validator: (input) {
if (input!.length < 6) {
return 'Longer password please';
}
},
decoration: InputDecoration(labelText: 'Password'),
onSaved: (input) => _password = input!,
obscureText: true,
),
ElevatedButton(
onPressed: signIn,
child: Text('Sign in'),
),
],
)),
);
}
void signIn() async {
if (_formKey.currentState!.validate()) {
_formKey.currentState!.save();
try {
User? user = (await FirebaseAuth.instance
.signInWithEmailAndPassword(email: _email, password: _password))
.user;
// print(user);
Navigator.push(context,
MaterialPageRoute(builder: (context) => Home(user: user!)));
} catch (e) {
print(e);
}
}
}
}
my home code (the page where the app goes after signing in)
lass Home extends StatelessWidget {
const Home({Key? key, required this.user}) : super(key: key);
final User user;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Home ${user.email}'),
),
body: StreamBuilder<DocumentSnapshot>(
stream: FirebaseFirestore.instance
.collection('users')
.doc(user.uid)
.snapshots(),
builder:
(BuildContext context, AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else if (snapshot.hasData) {
return checkRole(snapshot.data!);
}
return LinearProgressIndicator();
},
),
);
}
Center checkRole(DocumentSnapshot snapshot) {
if (snapshot.data() == null) {
return Center(
child: Text('no data set in the userId document in firestore'),
);
}
if ((snapshot.data() as dynamic)['role'] == 'admin') {
return adminPage(snapshot);
} else {
return userPage(snapshot);
}
}
Center adminPage(DocumentSnapshot snapshot) {
return Center(
child: Text(
'${(snapshot.data() as dynamic)['role']} ${(snapshot.data() as dynamic)['name']}'));
}
Center userPage(DocumentSnapshot snapshot) {
return Center(child: Text((snapshot.data() as dynamic)['name']));
}
}
I have two other files main.dart and signUp.dart
Would be glad to know the issues in code.
Thanks in advance.

Use Cloud Firestore on Flutter. But I got exception by widget Library

When I read a data from the Cloud Firestore.
I could get the data, but I caught the below exception.
════════ Exception caught by widgets library ═══════════════════════════════════════════════════════
The following NoSuchMethodError was thrown building StreamBuilder(dirty, state: _StreamBuilderBaseState<QuerySnapshot, AsyncSnapshot>#4f115):
The getter 'docs' was called on null.
Receiver: null
Tried calling: docs
I tried some idea on stackoverflow but I couldn't resolve.
Here is my code.
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
class AdminHome extends StatefulWidget {
final String email;
AdminHome({#required this.email});
#override
State<StatefulWidget> createState() {
return _AdminHomeState(this.email);
}
}
class _AdminHomeState extends State<AdminHome> {
final fireStoreInstance = FirebaseFirestore.instance;
String email;
_AdminHomeState(this.email);
#override
Widget build(BuildContext context) {
print("This email is $email");
return Scaffold(
appBar: AppBar(
title: Text("Admin Home Window"),
),
body: StreamBuilder<QuerySnapshot>(
stream: fireStoreInstance.collection(email).snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
return ListView(
children: snapshot.data.docs.map((DocumentSnapshot document) {
if (snapshot.data != null && !snapshot.hasError) {
return Card(
child: ListTile(
title: Text(document.data()["gameName"]),
onTap: () {
print("tapped");
Navigator.pushNamed(context, '/adminGameDetail');
},
),
);
} else if (snapshot.data == null && !snapshot.hasError) {
return Center(child: Text('No data'));
} else {
return Center(
child: Text('Woooops'),
);
}
}).toList(),
);
},
),
);
}
}
Please help it.
snapshot.data is not yet loaded while you are trying to access it. Try this:
StreamBuilder<QuerySnapshot>(
stream: fireStoreInstance.collection(email).snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if(!snapshot.hasData) return Text("Still Loading"); // return whatever widget you want to show while the data is being loaded.
if(snapshot.hasError) return Text("Error"); // Return whatever widget you want to show when an error occurs.
return ListView(
children: snapshot.data.docs.map((DocumentSnapshot document) {
if (snapshot.data != null && !snapshot.hasError) {
return Card(
child: ListTile(
title: Text(document.data()["gameName"]),
...
...
... Rest of the code

Fetching Data from a Realtime Firestore Document

I have seen similar questions and answers being solved with the StreamBuilder widget.
In my case when I am implementing it my code does not await to fetch the data and just moves on (in my case, the app jumps to the next page). Thus, do I need the build a StreamBuilder Widget or is there a simple method that could work and fetch the data in realtime?
I noticed that I did not use async* with the asterisc but if I do so, then the authentication is not working.
Clarification:
The code does not enter the following lines:
if (!snapshot.hasData)
return new Text('Loading...');
return new Text(
snapshot.data.data['name']
);
Also the print(test); statement prints the following:
StreamBuilder<DocumentSnapshot>
Here is the whole part:
onPressed: () async {
setState(() {
showSpinner = true;
});
try {
LoginScreen.user =
await _auth.signInWithEmailAndPassword(
email: email, password: password);
if (LoginScreen.user != null) {
// get the users data and save them
if (LoginScreen.user.user.uid !=
'IDVwQXAsZas213Va0OIH2IsoU5asdaTfraBJ2') {
Widget test = await StreamBuilder<DocumentSnapshot>(
stream: _firestore
.collection('Employees')
.document(LoginScreen.user.user.uid)
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<DocumentSnapshot> snapshot) {
if (!snapshot.hasData)
return new Text('Loading...');
return new Text(
snapshot.data.data['name']
);
},
);
print(test);
Navigator.pushReplacementNamed(
context, TabCreator.screenId);
} else {
}
}
} catch (e) {
print(e);
// when getting an erro stop spinner
setState(() {
showSpinner = false;
});
}
}
Update:
I created a new standard flutter project in order to see if there was something else within my code that was messing the StreamBuilder. I am still getting no output.
On a side note when I am implementing the following code within the onPressed method I am getting the wanted result:
Alternative Solution:
onPressed: () {
DocumentReference documentReference = await Firestore.instance
.collection('Employees')
.document('8nss0gppzNfOBMuRz9H44dv7gSd2');
documentReference.snapshots().listen((datasnapshot) {
if (datasnapshot.exists) {
print(datasnapshot.data['name'].toString());
} else {
print('Error!');
}
});
}
Here is the implemented StreamBuilder implemented in the standard Flutter project:
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final _auth = FirebaseAuth.instance;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'Testing',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
// DocumentReference documentReference = await Firestore.instance
// .collection('Employees')
// .document('8nss0gppzNfOBMuRz9H44dv7gSd2');
// documentReference.snapshots().listen((datasnapshot) {
// if (datasnapshot.exists) {
// print(datasnapshot.data['name'].toString());
// } else {
// print('Error!');
// }
// });
StreamBuilder<DocumentSnapshot>(
stream: Firestore.instance
.collection('Employees')
.document('8nss0gppzNfOBMuRz9H44dv7gSd2')
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.hasError)
return new Text('Error: ${snapshot.error}');
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return new Text('Loading...');
default:
return new Text(snapshot.data.data['name']);
}
},
);
},
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
Change your code to the following:
builder: : (BuildContext context,
AsyncSnapshot<DocumentSnapshot> snapshot) {
if (!snapshot.hasData){
return new Text('Loading...');
}
else{
print(snapshot);
Navigator.pushReplacementNamed(
context, TabCreator.screenId);
}
Add an else block so when you have data it will enter the else and navigate to the page.
Also you need to use the StreamBuilder inside the build method not inside the onPressed function which is used to handle data processing. Example you can do the following:
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
bool visible = false;
final firestoreInstance = Firestore.instance;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
Visibility(
child: StreamBuilder<DocumentSnapshot>(
stream: Firestore.instance
.collection('users')
.document('FIJbBBiplAGorYzdtUQF')
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<DocumentSnapshot> snapshot) {
print(snapshot);
if (snapshot.hasError)
return new Text('Error: ${snapshot.error}');
else if (snapshot.hasData) {
print(snapshot.data.data);
return new Text(snapshot.data.data["age"].toString());
}
return new CircularProgressIndicator();
},
),
visible: visible,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
visible = true;
});
},
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
So here I also use the Visibility() widget to hide it, but when FAB button is clicked the data from firestore will appear.

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