为什么 RiverPod StreamProvider 卡在加载中,即使 Stream.empty() 应该在数据库为 null 时返回
Why is RiverPod StreamProvider stuck in loading even though Stream.empty() should be returning when database is null
我正在使用这个 great flutter/riverpod/firebase starter architecture。在我导入 top_level_providers.dart
的初始 flutter 屏幕上,loading
参数就是所有被调用的参数。
在我的 accountStreamProvider
中,当发出 databaseProvider returns null Stream.empty()
时,根据文档“这是一个流,除了在监听时发送完成事件外什么都不做到。”。
为什么 account.when
部分没有调用 data
参数?
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,
};
}
}
Stream.empty()
从不包含任何数据。如果这是您的 StreamProvider
return,则不会调用 data
回调。您很可能只会得到 loading
。
为了获得 data
回调,您的流应该 return 至少一个元素(无论是否为 null)。
我正在使用这个 great flutter/riverpod/firebase starter architecture。在我导入 top_level_providers.dart
的初始 flutter 屏幕上,loading
参数就是所有被调用的参数。
在我的 accountStreamProvider
中,当发出 databaseProvider returns null Stream.empty()
时,根据文档“这是一个流,除了在监听时发送完成事件外什么都不做到。”。
为什么 account.when
部分没有调用 data
参数?
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,
};
}
}
Stream.empty()
从不包含任何数据。如果这是您的 StreamProvider
return,则不会调用 data
回调。您很可能只会得到 loading
。
为了获得 data
回调,您的流应该 return 至少一个元素(无论是否为 null)。