lucavenir / go_router_riverpod

An example on how to use Riverpod + GoRouter
460 stars 68 forks source link

Store Firebase User Globally using StateProvider #9

Closed myhendry closed 1 year ago

myhendry commented 1 year ago

Excellent example! I'm a newbie in Riverpod. Anyone has any idea how to store the user gotten from firebase after authentication such that I can access the user (including user firebase id) within my app? Thanks

lucavenir commented 1 year ago

Hi! I will refactor the firebase example ASAP. With that refactor I'll introduce a better use case that should answer your question, too.

If anyone else wants to contribute, PR are always welcome. The only requirement is to use codegen as much as possible

wstrange commented 1 year ago

I'm trying to work through this now, and it's tricky!

Firebase gives you a stream of User objects, or null if the User has not authenticated, or logs off.

Im finding that objects down in the tree (say the Home page) still sometimes have references to firebase handles that depend on the user context (say a query to firestore).

On logout, I'll get a bunch of exceptions where the firestore queries error out because the user has logged out. Even through the user has been redirected to a page that does not reference these queries, the Home pages still seems to be active in the tree, and the build() method gets called, re-issuing queries (hope that makes sense). I kinda work around this by just eating the exceptions, and eventually it all sorts itself out - but it seems kludgy.

The other issue I find is that I have a lot of boilerplate code that deals with the User stream being potentially null. So you end up doing a lot of this:

var user = ref.watch(userProviderStream);

user.when(  data,  error, loading,  etc....)

This pervades all down through the application. It works - but it's cumbersome / verbose. I'd like to be able to just get the user object in a page that is in theory never going to have a null User, because the navigation wont allow the user to be on that page without being authenticated.

I'm experimenting with have another StateProvider that just holds the User state, and using the redirect() trigger to populate it from the User stream - but I'm running into issue with it being out of sync with the User stream.

RE: code generation - it's nice! I wish it supported Streams. Right now its just Future?

wstrange commented 1 year ago

I should add: I tried to combine the concepts in your firebase example with the complete example. I did not get it working (a lot of errors, including weired firebase issues like "network error") - so I backed off that approach. I may have another go at it if I get some time.

myhendry commented 1 year ago

Thanks! I will give it a shot on my end too and try

lucavenir commented 1 year ago

Hi. I just updated firebase_example to include the following reasoning.

Since you have a StreamProvider containing your current user, if you need to access the currentUser then the best way to do so is the following:

final name = ref.watch(authProvider.select(
      (value) => value.valueOrNull?.displayName,
    ));

The above snippet can be inserted inside another custom provider, or just inside your widget (wherever you need it).

RE: code generation - it's nice! I wish it supported Streams. Right now its just Future?

Yes, at the moment riverpod_annotation can't generate StreamProviders.

This pervades all down through the application. It works - but it's cumbersome / verbose.

To some extent, this is inevitable, as you're dealing with asynchronous data. Nonetheless, you could refactor common used properties with another provider and just listen to that (if you're seeking reusability).

final test123Provider = Provider.autoDispose<User?>((ref) {
  return ref.watch(authProvider.select(
    (value) => value.valueOrNull,
  ));
});