刷新/重置StreamProvider - Flutter Firebase

p4tfgftt  于 2023-03-03  发布在  Flutter
关注(0)|答案(3)|浏览(150)

在我的应用程序中,我有以下提供商。

@override
  Widget build(BuildContext context) {
    return OverlaySupport.global(
      child: MultiProvider(
        providers: [userLoggedIn, currentUserData],
        child: MaterialApp(...)))
}
var userLoggedIn = StreamProvider<User?>.value(
      value: FirebaseAuth.instance.authStateChanges(), initialData: null);

  var currentUserData = StreamProvider<FrediUser>.value(
    updateShouldNotify: (_, __) => true,
    initialData: FrediUser(
      loginProvider: '',
      email: '',
      admin: false,
        profileSettings: [],
        profileChips: [],
        profileStats: [],
        id: 'loading',
        imageUrl: 'loading',
        bio: 'loading',
        username: 'loading'),
    value: currentUserID != null ? currentUserDataStream() : null,
  );

问题
当用户注销(或首次登录)时,提供程序为:

  • 包含旧用户数据(直到热重启完成,再次调用并重新加载提供程序)
  • Null或空,因为之前没有用户。
    • 我想做的是**在有了新用户后再次刷新或调用Stream Providers,或者在用户注销后删除所有数据。

谢谢大家!

py49o6xq

py49o6xq1#

您可以像这样监听auth状态的变化。

FirebaseAuth.instance
  .authStateChanges()
  .listen((User? user) {
    if (user == null) {
      print('User is currently signed out!');
    } else {
      print('User is signed in!');
    }
  });
wrrgggsh

wrrgggsh2#

我也遇到过与您类似的问题,我提出了一个解决方案,但不确定它在Provider体系结构中是否"有效

问题

我有一个DatabaseService类,它有一个Stream<CustomUser>函数类型的流函数,我是这样使用它的:

//--- main.dart ---//

runApp(MultiProvider(
      providers: [
      // ..some other providers.. //

        // data provider
        Provider<DatabaseService?>(
          create: (_) => databaseService,
        ),

        // data provider
        StreamProvider<CustomUser?>(
          create: (context) => databaseService.getCurrUserFromDb(),
          lazy: false,
          initialData: null,
          updateShouldNotify: (_, __) => true,
        ),

      ],
      child: MyApp(
        initPage: initPage,
      )
));

流函数:

//--- database_service.dart ---//

  // gets the user from database and
  // assigns it to the variable _user.
  Stream<CustomUser?> getCurrUserFromDB() async* {
    try {
      CustomUser? currUser;

      if (_user != null) {
        await for (DocumentSnapshot<Object?> event
            in users.doc(user.uid).snapshots()) {
          final jsonMap = event.data() as Map<String, dynamic>;
          currUser = CustomUser.fromJson(jsonMap);
          _user = currUser;
          CustomPreferences.setCurrUser(_user);
          yield currUser;
        }
      }
    } catch (e) {
      rethrow;
    }
  }

databaseService是具有命名构造函数的DatabaseService类。

这***不会***导致小部件在开始时重建,也不会在流具有新值时重建

溶液:

DatabaseService类中创建了一个StreamController,当用户登录时,我添加了流函数:getCurrUserFromDB()StreamController,就像这样

//--- authentication_screen.dart ---//

...
ElevatedButton(
    child: const Text("Sign In"),
    onPressed: () async {
      final user = await AuthService().googleSignIn();
      if (user != null) {
        final dbProvider = context.read<DatabaseService?>();
        await dbProvider?.setInitUser(user, context);
        await dbProvider?.cusUserController
            .addStream(dbProvider.getCurrUserFromDB());
      }
    }),
...

setInitUser(CustomUser? user)用于设置DatabaseService_user变量的值,user用于获取此变量。

推理

我***在应用程序启动时创建***StreamProvider,其源StreamController需要有一个流来侦听,所以我在尝试登录时提供了它。
或者更干净的解决方案是在DatabaseService类的构造函数中这样做:

//--- database_service.dart ---//

// method to add the stream to controller //
Future<void> addStream() async {
    if (!_cusUserController.isClosed && !_cusUserController.isPaused) {
      await _cusUserController.addStream(getCurrUserFromDB());
    }
  }

// constructor //
DatabaseService._init(CustomUser cusUser) {
    addStream();
    _user = cusUser;
  }

最后一点要注意的是,我没有声明控制器为final,当我声明为final时,流没有更新,所以现在看起来像这样:

//--- database_service.dart ---//

    StreamController<CustomUser?> _cusUserController = StreamController();

TL; DR

我创建了一个StreamProvider,它在其create属性中返回一个StreamController,后来在应用程序的生命周期中,我使用addStream方法为控制器提供了一个Stream。
抱歉,墙上的文字,我只是想出来尽可能清楚。

7y4bm7vi

7y4bm7vi3#

如果要控制何时可以重新生成StreamProvider,可以尝试使用来自StreamControllerStream,而不是返回流的Function
使用StreamController允许您使用以下命令向其流添加值:streamController.sink.add(event);。执行此操作将***刷新***流,从而允许您更新UI。
我在本例中使用Firebase,因此代码片段可能包含一些FirebaseAuth方法。

溶液

我创建了一个名为auth_helper.dart的类来处理身份验证。
class AuthHelper {

  // Creating a StreamController that has a Stream which will return the `User?`
  static final StreamController<User?> currUserController =
      StreamController<User?>();

  // This function is called in the main method to make sure the stream of my
  // stream controller starts listening to the changes when the auth state changes
  static init() async {
    FirebaseAuth.instance.authStateChanges().listen((event) {
      currUserController.sink.add(event);
    });
  }

  // This is an optional method that you can use to add a User object programmatically
  // This value can be null.
  static clearUserProgrammatically({User? user}) {
    if (!currUserController.isClosed && !currUserController.isPaused) {
      currUserController.sink.add(user);
    }
  }
}
我正在main.dart中调用此函数

runApp()之前调用它,以便我的currUserController在Firebase初始化之后立即开始侦听Auth更改。

// Firebase Initialization, etc.,
  AuthHelper.init();
  runApp(const MyApp());
MultiProvider现在将如下所示
MultiProvider(
    providers: [
      StreamProvider(
          // passing the stream of currUserController instead of 
          // `FirebaseAuth.instance.authStateChanges()`
          create: (_) => AuthHelper.currUserController.stream,
          initialData: null)
      ],
...
)
最后,这是手动/编程更新流的方法
ElevatedButton(
    child: const Text('Clean up provider by code'),
    onPressed: () async {
        AuthHelper.clearUserProgrammatically(null);
    },
),

source code

相关问题