I’m beginner in Flutter, Riverpod for the state management and firebase for the authentication.
I’m looking to retrieve the logged user’s email to pass to my postgres database and retrieve all the user information. In a first time, I just try to display the nickname of the current user. I am facing a problem when I log out of the app to log back in. The auth providers are not updated so I get informations from the very first connected user. For example currentUserEmailProvider
still get the first connected user email. Any help is welcome, I’m really stuck.
My auth_repository.dart:
class AuthRepository {
const AuthRepository(this._auth);
final FirebaseAuth _auth;
Stream<User?> get authStateChange => _auth.idTokenChanges();
Stream<User?>get authUserChange => _auth.userChanges();
String? get currentUserEmail => _auth.currentUser?.email;
Future<User?> signInWithEmailAndPassword(
String email, String password) async {
_auth.currentUser?.reload();
try {
final result = await _auth.signInWithEmailAndPassword(
email: email,
password: password,
);
return result.user;
} on FirebaseAuthException catch (e) {
if (e.code == 'user-not-found') {
throw const ErrorHandler(message: 'User not found');
} else if (e.code == 'wrong-password') {
throw const ErrorHandler(message : 'Wrong password');
} else {
throw const ErrorHandler(message: 'An error occurred. Please try again later');
}
}
}
Future<AppUser?> registerWithEmailAndPassword(String email, String password, String nickname, String role, String firstname, String lastname) async {
// Sans ces deux lignes la création d'un nouveau compte entraîne un login automatique sur ce compte
FirebaseApp app = await Firebase.initializeApp(
name: 'Secondary', options: Firebase.app().options);
try {
AppUser? appUser = await UserRepository(email).saveUser(email, nickname, role, firstname, lastname);
if(appUser != null) {
try {
UserCredential result =
await FirebaseAuth.instanceFor(app: app).createUserWithEmailAndPassword(email: email, password: password);
User? user = result.user;
if(user == null) {
throw Exception("user from firebase not found");
}
return appUser;
} on FirebaseException catch(e) {
await UserRepository(email).deleteUser(email);
print(ErrorHandler(message: e.code.toString()));
}
} else {
throw Exception("user from postgres database not found");
}
} on PostgreSQLException catch(e) {
print(ErrorHandler(message: e.code.toString()));
}
return null;
}
Future<void> signOut() async {
await _auth.signOut();
}
}
My user_repository.dart:
class UserRepository {
final String email;
PostgreSQLConnection? connection;
UserRepository(this.email){
connection = (connection == null || connection!.isClosed == true
? PostgreSQLConnection(
'10.0.2.2', 5432, DatabaseAccess.databaseName,
queryTimeoutInSeconds: 3600,
timeoutInSeconds: 3600,
username: DatabaseAccess.databaseUser,
password: DatabaseAccess.databasePassword) : connection);
}
}
Future<AppUser?> getCurrentUser(String? currentEmail) async {
print(currentEmail);
try {
await connection!.open();
final result = await connection!.mappedResultsQuery(
'select * from public.user where email = @emailValue',
substitutionValues: {
'emailValue': currentEmail,
},
allowReuse: true,
timeoutInSeconds: 30,
);
final userFromDataBase = result[0]['user']!;
return AppUser(
email: userFromDataBase['email'],
nickname: userFromDataBase['nickname'],
role: userFromDataBase['role'],
firstname: userFromDataBase['firstname'],
lastname: userFromDataBase['lastname'],
);
} on PostgreSQLException catch(e) {
print(ErrorHandler(message: e.toString()));
return null;
}
}
}
My providers.dart:
final authRepositoryProvider = Provider<AuthRepository>((ref) {
return AuthRepository(FirebaseAuth.instance);
});
final authStateProvider = StreamProvider<User?>((ref) {
return ref.read(authRepositoryProvider).authStateChange;
});
final currentUserEmailProvider = Provider<String?>((ref) {
return AuthRepository(FirebaseAuth.instance).currentUserEmail;
});
final userRepositoryProvider = Provider.autoDispose<UserRepository>((ref) {
return UserRepository(ref.read(currentUserEmailProvider)!);
});
final futureCurrentUserProvider = Provider<Future<AppUser?>>((ref) {
return ref.read(userRepositoryProvider).getCurrentUser(ref.read(currentUserEmailProvider));
});
final currentUserProvider = FutureProvider.autoDispose<AppUser?>((ref) => ref.read(userRepositoryProvider).getCurrentUser(ref.read(currentUserEmailProvider)));
My home_screen.dart:
class HomeScreen extends HookConsumerWidget {
const HomeScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
final currentUser = ref.watch(currentUserProvider);
return Scaffold(
body: currentUser.when(
data: (user) => _buildBody(context, user, ref),
loading: () => const Center(child: CircularProgressIndicator()),
error: (error, _) => _errorBody(context, ref),
)
);
}
Widget _buildBody(BuildContext context, AppUser? user, WidgetRef ref) {
if(user == null) {
return _errorBody(context, ref);
} else {
return Center(child: Text(
'Welcome ${user.getNickname}',
style: const TextStyle(fontSize: 20),
));
}
}
Widget _errorBody(BuildContext context, WidgetRef ref) {
return const Center(child: Text(
"Error: No user found",
style: TextStyle(fontSize: 20, color: Colors.red),
));
}
}
2
Answers
Ok I resolved my problem with this following. To be simplier I removed the singleton in my user_repository.dart. The problem was from my providers and I found a way to use the
authUserChanges()
method :providers.dart :
Try changing
to
and changing
to