Flutter实时用户数据同步插件firebase_user_stream的使用

Flutter实时用户数据同步插件firebase_user_stream的使用

Firebase User Stream

一个用于订阅用户重新加载更新的包。


注意事项

firebase_auth 版本 0.18.0 起,此库的功能已变得几乎无用,因为 firebase_auth 终于添加了 authStateChanges() 方法,这在很大程度上取代了此包的功能,并解决了以下描述的问题。

不过,此包仍然有一些功能可能对某些人有用:

  • 基于谓词的重新加载
  • 在同一行中重新加载并获取用户

尽管这些功能对大多数人来说并不实用。


旧版本(firebase_auth 版本低于 0.18.0)的问题

FirebaseAuth Flutter 插件通过 onAuthStateChanged 提供了一个 Stream<FirebaseUser>,这对于检测用户登录或登出非常有用。然而,它无法检测到用户数据本身的变化。

常见的问题是,当我们想检测用户是否刚刚验证了电子邮件地址时,即检查 FirebaseUser.isEmailVerified 是否发生了变化。这是因为此信息是由服务器更新的,而不是由应用推送的。

FirebaseAuth.currentUser() 只会检测到本地对用户所做的更改,但如果发生任何服务器端更改,则不会检测到它们,除非先调用 FirebaseUser.reload(),然后再次调用 FirebaseAuth.currentUser() 来获取重新加载后的用户。但即使如此,此用户也不会由 onAuthStateChanged 发出。


解决方案

有两种方法可以获取带有最新服务器信息的新 FirebaseUser

  1. 手动实现
Future<FirebaseUser> reloadCurrentUser() async {
  FirebaseUser oldUser = await FirebaseAuth.instance.currentUser();    
  oldUser.reload();
  FirebaseUser newUser = await FirebaseAuth.instance.currentUser();
  // 将 newUser 添加到 Stream 中,也许可以将其与 onAuthStateChanged 合并?
  return newUser; 
}
  1. 让我们为您完成!

开始使用

安装此包后,您可以使用 FirebaseUserReloader 来通过 onUserReloadedonAuthStateChangedOrReloaded 流获取重新加载更新。

FirebaseUserReloader 内部使用通过 FirebaseAuth.instance 返回的 FirebaseAuth 实例,因此您的整个应用将具有相同的单例实例,无需进行任何配置即可直接使用。

为了获取重新加载更新,请执行以下操作:

var subscription = FirebaseUserReloader.onUserReloaded.listen((user) {
  // 每次重新加载时都会打印新用户
  print(user);
});

// 这将触发重新加载,并且重新加载后的用户将由 onUserReloaded 发出
FirebaseUserReloader.reloadCurrentUser();

subscription.cancel();

每次调用 reloadCurrentUser() 时,重新加载后的用户将由 onUserReloaded 流发出。

请注意,onUserReloaded 将发出重新加载后的用户,这些用户可能是新的,也可能是当前用户如果用户数据未发生变化。

作为额外的好处,您可以为 reloadCurrentUser() 提供一个谓词,以便仅获取符合您条件的重新加载后的用户:

var subscription = FirebaseUserReloader.onUserReloaded.listen((user) {
  // 每次重新加载时都会打印新用户
  print(user);
});

// 这将触发重新加载,并且只有当 isEmailVerified == true 时,重新加载后的用户才会由 onUserReloaded 发出
FirebaseUserReloader.reloadCurrentUser((user) => user.isEmailVerified);

subscription.cancel();

您还可以将 reloadCurrentUser 的返回值作为重新加载后的用户,但在这种情况下,谓词将被忽略,重新加载后的用户总是会被返回。

var user = await FirebaseUserReloder.reloadCurrentUser();

如果您想监听登录、登出和用户重新加载的更新,请改用 onAuthStateChangedOrReloaded,它是 onUserReloadedFirebaseAuth.onAuthStateChanged 的便捷合并版本,并且作为一个广播流 <a href="https://pub.dev/documentation/rxdart/latest/rx/BehaviorSubject-class.html">Behavior Subject</a> 工作:

var subscription = FirebaseUserReloder.onAuthStateChangedOrReloaded.listen((user) {
  // 每次重新加载、登录或登出时都会打印新用户
  print(user);
});

// 这将触发重新加载,并且只有当 isEmailVerified == true 时,重新加载后的用户才会由 onUserReloaded 发出
FirebaseUserReloder.reloadCurrentUser((user) => user.isEmailVerified);

subscription.cancel();

onAuthStateChangedOrReloadedonUserReloaded 都是广播流。


测试

虽然此库使用了 static 方法以方便使用,但这并不会限制其可测试性。

FirebaseUserReloder 可以注入一个 FirebaseAuth 的模拟实例以进行单元测试。

有关如何在测试中控制其行为的示例,请查看我们自己的测试文件夹中的测试。


示例代码

以下是一个完整的示例代码,展示如何在项目中使用 firebase_user_stream

import 'package:firebase_user_stream/firebase_user_stream.dart';

void main() {
  // 监听用户重新加载事件
  FirebaseUserReloder.onUserReloaded.listen((user) {
    print('Reloaded: $user');
  });

  // 监听用户重新加载或身份状态变化事件
  FirebaseUserReloder.onAuthStateChangedOrReloaded.listen((user) {
    print('Reloaded or auth state changed: $user');
  });

  // 手动触发用户重新加载
  FirebaseUserReloder.reloadCurrentUser();
  FirebaseUserReloder.reloadCurrentUser((user) => user.emailVerified);
}

更多关于Flutter实时用户数据同步插件firebase_user_stream的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter实时用户数据同步插件firebase_user_stream的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


firebase_user_stream 是一个用于在 Flutter 应用中实时同步 Firebase 用户数据的插件。它允许你监听 Firebase 用户数据的变化,并在数据发生变化时自动更新 UI。以下是如何使用 firebase_user_stream 插件的步骤:

1. 添加依赖

首先,你需要在 pubspec.yaml 文件中添加 firebase_user_stream 插件的依赖:

dependencies:
  flutter:
    sdk: flutter
  firebase_core: latest_version
  firebase_auth: latest_version
  firebase_user_stream: latest_version

然后运行 flutter pub get 来安装依赖。

2. 初始化 Firebase

在使用 firebase_user_stream 之前,你需要初始化 Firebase。通常,你可以在 main.dart 文件中进行初始化:

import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  runApp(MyApp());
}

3. 使用 firebase_user_stream

firebase_user_stream 提供了一个 FirebaseUserStream 类,你可以使用它来监听 Firebase 用户数据的变化。

以下是一个简单的示例,展示如何使用 FirebaseUserStream 来监听用户数据的变化并更新 UI:

import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_user_stream/firebase_user_stream.dart';

class UserProfilePage extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('User Profile'),
      ),
      body: FirebaseUserStream(
        builder: (BuildContext context, User? user) {
          if (user == null) {
            return Center(child: Text('No user logged in.'));
          }

          return Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Text('User ID: ${user.uid}'),
                Text('Email: ${user.email ?? 'No email'}'),
                Text('Display Name: ${user.displayName ?? 'No display name'}'),
                Text('Phone Number: ${user.phoneNumber ?? 'No phone number'}'),
              ],
            ),
          );
        },
      ),
    );
  }
}

4. 解释代码

  • FirebaseUserStream 是一个 StreamBuilder 的封装,它监听 Firebase 用户数据的变化。
  • builder 方法接收一个 BuildContext 和一个 User? 对象。User? 对象是当前登录的用户,如果用户未登录,则为 null
  • builder 方法中,你可以根据用户数据的变化来更新 UI。

5. 处理用户登录和登出

你可以使用 FirebaseAuth 来处理用户的登录和登出操作。例如:

import 'package:firebase_auth/firebase_auth.dart';

Future<void> signInWithEmailAndPassword(String email, String password) async {
  try {
    await FirebaseAuth.instance.signInWithEmailAndPassword(
      email: email,
      password: password,
    );
  } catch (e) {
    print('Failed to sign in: $e');
  }
}

Future<void> signOut() async {
  await FirebaseAuth.instance.signOut();
}

6. 完整示例

以下是一个完整的示例,展示了如何使用 firebase_user_streamFirebaseAuth 来管理用户登录和登出:

import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_user_stream/firebase_user_stream.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Firebase User Stream Example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: UserProfilePage(),
    );
  }
}

class UserProfilePage extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('User Profile'),
      ),
      body: FirebaseUserStream(
        builder: (BuildContext context, User? user) {
          if (user == null) {
            return Center(child: Text('No user logged in.'));
          }

          return Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Text('User ID: ${user.uid}'),
                Text('Email: ${user.email ?? 'No email'}'),
                Text('Display Name: ${user.displayName ?? 'No display name'}'),
                Text('Phone Number: ${user.phoneNumber ?? 'No phone number'}'),
                ElevatedButton(
                  onPressed: () async {
                    await FirebaseAuth.instance.signOut();
                  },
                  child: Text('Sign Out'),
                ),
              ],
            ),
          );
        },
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () async {
          await signInWithEmailAndPassword('user@example.com', 'password');
        },
        child: Icon(Icons.login),
      ),
    );
  }
}

Future<void> signInWithEmailAndPassword(String email, String password) async {
  try {
    await FirebaseAuth.instance.signInWithEmailAndPassword(
      email: email,
      password: password,
    );
  } catch (e) {
    print('Failed to sign in: $e');
  }
}
回到顶部