我正在构建一个 Flutter 应用程序,并在 main.dart 文件中使用 StreamProvider 来提供有关登录用户数据的实时更新。
但是,我遇到了一个问题: • 当我注销并使用不同的用户帐户登录时,仍显示旧用户的信息。 • 只有当我完全关闭应用程序并重新打开它时,显示的信息才会正确更新以反映新用户。
我怀疑 Stream 或 currentUser 变量没有正确重置。我尝试了在网上找到的不同解决方案,但似乎没有任何效果。我对 Flutter 和应用程序开发的了解仍在增长,这个问题让我发疯。
相关代码如下: 主要.dart:
class _MyAppState extends State<MyApp> {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
StreamProvider<UserModel?>(
create: (context) {
final firebaseUser = FirebaseAuth.instance.currentUser?.uid;
if (firebaseUser != null) {
return OurDatabase().getUserStream(firebaseUser);
}
return Stream<UserModel?>.value(null);
},
initialData: null,
),
],
child: MaterialApp(
navigatorKey: navigatorKey,
title: 'GreenEyed',
theme: ThemeData(primaryColor: const Color.fromRGBO(113, 190, 71, 1)),
routes: {
'/': (ctx) => AuthRoot(),
'/auth_screen': (ctx) => AuthScreen(),
"/instructions_screen": (ctx) => InstructionScreen(),
'/my_profile_screen': (ctx) => MyProfileScreen(),
"/purchase_screen": (ctx) => PurchaseScreen(),
"/chat_screen": (ctx) => ChatScreen()
},
debugShowCheckedModeBanner: false,
),
);
}
}
在另一个屏幕中注销功能:
Future signOut() async {
showDialog(
context: context,
barrierDismissible: false,
builder: (context) => const Center(child: CircularProgressIndicator()));
try {
// Limpia el estado del usuario
await FirebaseAuth.instance.signOut();
// Notificar al UserAuthNotifier que el usuario ha cerrado sesión.
} on FirebaseAuthException catch (e) {
print(e);
}
navigatorKey.currentState!.popUntil((route) => route.isFirst);
}
我需要的是一种方法来确保当用户注销时,Stream或当前用户信息被正确清除或重置,以便新用户重新登录时正确显示数据。
我非常感谢任何解决此问题的指导或建议。预先感谢!
问题是您正在基于常量值创建
Stream
,即创建时(第一次需要时)用户的 uid
。
您真正想要的是观察用户在
FirebaseAuth
中的变化并对此做出反应。 “我们的”数据库似乎为所提供的用户 ID 返回 Stream<UserModel?>
。
这里棘手的部分可能是,每次用户 ID 更改时,您都想要 switch 到不同的流,但是
create
的 StreamProvider
方法只允许您在创建时返回一个流。
您可能已经注意到,这里的关键字是 switch。
rxdart
中有一个很好的运算符,它允许您构建一个流,该流可以从其他流切换其元素源并处理它们的订阅:switchMap
这是示例代码。我出于演示目的模拟了一些课程。
https://dartpad.dev/?id=ab7a9699c18353cc324ff05191aee8e6
import 'dart:async';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:rxdart/rxdart.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
StreamProvider<User?>(
create: (context) {
return FirebaseAuth.instance
.userChanges()
.map((user) => user?.uid)
.switchMap(
(userId) => switch (userId) {
null => Stream<User?>.value(null),
_ => OurDatabase().getUserStream(userId),
},
);
},
initialData: null,
),
],
child: const MaterialApp(
debugShowCheckedModeBanner: false,
home: AuthStateWidget(),
),
);
}
}
class AuthStateWidget extends StatelessWidget {
const AuthStateWidget();
Widget build(BuildContext context) {
final user = Provider.of<User?>(context);
return Scaffold(
body: Center(
child: switch (user) {
null => Text('User is signed out.'),
_ => Text('Hi, ${user.name}'),
}),
);
}
}
class User {
final String id;
final String name;
const User({
required this.id,
required this.name,
});
}
// These classes can be ignored. They are only used to mock the real classes for demo purposes.
class OurDatabase {
Stream<User?> getUserStream(String? userId) {
if (userId == null) {
return Stream<User?>.value(null);
}
return Stream.value(
User(
id: userId,
name: 'User $userId',
),
);
}
}
class FirebaseAuth {
late final Random _random = Random();
late final Stream<FirebaseUser?> _userStream =
Stream.periodic(const Duration(seconds: 2), (count) {
final isSignedOut = count % 2 == 0;
if (isSignedOut) {
return null;
}
final userId = _random.nextInt(10000);
return FirebaseUser(uid: '$userId');
});
FirebaseAuth._();
static final FirebaseAuth instance = FirebaseAuth._();
Stream<FirebaseUser?> userChanges() => _userStream;
}
class FirebaseUser {
final String uid;
const FirebaseUser({
required this.uid,
});
}