繁体   English   中英

Flutter - 使用提供商 package 监听 Firebase 用户的用户配置文件 (Firestore) 中的数据更改

[英]Flutter - Listening for data changes in user profile (Firestore) of Firebase user using provider package

到现在为止,我成功地将我的 Firebase 用户的身份验证更改为 map 并将其更改为我自己的自定义用户 class

import 'package:mypckg/models/user.dart' as local;
import 'package:firebase_auth/firebase_auth.dart' as auth;
import 'package:mypckg/services/database.dart';

class AuthService {
  final auth.FirebaseAuth _auth = auth.FirebaseAuth.instance;

  // create user obj based on firebase user
  Future<local.User> _userFromFirebaseUser(auth.User user) async {
    return user != null
        ? local.User(/* ... init user properties ... */)
        : null;
  }

  // auth change user stream
  Stream<local.User> get user {
    return _auth.authStateChanges().asyncMap(_userFromFirebaseUser);
  }

...

在 Cloud Firestore 中,我存储了该用户的其他值,这些值未被 Firebase 用户覆盖,例如

Cloud Firestore 中的用户集合

main.dart中,提供程序设置为向本地用户提供我的应用程序,以防他登录或注销 (authStateChanges)。 我的想法是订阅另一个 stream,它将监听 Cloud Firestore 中“用户”文档的变化。

class MyPckg extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final i18n = I18n.delegate;
    //AuthService().signInAnon();
    return MultiProvider(
      providers: [
        StreamProvider<User>(
          create: (_) => AuthService().user,
        ),
        /* my idea to subscribe to another stream which will listen to changes on user details in Firestore */
        StreamProvider<User>(
          create: (_) => AuthService().customUser,
        ),
      ],
      child: DynamicTheme(
        defaultBrightness: Brightness.light,
        data: (brightness) {

...

我有一个配置文件视图,用户可以在其中编辑这些值,例如语言环境,它会正确地写入 Firestore

Future<void> updateUserLanguage(String language) async {
    return await usersCollection.doc(uid).update({
      'language': language,
    });
  }

但是视图不会重建,因为当前的 stream 仅对 authStateChanges 做出反应。

有没有人有一个工作示例,如何设置来自 Firestore 集合中用户的链接,我的应用程序将监听那里所做的更改? 我的 customUser 方法必须是什么样子?

谢谢!

我决定用 StreamProvider 包装我登录的 state 中的所有内容:

class Wrapper extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final user = Provider.of<User>(context);
    if (user == null) {
      return Authenticate();
    } else {
      return StreamProvider<Profile>.value(
        value: DatabaseService(uid: user.uid).profile,
        initialData: Profile(userName: '0', userRole: '0'),
        child: HomeScreen(),
      );
    }
  }
}

数据库服务接收 UID 并返回快照:

class DatabaseService {
  final String uid;
  DatabaseService({
    this.uid,
  });

static CollectionReference _profileCollection =
      FirebaseFirestore.instance.collection('profiles');

  Stream<Profile> get profile {
    return _profileCollection.doc(uid).snapshots().map(_profileFromSnapshot);
  }

  Profile _profileFromSnapshot(DocumentSnapshot snapshot) {
    return Profile(
      userName: snapshot.data()['userName'] ?? '',
      userRole: snapshot.data()['role'] ?? '',
    );
  }
}

最后,我在任何需要的地方通过消费者使用数据:

class HomeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
        child: Center(
          child: Consumer<Profile>(builder: (
            context,
            profile,
            child,
          ) {
            return Text(profile.userName)
...

编辑:我制作了有关此技术的视频。 链接: https://youtu.be/mAH9YT_y6ec

对于那些也在努力使用这种方法的人,这是我的解决方案(感谢所有引导我采用这种方法的贡献者):

我在 MyClass 中设置了一个方法“setAppUser”,任何孩子都可以调用它。 对此很重要

static _MyClassState? of(BuildContext context) =>
          context.findAncestorStateOfType<_MyClassState>();

所以这里是 main.dart(只有必要的部分):

class MyClass extends StatefulWidget {
  @override
  _MyClassState createState() => _MyClassState();
  static _MyClassState? of(BuildContext context) =>
      context.findAncestorStateOfType<_MyClassState>();
}

class _MyClassState extends State<MyClass> {
  User appUser = User();

  set setAppUser(User user) => {
        setState(() {
          appUser = user;
        })
      };

  @override
  Widget build(BuildContext context) {
  ...
    return MultiProvider(
        providers: [
          StreamProvider<UserLogin>(
            initialData: UserLogin(signedIn: false),
            create: (_) => AuthService().userAuthStateChanges,
            catchError: (_, error) => UserLogin(signedIn: false),
          ),
          StreamProvider<User>(
            initialData: appUser,
            create: (_) => AuthService().userData,
            catchError: (_, error) => appUser,
          ),
        ],
        child: MaterialApp(
        ...

在我的登录表单中,在收到我的登录新用户后,我调用了 main.dart 的“setAppUser”方法。 现在小部件被重建,新的 appUser 被设置为我的提供者。

  ...
  await _auth
    .signInWithEmailAndPassword(
    textControllerEmail.text,
    textControllerPassword.text)
      .then((result) {
        if (result.id != "") {
          MyClass.of(context)!.setAppUser = result;
          ...

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM