#Does account.get throw an exception in flutter?

1 messages · Page 1 of 1 (latest)

trim onyx
#

title.

wild lodge
#

Yes

trim onyx
#

hm

wild lodge
#

Check this #🏠│general message

trim onyx
#

How do you recommend I create a provider?

trim onyx
#

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;
    }
  }
wild lodge
#

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.

trim onyx
#

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

trim onyx
#

@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

wild lodge
#

I'm not sure about Riverpod haven't used it recently

jovial scaffold
#

I think you should handle the exception by wrapping it in a try/catch.

Where and how are you using auth state?

trim onyx
#

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

jovial scaffold
jovial scaffold
trim onyx
#

@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

jovial scaffold
trim onyx
#

Well

trim onyx
jovial scaffold
trim onyx
#

also getting

#

AppwriteException (AppwriteException: general_unauthorized_scope, User (role: guests) missing scope (account) (401))

#

which is because of the account.get()?

trim onyx
# jovial scaffold 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?

jovial scaffold
trim onyx
#

It says uncaught

jovial scaffold
jovial scaffold
jovial scaffold
trim onyx
#

but in general it shouldn't throw an exception for using account.get()

#

at least in my opinion, maybe I'm wrong

trim onyx
#

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)

jovial scaffold
jovial scaffold
trim onyx
#

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?"

jovial scaffold
jovial scaffold
trim onyx
#

It isn't working ATM. Working on it.

trim onyx
#

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

jovial scaffold
trim onyx
#

okay. It technically shouldn't be a repository but I'll do that

trim onyx
# jovial scaffold you can try this

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

topaz zephyr
trim onyx
# topaz zephyr Please, can you publish the code of the "appwriteAccountProvider" to take the ac...
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

topaz zephyr
trim onyx
#

oh

#

the appwrite account