Why is RiverPod StreamProvider stuck in loading even though Stream.empty() should be returning when database is null
607
Stream.empty()
never contains any data. If this is what your StreamProvider
returns, the data
callback will not be called. Most likely you'll get loading
only.
In order to get a data
callback, your stream should return at least one element (whether it's null or not).
Author by
Zelf
Updated on December 27, 2022Comments
-
Zelf over 1 year
I'm using this great flutter/riverpod/firebase starter architecture. On my initial flutter screen, which imports
top_level_providers.dart
theloading
parameter is all that is ever called.In my
accountStreamProvider
when the databaseProvider returns nullStream.empty()
is emitted, which according to the docs "This is a stream which does nothing except sending a done event when it's listened to.".Why isn't the
data
parameter being called in theaccount.when
section?root_screen.dart
class AppStartupScreenRouter extends ConsumerWidget { const AppStartupScreenRouter({ Key key, }) : super(key: key); @override Widget build(BuildContext context, ScopedReader watch) { final didCompleteOnboarding = watch(onboardingViewModelProvider.state); AsyncValue<Account> account = watch(accountStreamProvider); return account.when( data: (data) { if (data != null) { // evaluate the account info data and // return a screen widget } else if (didCompleteOnboarding) { // return a screen widget } return IntroScreen(); }, loading: () => LoadingScreen(), error: (err, stack) => EmptyContent(), ); } }
top_level_providers.dart
final firebaseAuthProvider = Provider<FirebaseAuth>((ref) => FirebaseAuth.instance); final authStateChangesProvider = StreamProvider<User>( (ref) => ref.watch(firebaseAuthProvider).authStateChanges()); final databaseProvider = Provider<FirestoreDatabase>((ref) { final auth = ref.watch(authStateChangesProvider); if (auth.data?.value?.uid != null) { return FirestoreDatabase(uid: auth.data?.value?.uid); } return null; }); final accountStreamProvider = StreamProvider<Account>((ref) { final db = ref.watch(databaseProvider); return db != null ? db.accountStream() : Stream.empty(); });
firestore_database.dart
class FirestoreDatabase { FirestoreDatabase({@required this.uid}) : assert( uid != null, 'Cannot create FirestoreDatabase entry with null uid'); final String uid; final _service = FirestoreService.instance; Future<void> setAccount(Account account) => _service.setData( path: FirestorePath.account(uid), data: account.toMap(), ); Stream<Account> accountStream() => _service.documentStream( path: FirestorePath.account(uid), builder: (data, documentId) => Account.fromMap(data, uid), ); Future<void> deleteAccount(Account account) => _service.deleteData(path: FirestorePath.account(uid)); }
account.dart
@immutable class Account extends Equatable { const Account({ @required this.id, @required this.firstName, @required this.lastName, }); final String id; final String firstName; final String lastName; @override List<Object> get props => [ id, firstName, lastName, ]; @override bool get stringify => true; factory Account.fromMap(Map<String, dynamic> data, String documentId) { if (data == null) { return null; } final firstName = data['firstName'] as String; final lastName = data['lastName'] as String; if (firstName == null || lastName == null) { return null; } return Account( id: documentId, firstName: firstName, lastName: lastName, ); } Map<String, dynamic> toMap() { return { 'firstName': firstName, 'lastName': lastName, }; } }