Error in FirebaseAuth while using Provider - firebase

I want to create an entering page with firebase_auth. I can sign in successfully but I can't log in. When I try to log in application doesn't give any error but it isn't pass the main page. Still it is in log in page. When I restart the debugging now it pass the main page. Can you see my code and help me?
Here is my auth service code
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:library_app/models/person.dart';
class FirebaseAuthService {
final FirebaseAuth _auth = FirebaseAuth.instance;
Person? _createPerson(User? user) {
return user == null ? null : Person.fromFirebaseUser(user);
}
Stream<Person?> get statusFollower {
return _auth.authStateChanges().map(_createPerson);
}
void createUserEmailAndPassword({
required String email,
required String password,
}) async {
try {
var _userCredential = await _auth.createUserWithEmailAndPassword(
email: email,
password: password,
);
_createPerson(_userCredential.user);
} catch (e) {
debugPrint(e.toString());
}
}
loginUserEmailAndPassword({
required String email,
required String password,
}) async {
try {
var _userCredential = await _auth.signInWithEmailAndPassword(
email: email,
password: password,
);
_createPerson(_userCredential.user);
} catch (e) {
debugPrint(e.toString());
}
}
void signOut() async {
await _auth.signOut();
}
}
And here is my orientation code
import 'package:flutter/cupertino.dart';
import 'package:library_app/models/person.dart';
import 'package:library_app/pages/error_page.dart';
import 'package:library_app/pages/loading_page.dart';
import 'package:library_app/pages/main_page.dart';
import 'package:library_app/pages/sign_in_page.dart';
import 'package:library_app/services/firebase_auth_service.dart';
import 'package:provider/provider.dart';
class OrientationSystem extends StatelessWidget {
const OrientationSystem({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
var _authService = Provider.of<FirebaseAuthService>(context, listen: false);
return StreamBuilder<Person?>(
stream: _authService.statusFollower,
builder: (context, stream) {
if (stream.connectionState == ConnectionState.waiting) {
return const LoadingPage();
}
if (stream.hasData) {
return const MainPage();
}
if (!stream.hasData) {
return const SigInPage();
} else {
return const ErrorPage();
}
},
);
}
}
what must I do?
please help...

You did not extend or used with ChangeNotifier class in the FirebaseAuthService construction. Also, you need to notifyListener() after doing the tasks or after updating data inside your FirebaseAuthService class so that your other classes may listen to the updated values.
However, please update your question to something like "Error in FirebaseAuth while using Provider". Make your question titles as much relevant as you can otherwise you might get ban by the system. This is a suggestion.

Related

Firebase query Stream with filters not updating when document is added: Flutter

I am working on the chat section of my flutter application. I have a chat_messages collection which contains all of the messages sent by users for all chats in the application. Below is the structure of a chat_messages document:
Here user is the sender of the message. I would like to display the number of unread messages (where the message.seen==false) for a specific user hence i used the query below to get a stram of all messages which where not seen by the user and i listen to that stream for any new messages sent:
unreadMessagesStream = queryChatMessagesRecord(queryBuilder: (query)
{
return query
.where('chat_users', arrayContains: currentUserReference)
.where('user', isNotEqualTo: currentUserReference)
.where('seen', isEqualTo: false);
});
unreadMessagesStream.listen((msgs) {
if (mounted)
setState(() {
unreadMessagesCount = msgs?.length ?? 0;
});
});
Unfortunately, this stream only produces a value once when the app is run, but later on when any new message is sent, new values are not received in the stream and the number of unread messages remain the same.
NB: If I remove the filters and query the whole collection is works perfectly fine.
I give you a snippet of my code to get it done faster: The code juste below is for each user. It's a subcollection of the chat. So a user could have a chatMembre for each chat.
import 'package:cloud_firestore/cloud_firestore.dart';
enum IsDoing { reading, notReading, writing, recording }
class ChatMembre {
final String id;
final DateTime lastReading;
final DateTime lastReceived;
final IsDoing isDoing;
final bool hasSubscribeToTopic;
ChatMembre(
{required this.id,
required this.lastReading,
required this.lastReceived,
required this.isDoing,
required this.hasSubscribeToTopic});
Map<String, dynamic> toMap() {
return {
'id': id,
'lastReading': lastReading == DateTime.now()
? FieldValue.serverTimestamp()
: DateTime.now(),
'lastReceived': lastReceived == DateTime.now()
? FieldValue.serverTimestamp()
: DateTime.now(),
'isDoing':
isDoing.toString().substring(isDoing.toString().indexOf(".") + 1),
'isSubscribeToTopic': hasSubscribeToTopic
};
}
factory ChatMembre.fromMap(Map<String, dynamic>? map) {
if (map == null || map.isEmpty) {
return ChatMembre(
id: '',
lastReading: DateTime.now(),
lastReceived: DateTime.now(),
hasSubscribeToTopic: false,
isDoing: IsDoing.notReading);
}
IsDoing isDoing;
switch (map["isDoing"]) {
case "reading":
isDoing = IsDoing.reading;
break;
case "writing":
isDoing = IsDoing.writing;
break;
case "recording":
isDoing = IsDoing.recording;
break;
default:
isDoing = IsDoing.notReading;
break;
}
return ChatMembre(
id: (map['id'] ?? '') as String,
lastReading:
((map['lastReading'] ?? Timestamp.now()) as Timestamp).toDate(),
lastReceived:
((map['lastReceived'] ?? Timestamp.now()) as Timestamp).toDate(),
isDoing: isDoing,
hasSubscribeToTopic: (map['isSubscribeToTopic'] ?? false) as bool);
}
#override
String toString() {
return 'ChatMembre{id: $id, lastReading: $lastReading, lastReceived: $lastReceived, isDoing: $isDoing, hasSubscribeToTopic: $hasSubscribeToTopic}';
}
}
And under it's to look for the state of the chat page.
import 'dart:convert';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:customer/constants/credentials.dart';
import 'package:customer/constants/firestore_path.dart';
import 'package:customer/domain/repositories/my_chat_repository.dart';
import 'package:customer/services/firestore_service.dart';
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:http/http.dart' as http;
class ChatRoomLifeCycle extends StatefulWidget {
final Widget child;
final MyChatRepository chatRepo;
final String chatId;
final String? token;
final String? idTo;
final Timestamp? lastReceivedOfFriend;
const ChatRoomLifeCycle(
{Key? key,
required this.chatId,
required this.chatRepo,
required this.child,
this.token,
this.idTo,
this.lastReceivedOfFriend})
: super(key: key);
#override
_ChatRoomLifeCycleState createState() => _ChatRoomLifeCycleState();
}
class _ChatRoomLifeCycleState extends State<ChatRoomLifeCycle>
with WidgetsBindingObserver {
late GlobalKey<AnimatedListState> listKey;
bool hasSentFcm = false;
#override
void initState() {
super.initState();
sendPushMessage();
WidgetsBinding.instance!.addObserver(this);
widget.chatRepo.setIsReading();
}
#override
void dispose() {
widget.chatRepo.setIsNotReading(isFromDispose: true);
WidgetsBinding.instance!.removeObserver(this);
super.dispose();
}
#override
Widget build(BuildContext context) {
return widget.child;
}
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
switch (state) {
case AppLifecycleState.paused:
widget.chatRepo.setIsNotReading();
break;
case AppLifecycleState.resumed:
widget.chatRepo.setIsReading();
break;
case AppLifecycleState.inactive:
widget.chatRepo.setIsNotReading();
break;
case AppLifecycleState.detached:
widget.chatRepo.setIsNotReading();
break;
}
}
Future<void> sendPushMessage() async {
if (hasSentFcm || widget.idTo == null || widget.token == null) {
return;
}
FirestoreService.instance.updateData(
path: MyPath.myUserStatus(uid: widget.idTo!), data: {'isLogin': false});
try {
await http
.post(
Uri.parse('https://fcm.googleapis.com/fcm/send'),
headers: <String, String>{
'Content-Type': 'application/json',
'Authorization': 'key=$serverToken',
},
body: constructFCMPayload(widget.token!),
)
.catchError((onError) {});
hasSentFcm = true;
} catch (e) {
Fluttertoast.showToast(msg: e.toString());
}
}
// Crude counter to make messages unique
/// The API endpoint here accepts a raw FCM payload for demonstration purposes.
String constructFCMPayload(String token) {
return jsonEncode(<String, dynamic>{
'data': <String, dynamic>{
'test': 'check online',
'chatId': widget.chatId,
'idTo': widget.idTo
},
'to': token,
});
}
}

The argument type 'Stream<MyUser?>' can't be assigned to the parameter type 'Stream<MyUser?>?'

I'm trying to create a user system with assistment of this playlist. But, it's kind of outdated so I'm trying to keeping up with today. I'm a totally beginner. I read few things from the stackoverflow but I couldn't keep up. Here is what error it gave:
The argument type 'Stream<MyUser?>' can't be assigned to the parameter type 'Stream<MyUser?>?'.
And here is the main.dart: (To be specific error is right in the "value: AuthService().user," line. The "AuthService().user" part is underline in red.)
import 'package:flutter/material.dart';
import 'package:unistuff_main/screens/wrapper.dart';
import 'package:provider/provider.dart';
import 'package:unistuff_main/services/auth.dart';
import 'package:unistuff_main/models/myuser.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return StreamProvider<MyUser?>.value(
initialData: null,
value: AuthService().user,
child: MaterialApp(
home: Wrapper(),
),
);
}
}
And here is my myuser.dart file:
class MyUser {
final String? uid;
MyUser({this.uid});
}
And here is auth.dart: (If you noticed I didn't add any login method yet. Is that can cause the problem?)
import 'package:firebase_auth/firebase_auth.dart';
import 'package:unistuff_main/models/MyUser.dart';
class AuthService {
final FirebaseAuth _auth = FirebaseAuth.instance; //private data member
//create MyUser object based on FirebaseUser
MyUser? _userfromFirebase(User user) {
return user != null ? MyUser(uid: user.uid) : null;
}
//auth change user stream
Stream<MyUser?> get user {
return _auth
.authStateChanges()
.map((User? user) => _userfromFirebase(user!));
}
//sign in with email & password
Future signInWithMail() async {
try {} catch (e) {}
}
//register with email & password
//sign out
}
Thanks for any help..
Library URIs are case sensitive. AuthService imports MyUser from MyUser.dart, while MyApp imports MyUser from myuser.dart. Even though these are the same file (assuming a case-insensitive file system), they are considered different libraries, so the types don't match.
You will need to change one of the import statements to match the other.
It seems that the type of your AuthService().user isn't null-able, so try to change the type of the user from your auth.dart to Stream<MyUser?>?.
Stream<MyUser?>? get onAuthStateChanges => FirebaseAuth.instance
.authStateChanges()
.map((currentUser) => MyUser.fromSnapshot(currentUser!));
I'm not sure, but you probably are using the authStateChanges() from FirebaseAuth.instance. So if it doesn't solve your problem, print the AuthService().user :)
In the line when error occurs, try to use operator ?? and pass the raw object if null.
For example:
MyObject(
stringField: myString ?? '', //you are ensuring that if your String is null, there will be no error
);
Instead of:
Stream<MyUser?> get user {
return _auth
.authStateChanges()
.map((User? user) => _userfromFirebase(user!));
}
Write:
Stream<MyUser?> get user {
return _auth.authStateChanges().map(_userFromFirebaseUser);
}

Endless loop in firestore

I'm trying to update my documents in firestore, so when I'm trying to update it keeps updating without stopping. The first time it updates using the data from the signup dart file, then the second time it updates using the data from another dart file.
Here is the code for the signup:
FirebaseAuth auth = FirebaseAuth.instance;
await auth.createUserWithEmailAndPassword(
email: emailController.text, password: passwordController.text)
.then((value) => {
Navigator.pushNamed(context, 'DialogFlow'),
user=auth.currentUser,
user.sendEmailVerification(),
DatabaseService(uid:user.uid).UpdateUserData("", emailController.text, ChatScreenState().mess)
Here is the code for the other dart file:
#override
Widget build(BuildContext context) {
WidgetsBinding.instance.addPostFrameCallback((_) => _scrollToEnd());
FirebaseAuth auth = FirebaseAuth.instance;
user=auth.currentUser;
DatabaseService db = DatabaseService(uid: user.uid);
return StreamBuilder(
stream: FirebaseFirestore.instance.collection("users").doc(user.uid).snapshots(),
builder: (context , snapshot){
print("====================================");
print(snapshot.data);
print("====================================");
if (snapshot.data != null) {
this.userTestMessage = "";
shhh = pressed ? true : false;
flag = true;
print(Retrieved_messages);
if (Retrieved_messages==false) {
this.messsages = snapshot.data['messsages'];
Retrieved_messages=true;
}
db.UpdateUserData(
user.displayName, user.email, this.messsages);
print(mess);
print(Retrieved_messages);
print("==============================");
print(snapshot.data);
print("==============================");
}
if (db.getUserMessages() == null) {
if (user != null) {
db.UpdateUserData(
user.displayName, user.email, this.messsages);
}
}
And the code for the database which sets and updates the documents is:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:satoshi/models/Userdata.dart';
import 'package:satoshi/widgets/dialog_flow.dart';
class DatabaseService {
//collection reference
final String uid;
List messsages=[];
DatabaseService({this.uid, this.messsages});
final CollectionReference userCollection = FirebaseFirestore.instance
.collection('users');
SetUserData(String Username, String Email,
List messsages) async
{ try {
return await FirebaseFirestore.instance.collection("users").doc(uid).set({
'Username': Username,
'Email': Email,
'messsages': messsages,
}
);
}catch(e){
print(e+" this is the error");
}
}
UpdateUserData(String Username, String Email,
List messsages) async
{ try {
return await FirebaseFirestore.instance.collection("users").doc(uid).update({
'Username': Username,
'Email': Email,
'messsages': messsages,
}
);
}catch(e){
print(e+" this is the error");
}
}
Future getUserMessages() async
{
DocumentSnapshot UserDetail = await userCollection.doc(uid).get();
var msg = UserDetail.data()['messsages'];
return await msg;
}
Stream<QuerySnapshot> get users {
return userCollection.snapshots();
}
Userdata userDataFromSnapshot(DocumentSnapshot snapshot) {
return Userdata(uid: uid,
name: snapshot.get('Username'),
email: snapshot.get('Email'),
messsages: snapshot.get('messsages'),
);
}
Stream<Userdata> get userData {
return userCollection.doc(uid).snapshots().asyncMap(userDataFromSnapshot);
}
}
Note: it keeps adding the data in the signup code, then adds the data in the other dart file, which results in an endless loop, also the snapshot isn't updating, it remains the same data as the signup
You are calling the Update function inside the stream builder so what it basically does is once the update function is called firebase gets notified of the document change and rebuilds the widget so again the update function is called and it turns into an infinite loop. what you can do is add any condition such that it won't get called again once the data is updated.
Example
if (<Somecondition>){
db.UpdateUserData(
user.displayName, user.email, this.messsages);
}

UserCredential in Firebase

I'm having an error with Firebase for the first time, and not sure why it keeps showing the error.
Error :
The name 'UserCredential' is defined in the libraries 'package:firebase/src/auth.dart (via package:firebase/firebase.dart)' and 'package:firebase_auth/firebase_auth.dart'.\nTry using 'as prefix' for one of the import directives, or hiding the name from all but one of the imports.
import 'package:firebase_auth/firebase_auth.dart';
class AuthService {
final FirebaseAuth _auth = FirebaseAuth.instance;
// Create user object based on FirebaseUser
CustomUsers _customModelForFirebaseUser(user) {
return user != null ? CustomUsers(uid: user.uid) : null;
}
// auth changed user stream
Stream<CustomUsers> get user {
return _auth.authStateChanges().map(_customModelForFirebaseUser);
}
//Signin Anon
Future signInAnon() async {
try {
UserCredential userCredential = await _auth.signInAnonymously();
CustomUsers user = userCredential.user;
print(_customModelForFirebaseUser(user));
} catch (e) {
print(e.toString());
return null;
}
}
The package have same class name, which causing packing to conflicts. Try to hide one of them like this
import 'package:firebase_auth/firebase_auth.dart' hide UserCredential;
For more explanation check out this webiste dart-flutter-how-to-solve-naming-conflict-or-ambiguous

Getting Boolean State from Cloud_Firestore in dart

I'm making an app that needs to display two separate home pages depending on whether or not a counselor value equals true on my Cloud Firestore Database. I am new to Object Oriented Programing, dart, and Firestore so please bear with me.
What I'm trying to do is initialize a variable called counselor and set that equal to the Counselor field value on my database. Then I wish to first check if the user has signed in and if they have signed it that's when I use a series of if statements to see whether or or not the counselor boolean equals true or false. and depending on the result it will display a certain homepage.
I get an error message on my app saying that the I'm returning a null value on my counselor widget. I suspect this is because I'm setting the counselor variable to equal the name of the field Counselor on my database and not it's actual boolean value. Problem is I'm not aware of the syntax to circumvent this problem.
Here is my code
import 'package:strength_together/models/user.dart';
import 'package:strength_together/Screens/authenticate/authenticate.dart';
import 'package:strength_together/Screens/home/home.dart';
import 'package:strength_together/Screens/home/wrapper.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:strength_together/services/database.dart';
import 'package:strength_together/Screens/home/counsHome.dart';
class Wrapper extends StatelessWidget {
#override
Widget build(BuildContext context) {
final user = Provider.of<SUser>(context);
bool counselor = getCounselor();
//return either authenticate or home/couselor home
if (user == null){
return Authenticate();
}else if(counselor == true){
return CounselorHome();
}else if(counselor == false){
return Home();
}
}
}
// Database class
import 'package:cloud_firestore/cloud_firestore.dart';
class DatabaseService {
final String uid;
DatabaseService({this.uid});
//collection reference
final CollectionReference userCollection = FirebaseFirestore.instance
.collection('User Data');
Future updateUserData(String name, bool counselor) async {
return await userCollection.doc(uid).set({
'name': name,
'Counselor': true,
});
}
//get user data stream
Stream<QuerySnapshot> get userData {
return userCollection.snapshots();
}
Future<DocumentSnapshot> getDataSnapshotForCounselor() async
{
return await FirebaseFirestore.instance.collection('User Data')
.doc(uid)
.get();
}
bool getCounselorValue(DocumentSnapshot dataSnapshotForCounselor) {
//modify this by passing proper keyValue to get counselor.
return dataSnapshotForCounselor.data()['Counselor'];
}
}
// auth class
import 'package:firebase_auth/firebase_auth.dart';
import 'package:strength_together/Screens/authenticate/register.dart';
import 'package:strength_together/models/user.dart';
import 'package:strength_together/services/database.dart';
import 'package:flutter/material.dart';
class AuthService{
final FirebaseAuth _auth = FirebaseAuth.instance;
//instance of counselor
bool _counselor;
bool get counselor => counselor;
void setCounselor(bool counselor) {
_counselor = counselor;
}
//create user obj based on firebase user from my code
SUser _userFromFirebaseUser(User user){
return user != null ? SUser(uid: user.uid) : null;
}
//auth change user stream
Stream<SUser> get user {
return _auth.authStateChanges()
.map(_userFromFirebaseUser);
}
//method to sign in anon
Future signInAnon() async{
try{
UserCredential result = await _auth.signInAnonymously();
User user = result.user;
return _userFromFirebaseUser(user);
}catch(e){
print(e.toString());
return null;
}
}
//method to sign in with email/pass
Future signInWithEmailAndPassword(String email, String password) async{
try{
UserCredential result = await _auth.signInWithEmailAndPassword(email: email, password: password);
User user = result.user;
return _userFromFirebaseUser(user);
}catch(e){
print(e.toString());
return null;
}
}
//method to register with email/pass
Future registerWithEmailAndPassword(String email, String password) async{
try{
UserCredential result = await _auth.createUserWithEmailAndPassword(email: email, password: password);
User user = result.user;
//create a new document for that user with the uid
await DatabaseService(uid: user.uid).updateUserData('New user', _counselor);
return _userFromFirebaseUser(user);
}catch(e){
print(e.toString());
return null;
}
}
//sign in with google
//sign out
Future signOut() async{
try{
return await _auth.signOut();
}catch(e){
print(e.toString());
return null;
}
}
There are lots of problems with your code . First of all you are using Firestore.instance and .document which are deprecated . Use FirebaseFirestore.instance and FirebaseFirestore.instance.collection(...).doc(...) instead.
Coming to your code , Use a FutureBuilder to decide which page to show , while deciding , you can show a CircularProgressIndicator to the user.
Next , this code
Firestore.instance.collection('User Data').document('Counselor');
returns a DocumentReference and not the actual value that is required .
So after you get the DocumentReference use .get() which returns a Future<DocumentSnapshot> , DocumentSnapshot class has a method data() which returns Map<String,dynamic>. Once you get the Map<> , you can simply get the data you want by passing the key value.
The whole process would look similar to this :
Future<bool> getCounselorValue() async {
return await FirebaseFirestore.instance.collection(...).doc(...).get().data()['keyvalue'];
}
Aagain , remember you are dealing with a Future so you have to use either await , .then() or FutureBuilder . I recommend FutureBuilder for your case , if you are not sure how to use it, refer to this . I also answered this which might be helpful if you want to dynamically decide which page to show to user first (refer to first method).
Edit:
Ok , so there was a little mistake in my code .Now I am sharing with you a little more code which you might find helpful .
make your class Stateful from Stateless and follow this code :
class Wrapper extends StatefulWidget{
WrapperState createState()=> WrapperState();
}
class WrapperState extends State<Wrapper>
{
Future<DocumentSnapshot> documentSnapshot;
User getUser(BuildContext context)
{
return Provider.of<User>(context);
}
dynamic getStartingPage(bool counselor,User user)
{
if (user == null){
return Authenticate();
}else if(counselor == true){
return CounselorHome();
}else if(counselor == false){
return Home();
}
}
Future<DocumentSnapshot> getDocumentSnapshotForCounselor() async
{
return await FirebaseFirestore.instance.collection(...).doc(...).get();
}
bool getCounselorValue(DocumentSnapshot documentSnapshotForCounselor)
{
//modify this by passing proper keyValue to get counselor.
return documentSnapshotForCounselor.data()['keyValue'];
}
#override
void initState()
{
super.initState();
documentSnapshot=getDocumentSnapshotForCounselor();
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future:documentSnapshot,
builder: (BuildContext ctx, AsyncSnapshot<dynamic> snapshot) {
if (snapshot.connectionState != ConnectionState.done) {
return Center( child : const CircularProgressIndicator());
}
else
return getStartingPage(getCounselorValue(snapshot.data),getUser(context));
}
);
}
}
Modify getDocumentSnapshotForCounselor and getCounselorValue methods ,so that you can get the value of the field from the database.
Upgrade to latest version of cloud_firestore , visit this to understand how to do it and what to import .
Edit 2:
When you sign in user ,either by email password or using google sign in etc. It returns a Future<UserCredential> , UserCredential has a property user , which returns User. Now what we want is, a unique uid of the user which we can get using User.uid :
UserCredential userCredential =await _auth.createUserWithEmailAndPassWord();
String uid= userCredential.user.uid;
Now create an entry for the user in the database using this uid:
FirebaseFirestore.instance.collection("User Data").doc(uid).set({"Counselor":true,"name":name});
Afterwards , to get the counselor value , :
FirebaseFirestore.instance.collection("User Data").doc(uid).get();
and
documentSnapshotForCounselor.data()['Counselor'];
The above code is just for an explanation , make appropriate changes to your code.

Resources