简体   繁体   中英

Flutter Riverpod and Firebase

I've been working through some samples using riverpod attempting to correctly separate the logic from the UI layer and business layer. My use case in this instance is that I want to store a custom user profile in Firestore however I'm confused about the provider setup when a ViewModel is involved.

The sample from codewithandrea.com defines two files per screen, a model (ProfileModel) and a page/pagebuilder(ProfilePageBuilder). At the top of the ProfilePageBuilder it declares a state provider to access the ViewModel. This provider resolves the dependencies required by the ProfileModel.

final profileModelProvider =
    StateNotifierProvider.autoDispose<ProfileModel, ProfileState>((ref) {
  final authService = ref.watch(authServiceProvider);

  final databaseService = ref.watch(databaseProvider)!;

  return ProfileModel(
      authService: authService, databaseService: databaseService);
});

Now from here, I want the page to have access to the user profile returned from Firestore but the Firestore service returns a Stream<UserProfile> . My questions are:

  • Do I resolve the stream to a future and return the profile when calling a method in the page, such as loadProfile() within initState.
  • Or do I create a new stream provider in the PageBuilder linking to the Firestore service (I feel this is working against the architecture)
  • Do I return the stream from the ViewModel as a stream and then create a stream provider that looks at the ViewModel rather than the service?

This is common question with Firebase and Riverpod.

Whether you want to return a stream is up to you. You don't have to. Here's the line of thought to decide what you want.

  • If the UI should know the "previous" value, you probably should return a stream.
  • if the UI only cares about the "current" value, you don't have to return a stream.
  • You could keep track of the last value with a stateful widget, so may be you only need the current value.

I'll use riverpod 2.0 with code generation syntax, but these all can be written without codegen if you prefer, just more verbose.

One way is to use a BehaviorSubject from package:rxdart which remembers only the last value of a stream.

import 'package:rxdart/rxdart.dart';

@riverpod
BehaviorSubject<UserProfile> userProfileSubject(
       UserProfileStreamRef ref, 
       String userId, 
       /*more params if you want*/) {

   final firebaseStream = ...

   // goal of this provider is to get the stream
   // from firebase and convert to a BehaviorSubject
   return BehaviorSubject()
           ..addStream(firebaseStream);
}

@riverpod
UserProfile? userProfile(UserProfileRef ref) {
   // this provider listens to the behavior subject
   // and refreshes itself when the value changes
   // so the dependent providers and widgets can react
   final subject = ref.watch(userProfileSubjectProvider(userId, ...));


   // if no params to provider
   //final subject = ref.watch(userProfileSubjectProvider);

   // when there's a new value, invalidate self
   // so we re-run this and return the new value
   subject.doOnData((newProfile) {ref.invalidateSelf();});

   // note that subject can have a null value until
   // first value is emitted from the stream or stream emits a null.
   // your UI must handle this case.
   return subject.value;
}

now your view model or whatever can depend on this userProfileProvider or use it in a ConsumerWidget directly.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM