当用户更改时,监听用户的 StreamProvider 不会更新
StreamProvider listening to User doesn't update when User changes
在我的应用程序中,我监听 Cloud Firestore 中用户文档的更改。
我通过获取当前用户 ID,然后获取与该 ID 关联的文档来执行此操作。
class UserService {
...
//GET A USER'S INFORMATION AS A STREAM
// ? IF NO UID IS PASSED, IT GETS THE INFO OF THE CURRENT USER
Stream<User> getUserInfoAsStream({String uid}) async* {
if (uid == null) {
uid = await AuthService().getUID();
}
yield* Firestore.instance
.collection('users')
.document(uid)
.snapshots()
.map((doc) => User.fromFirestore(doc));
}
...
然后我使用 StreamProvider
收听我 main.dart
中的流
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
StreamProvider<User>.value(
value: UserService().getUserInfoAsStream(),
),
],
child: MaterialApp(
debugShowCheckedModeBanner: false,
home: SplashScreen(),
),
);
}
}
在应用程序的生命周期中,它运行完美,但是当用户使用 FirebaseAuth.instance.signOut();
注销然后使用其他用户登录时,流保持不变(即它监听旧的 uid 流),StreamProvider 不监听新的数据流。
| 注销代码供参考 |
// ? SIGN OUT CODE: If user signed out, it returns true, else, false
Future<bool> signOut() async {
try {
await _firebaseAuth.signOut();
return true;
} catch (error) {
print(error);
return false;
}
}
| 用在什么地方 |
FlatButton(
onPressed: () {
AuthService().signOut().then((value) =>
Navigator.of(context).pushAndRemoveUntil(
CupertinoPageRoute(
builder: (BuildContext context) {
return Onboarding();
}), (route) => false));
},
child: Text("Yes")),
为了解决这个问题,我会把当前的uid传递给StreamProvider,但是我只能异步获取当前的uid。
如何使用 StreamProvider 监听异步流,并在用户更改时更新它?
编辑:我设法在某种程度上解决了这个问题,方法是在登录页面之后立即将提供者从小部件树上移到屏幕上。但是因为提供者是有范围的,所以我不得不在我原来的 MaterialApp 之后创建一个全新的 MaterialApp,这弄乱了我应用程序中的一些组件。
有没有更好的解决方法?
我设法通过从 provider
包切换到 get_it
来解决问题。
get_it
允许您注册和注销单例,这意味着当用户登录时,我可以注册单例,以便它可以在依赖它的所有屏幕上使用。然后,当我注销时,我只是注销它。这样,用户总是在登录和注销后更新。
这是你自己做的方法。
在您的 pubspec.yaml.
中安装软件包 get_it
get_it: ^4.0.2
在您的 main.dart
旁边创建一个名为 locator.dart
的新文件。在其中,添加此代码:
GetIt locator = GetIt.instance;
void setupLocator() {
// Replace this with the object you're trying to listen to.
User user;
Stream<User> userStream = UserService().getUserInfoAsStream();
userStream.listen((event) => user = event);
locator.registerLazySingleton(() => user); // Register your object
}
登录时,只需调用setupLocator();
,退出时,使用此代码:
locator.unregister<User>();
这就是我所做的一切,运行!
编辑:我设法通过使用 UserProvider
单例来使它变得更好更轻,该单例侦听身份验证的变化,然后在用户登录时获取当前用户。
import 'package:planster/models/core/user.dart';
import 'package:planster/models/services/auth_service.dart';
import 'package:planster/models/services/user_service.dart';
class UserProvider {
// SINGLETON INITIALIZATION
static final UserProvider _singleton = UserProvider._internal();
factory UserProvider.instance() {
return _singleton;
}
UserProvider._internal() {
listenToUserAuthState();
}
// VARIABLES
User user;
void listenToUserAuthState() async {
AuthService().onAuthStateChanged.listen((event) {
uid = event.uid;
if (uid != null)
UserService().getUserInfoAsStream(uid: uid).listen((userEvent) {
user = userEvent;
});
});
}
}
在我的应用程序中,我监听 Cloud Firestore 中用户文档的更改。 我通过获取当前用户 ID,然后获取与该 ID 关联的文档来执行此操作。
class UserService {
...
//GET A USER'S INFORMATION AS A STREAM
// ? IF NO UID IS PASSED, IT GETS THE INFO OF THE CURRENT USER
Stream<User> getUserInfoAsStream({String uid}) async* {
if (uid == null) {
uid = await AuthService().getUID();
}
yield* Firestore.instance
.collection('users')
.document(uid)
.snapshots()
.map((doc) => User.fromFirestore(doc));
}
...
然后我使用 StreamProvider
收听我 main.dart
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
StreamProvider<User>.value(
value: UserService().getUserInfoAsStream(),
),
],
child: MaterialApp(
debugShowCheckedModeBanner: false,
home: SplashScreen(),
),
);
}
}
在应用程序的生命周期中,它运行完美,但是当用户使用 FirebaseAuth.instance.signOut();
注销然后使用其他用户登录时,流保持不变(即它监听旧的 uid 流),StreamProvider 不监听新的数据流。
| 注销代码供参考 |
// ? SIGN OUT CODE: If user signed out, it returns true, else, false
Future<bool> signOut() async {
try {
await _firebaseAuth.signOut();
return true;
} catch (error) {
print(error);
return false;
}
}
| 用在什么地方 |
FlatButton(
onPressed: () {
AuthService().signOut().then((value) =>
Navigator.of(context).pushAndRemoveUntil(
CupertinoPageRoute(
builder: (BuildContext context) {
return Onboarding();
}), (route) => false));
},
child: Text("Yes")),
为了解决这个问题,我会把当前的uid传递给StreamProvider,但是我只能异步获取当前的uid。
如何使用 StreamProvider 监听异步流,并在用户更改时更新它?
编辑:我设法在某种程度上解决了这个问题,方法是在登录页面之后立即将提供者从小部件树上移到屏幕上。但是因为提供者是有范围的,所以我不得不在我原来的 MaterialApp 之后创建一个全新的 MaterialApp,这弄乱了我应用程序中的一些组件。
有没有更好的解决方法?
我设法通过从 provider
包切换到 get_it
来解决问题。
get_it
允许您注册和注销单例,这意味着当用户登录时,我可以注册单例,以便它可以在依赖它的所有屏幕上使用。然后,当我注销时,我只是注销它。这样,用户总是在登录和注销后更新。
这是你自己做的方法。
在您的 pubspec.yaml.
中安装软件包get_it
get_it: ^4.0.2
在您的 main.dart
旁边创建一个名为 locator.dart
的新文件。在其中,添加此代码:
GetIt locator = GetIt.instance;
void setupLocator() {
// Replace this with the object you're trying to listen to.
User user;
Stream<User> userStream = UserService().getUserInfoAsStream();
userStream.listen((event) => user = event);
locator.registerLazySingleton(() => user); // Register your object
}
登录时,只需调用setupLocator();
,退出时,使用此代码:
locator.unregister<User>();
这就是我所做的一切,运行!
编辑:我设法通过使用 UserProvider
单例来使它变得更好更轻,该单例侦听身份验证的变化,然后在用户登录时获取当前用户。
import 'package:planster/models/core/user.dart';
import 'package:planster/models/services/auth_service.dart';
import 'package:planster/models/services/user_service.dart';
class UserProvider {
// SINGLETON INITIALIZATION
static final UserProvider _singleton = UserProvider._internal();
factory UserProvider.instance() {
return _singleton;
}
UserProvider._internal() {
listenToUserAuthState();
}
// VARIABLES
User user;
void listenToUserAuthState() async {
AuthService().onAuthStateChanged.listen((event) {
uid = event.uid;
if (uid != null)
UserService().getUserInfoAsStream(uid: uid).listen((userEvent) {
user = userEvent;
});
});
}
}