#Does account.get throw an exception in flutter?
1 messages · Page 1 of 1 (latest)
Yes
hm
Check this #🏠│general message
How do you recommend I create a provider?
I see that, trying to create a Riverpod provider for my Appwrite authentication
I had this
@Riverpod(keepAlive: true)
Future<User?> authState(AuthStateRef ref) {
return ref.watch(authRepositoryProvider).currentUser;
}
@Riverpod(keepAlive: true)
AuthRepository authRepository(AuthRepositoryRef ref) {
final account = ref.watch(appwriteAccountProvider);
return AuthRepository(account: account);
}
class AuthRepository {
AuthRepository({required this.account});
final Account account;
Future<User> get currentUser => account.get();
but
obviously that would throw and I didn't realize until I looked at it again
hm
I think? I can do this
@Riverpod(keepAlive: true)
Future<User?> authState(AuthStateRef ref) {
return ref.watch(authRepositoryProvider).currentUser;
}
@Riverpod(keepAlive: true)
AuthRepository authRepository(AuthRepositoryRef ref) {
final account = ref.watch(appwriteAccountProvider);
return AuthRepository(account: account);
}
class AuthRepository {
AuthRepository({required this.account});
final Account account;
Future<User?> get currentUser => _getCurrentUser();
Future<User?> _getCurrentUser() async {
try {
final user = await account.get();
return user;
} catch (e) {
// TODO: Do I handle this? Prolly not
return null;
}
}
It looks like you can use something like this
https://riverpod.dev/docs/providers/future_provider#usage-example-reading-a-configuration-file
final authProvider = FutureProvider<User>((ref) async {
Future<User> get currentUser => account.get();
return currentUser;
});
Then in your code something like this
AsyncValue<User> user = ref.watch(authProvider);
user.when(
loading: () => const CircularProgressIndicator(),
error: (err, stack) => Text('Error: $err'),
data: (user) {
// use the user
}
Then you can have a provider that has three states
This is not complete example, but, a starting point.
yeah those @riverpod are the new syntax they do the same thing basically
so authState becomes authStateProvider which is a FutureProvider<User?>
which just returns a ref.watch(authRepositoryProvider).currentUser
@wild lodge is there an Appwrite Flutter setup using Riverpod base starter repo or tutorial?
I did this
import 'package:appwrite/appwrite.dart';
import 'package:appwrite/models.dart';
import 'package:bealbot_ai/core/core.dart';
import 'package:bealbot_ai/features/auth/domain/app_user.dart';
import 'package:flutter/foundation.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
// Want to sign up or get user account -> Account (from appwrite)
// Want to access user related data -> model.User (from models.dart)
part 'auth_repository.g.dart';
@Riverpod(keepAlive: true)
Future<User> authState(AuthStateRef ref) async {
return await ref.watch(appwriteAccountProvider).get();
}
@Riverpod(keepAlive: true)
AuthRepository authRepository(AuthRepositoryRef ref) {
final account = ref.watch(appwriteAccountProvider);
return AuthRepository(account: account);
}
class AuthRepository {
AuthRepository({required this.account});
final Account account;
Future<User> get currentUser => account.get();
Future<AppUser> signUp({
required String email,
required String password,
}) async {
try {
final userId = ID.unique();
await account.create(
userId: userId,
email: email,
password: password,
);
final AppUser appUser = AppUser(
email: email,
name: getNameFromEmail(email),
profilePic: '',
uid: userId,
);
return appUser;
} on AppwriteException catch (e, st) {
//TODO: Implement Error Handling
debugPrint("Appwrite Exception: ${e.message}\n${st.toString()}");
} catch (e, st) {
//TODO: Implement Error Handling
debugPrint("Appwrite Exception: ${e.toString()}\n${st.toString()}");
}
throw Exception("Failed to sign up user, we should never reach here");
}
Future<void> login({
required String email,
required String password,
}) async {
try {
final result = await account.createEmailSession(
email: email,
password: password,
);
} on AppwriteException catch (e, st) {
//TODO: Implement Error Handling
debugPrint("Appwrite Exception: ${e.message}\n${st.toString()}");
} catch (e, st) {
//TODO: Implement Error Handling
debugPrint("Appwrite Exception: ${e.toString()}\n${st.toString()}");
}
}
}
but unsure if that's the right implementation
I'm not sure about Riverpod haven't used it recently
So what's the problem?
I think you should handle the exception by wrapping it in a try/catch.
Where and how are you using auth state?
I’m trying to create an authProvider
That has the users logged in state or not
But the account.get only throws an exception or the user
Yes and?
I think you should be creating a state notifier: https://riverpod.dev/docs/providers/state_notifier_provider
@jovial scaffold so Riverpod 2.0 came out and got rid of State Notifiers
this is what I have right now
@Riverpod(keepAlive: true)
Future<User?> authState(AuthStateRef ref) async {
final User? user = await ref.watch(authRepositoryProvider).currentUser;
return user;
}
@Riverpod(keepAlive: true)
AuthRepository authRepository(AuthRepositoryRef ref) {
final account = ref.watch(appwriteAccountProvider);
return AuthRepository(account: account);
}
class AuthRepository {
AuthRepository({required this.account});
final Account account;
Future<User?> get currentUser {
try {
return account.get();
} catch (e) {
return Future.value(null);
}
}
but I'm getting an exception on the account.get() period
Maybe you should do something like this then:
Well
this is how Andrea did it in his Firebase course https://github.com/bizz84/starter_architecture_flutter_firebase/blob/master/lib/src/features/authentication/data/firebase_auth_repository.dart
the firebase sdk is different because it uses streams
also getting
AppwriteException (AppwriteException: general_unauthorized_scope, User (role: guests) missing scope (account) (401))
which is because of the account.get()?
probably
okay so... I have a try/catch, and it still is erroring out on top of that because of the account.get using fromMap in Flutter which throws an exception if there's nothing there
Why wouldn't it just return Future<User?> and return null if not signed in?
are you calling account.get() anywhere else?
What exactly is going on? Maybe it's just your debugger set to stop execution on every exception (even the caught ones)
It says uncaught
wait...you should await account.get()
also, it's typically not a good idea to make a network call in a getter like that
it's in a provider
so? AuthRepository is in a provider, but every time you access currentUser you'll be making a network call
hm true I can update the user when it's not null and store it
but in general it shouldn't throw an exception for using account.get()
at least in my opinion, maybe I'm wrong
how exactly do you recommend I set the the user value? The issue is when I check I want it to be null until it isn't
and right now it's throwing, and I have no way of knowing when it's set other than after login but the authRepository handles all of the methods around the user and that ref.watch is supposed to update the data (it's a reactive caching framework so the extra network calls don't matter)
thats why i recommend doing something like this where you have methods for login and/or init that can be called to make an api call and then set the state
what's the new code? and where are you calling currentUser?
I'm currently doing this
@Riverpod(keepAlive: true)
Future<User?> authState(AuthStateRef ref) async {
final User? user =ref.watch(authRepositoryProvider).currentUser;
return user;
}
@Riverpod(keepAlive: true)
AuthRepository authRepository(AuthRepositoryRef ref) {
final account = ref.watch(appwriteAccountProvider);
return AuthRepository(account: account);
}
class AuthRepository {
AuthRepository({required this.account});
final Account account;
User? user;
User? get currentUser {
if (user != null) {
return user;
} else {
return null;
}
}
Future<User?> _checkCurrentUser() async {
try {
final maybeUser = await account.get();
user = maybeUser;
} catch (e) {
return Future.value(null);
}
}
which is obviously disconnected
I could do on login ref.read(authRepositoryProvider).checkCurrentUser() after login
but I really don't think it makes sense the way it gets translated to flutter
because if it was just Future<User?> instead of a User than it would be null until it's not and the provider would work correctly
It just seems like an implementation where error handling is on the developer even though the thing I'm asking is "is the user logged in?"
calling checkCurrentUser() won't trigger any state updates
so what exception is being thrown now?
It isn't working ATM. Working on it.
okay so if checkCurrentUser won't trigger any state updates what am I supposed to do?
that's my issue I'm having here
if I could call account.get() and it would return User? then I could just watch that value, because I can't do that I have to develop a roundabout way of creating a simple authentication system
i already told you
you can try this
okay. It technically shouldn't be a repository but I'll do that
I ended up doing this in case it happens again
import 'package:appwrite/models.dart';
import 'package:bealbot_ai/core/core.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'appwrite_auth_state.g.dart';
@Riverpod(keepAlive: true)
class AppwriteAuthState extends _$AppwriteAuthState {
User? user;
@override
Future<User?> build() async {
return await checkCurrentUser();
}
Future<User?> checkCurrentUser() async {
if (user != null) {
return user;
} else {
try {
final account = ref.watch(appwriteAccountProvider);
final maybeUser = await account.get();
user = maybeUser;
return user;
} catch (e) {
return Future.value(null);
}
}
}
}
still testing to make sure it works
Please, can you publish the code of the "appwriteAccountProvider" to take the account, thanks
import 'package:appwrite/models.dart';
import 'package:bealbot_ai/core/core.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'appwrite_auth_state.g.dart';
@Riverpod(keepAlive: true)
class AppwriteAuthState extends _$AppwriteAuthState {
User? user;
@override
Future<User?> build() async {
return await checkCurrentUser();
}
Future<User?> checkCurrentUser() async {
if (user != null) {
return user;
} else {
try {
final account = ref.watch(appwriteAccountProvider);
final maybeUser = await account.get();
user = maybeUser;
return user;
} catch (e) {
return Future.value(null);
}
}
}
}
import 'package:appwrite/appwrite.dart';
import 'package:appwrite/models.dart';
import 'package:bealbot_ai/core/core.dart';
import 'package:bealbot_ai/features/auth/data/appwrite_auth_state.dart';
import 'package:bealbot_ai/features/auth/domain/app_user.dart';
import 'package:flutter/foundation.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
// Want to sign up or get user account -> Account (from appwrite)
// Want to access user related data -> model.User (from models.dart)
part 'auth_repository.g.dart';
@Riverpod(keepAlive: true)
Future<User?> authState(AuthStateRef ref) async {
return ref.watch(appwriteAuthStateProvider).value;
}
@Riverpod(keepAlive: true)
AuthRepository authRepository(AuthRepositoryRef ref) {
final account = ref.watch(appwriteAccountProvider);
return AuthRepository(account: account);
}
class AuthRepository {
AuthRepository({required this.account});
final Account account;
User? user;
User? get currentUser {
if (user != null) {
return user;
} else {
return null;
}
}
Future<User?> checkCurrentUser() async {
try {
final maybeUser = await account.get();
user = maybeUser;
} catch (e) {
return Future.value(null);
}
return null;
}
Future<AppUser> signUp({
required String email,
required String password,
}) async {
try {
final userId = ID.unique();
await account.create(
userId: userId,
email: email,
password: password,
);
final AppUser appUser = AppUser(
$id: userId,
email: email,
name: getNameFromEmail(email),
profilePic: '',
);
return appUser;
} on AppwriteException catch (e, st) {
//TODO: Implement Error Handling
debugPrint("Appwrite Exception: ${e.message}\n${st.toString()}");
} catch (e, st) {
//TODO: Implement Error Handling
debugPrint("Appwrite Exception: ${e.toString()}\n${st.toString()}");
}
throw Exception("Failed to sign up user, we should never reach here");
}
Future<void> login({
required String email,
required String password,
}) async {
try {
final result = await account.createEmailSession(
email: email,
password: password,
);
debugPrint(result.toString());
} on AppwriteException catch (e, st) {
//TODO: Implement Error Handling
debugPrint("Appwrite Exception: ${e.message}\n${st.toString()}");
} catch (e, st) {
//TODO: Implement Error Handling
debugPrint("Appwrite Exception: ${e.toString()}\n${st.toString()}");
}
}
}
I did this in the end
One question, when it make "final account = ref.watch(appwriteAccountProvider);" that provieder where is it?
wherever you need it
oh
the appwrite account