Riverpod 的 StreamProvider 只产生一次 StreamValue |颤振与蜂巢
Riverpod's StreamProvider yields StreamValue only once | Flutter & Hive
我写了一个 StreamProvider,我在启动后立即收听,以获取有关可能登录的用户的所有信息。如果没有用户,那么结果将为 null,侦听器将保持加载状态,因此我决定发回一个空用户的默认值,让我知道加载已完成。
我必须这样做,因为 Hive 的 watch() 方法仅在数据更改时触发,而在启动时不会触发。
所以在那之后,我希望 watch() 方法完成它的工作,但问题是以下情况:
启动时:无用户 - 插入用户 -> watch 方法被触发 -> 我得到插入的用户数据 -> 删除登录用户-> 未触发监视方法。
At startup: Full user - 删除用户 -> watch 方法被触发 -> 我得到一个空用户 -> 插入用户 -> watch方法未触发。
一段时间后,我发现我可以随心所欲地使用所有 CRUD 操作,并且 Hive 的盒子做了它应该做的,但是 watch() 方法在被触发后不再被触发一次.
流媒体提供者:
final localUsersBoxFutureProvider = FutureProvider<Box>((ref) async {
final usersBox = await Hive.openBox('users');
return usersBox;
});
final localUserStreamProvider = StreamProvider<User>((ref) async* {
final usersBox = await ref.watch(localUsersBoxFutureProvider.future);
yield* Stream.value(usersBox.get(0, defaultValue: User()));
yield* usersBox.watch(key: 0).map((usersBoxEvent) {
return usersBoxEvent.value == null ? User() : usersBoxEvent.value as User;
});
});
听众:
return localUserStream.when(
data: (data) {
if (data.name == null) {
print('Emitted data is an empty user');
} else {
print('Emitted data is a full user');
}
return Container(color: Colors.blue, child: Center(child: Row(children: [
RawMaterialButton(
onPressed: () async {
final globalResponse = await globalDatabaseService.signup({
'email' : 'name@email.com',
'password' : 'password',
'name' : 'My Name'
});
Map<String, dynamic> jsonString = jsonDecode(globalResponse.bodyString);
await localDatabaseService.insertUser(User.fromJSON(jsonString));
},
child: Text('Insert'),
),
RawMaterialButton(
onPressed: () async {
await localDatabaseService.removeUser();
},
child: Text('Delete'),
)
])));
},
loading: () {
return Container(color: Colors.yellow);
},
error: (e, s) {
return Container(color: Colors.red);
}
);
CRUD 方法:
Future<void> insertUser(User user) async {
Box usersBox = await Hive.openBox('users');
await usersBox.put(0, user);
await usersBox.close();
}
Future<User> readUser() async {
Box usersBox = await Hive.openBox('users');
User user = usersBox.get(0) as User;
await usersBox.close();
return user;
}
Future<void> removeUser() async {
Box usersBox = await Hive.openBox('users');
await usersBox.delete(0);
await usersBox.close();
}
知道如何告诉 StreamProvider watch() 方法应该保持活动状态,即使已经发出了一个值吗?
but the watch() method is not triggered anymore after it got triggered
once
那是因为在每次 CRUD 之后你都关闭了盒子,所以流(使用那个盒子)停止发出值。如果您是从 riverpod (await Hive.openBox('users')
) 之外的某个地方调用它并不重要,它调用相同的引用。只有当你停止使用它时你才应该关闭它,我建议使用 autodispose 和 riverpod 在不再使用时关闭它并且可能将那些 CRUD 方法放在由 riverpod 控制的 class 中,这样你就可以完全控制那个盒子的生命周期
final localUsersBoxFutureProvider = FutureProvider.autoDispose<Box>((ref) async {
final usersBox = await Hive.openBox('users');
ref.onDispose(() async => await usersBox?.close()); //this will close the box automatically when the provider is no longer used
return usersBox;
});
final localUserStreamProvider = StreamProvider.autoDispose<User>((ref) async* {
final usersBox = await ref.watch(localUsersBoxFutureProvider.future);
yield* Stream.value(usersBox.get(0, defaultValue: User()) as User);
yield* usersBox.watch(key: 0).map((usersBoxEvent) {
return usersBoxEvent.value == null ? User() : usersBoxEvent.value as User;
});
});
并且在您的方法中使用来自 localUsersBoxFutureProvider 的相同实例框并且不要在每个实例之后关闭该框,当您停止收听 stream
或 localUsersBoxFutureProvider
时它会自行关闭
我写了一个 StreamProvider,我在启动后立即收听,以获取有关可能登录的用户的所有信息。如果没有用户,那么结果将为 null,侦听器将保持加载状态,因此我决定发回一个空用户的默认值,让我知道加载已完成。 我必须这样做,因为 Hive 的 watch() 方法仅在数据更改时触发,而在启动时不会触发。 所以在那之后,我希望 watch() 方法完成它的工作,但问题是以下情况:
启动时:无用户 - 插入用户 -> watch 方法被触发 -> 我得到插入的用户数据 -> 删除登录用户-> 未触发监视方法。
At startup: Full user - 删除用户 -> watch 方法被触发 -> 我得到一个空用户 -> 插入用户 -> watch方法未触发。
一段时间后,我发现我可以随心所欲地使用所有 CRUD 操作,并且 Hive 的盒子做了它应该做的,但是 watch() 方法在被触发后不再被触发一次.
流媒体提供者:
final localUsersBoxFutureProvider = FutureProvider<Box>((ref) async {
final usersBox = await Hive.openBox('users');
return usersBox;
});
final localUserStreamProvider = StreamProvider<User>((ref) async* {
final usersBox = await ref.watch(localUsersBoxFutureProvider.future);
yield* Stream.value(usersBox.get(0, defaultValue: User()));
yield* usersBox.watch(key: 0).map((usersBoxEvent) {
return usersBoxEvent.value == null ? User() : usersBoxEvent.value as User;
});
});
听众:
return localUserStream.when(
data: (data) {
if (data.name == null) {
print('Emitted data is an empty user');
} else {
print('Emitted data is a full user');
}
return Container(color: Colors.blue, child: Center(child: Row(children: [
RawMaterialButton(
onPressed: () async {
final globalResponse = await globalDatabaseService.signup({
'email' : 'name@email.com',
'password' : 'password',
'name' : 'My Name'
});
Map<String, dynamic> jsonString = jsonDecode(globalResponse.bodyString);
await localDatabaseService.insertUser(User.fromJSON(jsonString));
},
child: Text('Insert'),
),
RawMaterialButton(
onPressed: () async {
await localDatabaseService.removeUser();
},
child: Text('Delete'),
)
])));
},
loading: () {
return Container(color: Colors.yellow);
},
error: (e, s) {
return Container(color: Colors.red);
}
);
CRUD 方法:
Future<void> insertUser(User user) async {
Box usersBox = await Hive.openBox('users');
await usersBox.put(0, user);
await usersBox.close();
}
Future<User> readUser() async {
Box usersBox = await Hive.openBox('users');
User user = usersBox.get(0) as User;
await usersBox.close();
return user;
}
Future<void> removeUser() async {
Box usersBox = await Hive.openBox('users');
await usersBox.delete(0);
await usersBox.close();
}
知道如何告诉 StreamProvider watch() 方法应该保持活动状态,即使已经发出了一个值吗?
but the watch() method is not triggered anymore after it got triggered once
那是因为在每次 CRUD 之后你都关闭了盒子,所以流(使用那个盒子)停止发出值。如果您是从 riverpod (await Hive.openBox('users')
) 之外的某个地方调用它并不重要,它调用相同的引用。只有当你停止使用它时你才应该关闭它,我建议使用 autodispose 和 riverpod 在不再使用时关闭它并且可能将那些 CRUD 方法放在由 riverpod 控制的 class 中,这样你就可以完全控制那个盒子的生命周期
final localUsersBoxFutureProvider = FutureProvider.autoDispose<Box>((ref) async {
final usersBox = await Hive.openBox('users');
ref.onDispose(() async => await usersBox?.close()); //this will close the box automatically when the provider is no longer used
return usersBox;
});
final localUserStreamProvider = StreamProvider.autoDispose<User>((ref) async* {
final usersBox = await ref.watch(localUsersBoxFutureProvider.future);
yield* Stream.value(usersBox.get(0, defaultValue: User()) as User);
yield* usersBox.watch(key: 0).map((usersBoxEvent) {
return usersBoxEvent.value == null ? User() : usersBoxEvent.value as User;
});
});
并且在您的方法中使用来自 localUsersBoxFutureProvider 的相同实例框并且不要在每个实例之后关闭该框,当您停止收听 stream
或 localUsersBoxFutureProvider
时它会自行关闭