首页 > 解决方案 > 如何拥有需要用户 ID 的流提供程序?

问题描述

我有一个多提供者,它有一个身份验证提供者和一个用户提供者,并且围绕着主应用程序。身份验证提供程序有一个异步方法,该方法返回一个Future<AuthenticationCertificate>. 用户提供者有一个需要该身份验证证书的流。那么如何将证书获取到我的用户提供程序中呢?

这是我的 multiprovider,它包裹在主应用程序周围

return MultiProvider(
  providers: [
    ChangeNotifierProvider<PreferencesProvider>(
        create: (_) => PreferencesProvider()),
    Provider<AuthenticationProvider>(create: (_) => AuthenticationProvider(),),
    Provider<GroupProvider>(create: (_) => GroupProvider()),
    Provider<UserProvider>(
      create: (_) => UserProvider(),
    ),
    StreamProvider(create: (context) {
      return Provider.of<UserProvider>(context).authenticatedUserStream(Provider.of<AuthenticationProvider>(context).userAuthenticationCertificate());
    }),
  ],

这是我的身份验证提供程序

class AuthenticationProvider {
  final FirebaseAuth _authentication = FirebaseAuth.instance;
  String _smsCode;
  String _verificationID;

  Future<void> registerWithPhone(
      {String phoneNumber = '',
      BuildContext context,
      Function(AuthCredential, BuildContext) onLogin,
      Function(AuthException, BuildContext) onLoginFailed}) async {
    String localizedPhoneNumber = phoneNumber.toString();

    return await _authentication.verifyPhoneNumber(
        phoneNumber: localizedPhoneNumber,
        timeout: Duration(seconds: 30),
        verificationCompleted: (authCredential) =>
            _verificationComplete(authCredential, context, onLogin),
        verificationFailed: (authException) =>
            _verificationFailed(authException, context, onLoginFailed),
        codeAutoRetrievalTimeout: (verificationId) =>
            _codeAutoRetrievalTimeout(verificationId),
        codeSent: (verificationId, [code]) =>
            _smsCodeSent(verificationId, [code]));
  }

  _verificationComplete(AuthCredential authCredential, BuildContext context,
      Function(AuthCredential, BuildContext) onLogin) {
    FirebaseAuth.instance
        .signInWithCredential(authCredential)
        .then((authResult) {
      onLogin(authCredential, context);
    });
  }

  _verificationFailed(AuthException exception, BuildContext context,
      Function(AuthException, BuildContext) onLoginFailed) {
    onLoginFailed(exception, context);
  }

  _smsCodeSent(String verificationId, List<int> code) {
    _smsCode = verificationId;
  }

  _codeAutoRetrievalTimeout(String verificationId) {
    _smsCode = verificationId;
  }

  Future<bool> isSignedIn() async {
    return await _authentication.currentUser() != null;
  }

  Future<UserAuthenticationCertificate> userAuthenticationCertificate() async
  {
    FirebaseUser authenticatedUser = await _authentication.currentUser();
    if(authenticatedUser != null)
      return UserAuthenticationCertificate.fromFirebase(authenticatedUser);
    return null;
  }

  Future deleteAuthenticatedUser() async
  {
    FirebaseUser authenticatedUser = await _authentication.currentUser();
    if(authenticatedUser != null)
      return authenticatedUser.delete();
    return null;
  }
}

这是我的用户提供者

class UserProvider {
  static const userCollectionKey = 'users';
  final CollectionReference userCollection =
      Firestore.instance.collection(userCollectionKey);

  Stream<UserModel> authenticatedUserStream(UserAuthenticationCertificate certificate) {
    Stream<DocumentSnapshot> userData = userCollection.document(certificate.userID).snapshots();
    Stream<DocumentSnapshot> privateUserData = userCollection.document(certificate.userID).collection('private').document('data').snapshots();
    Stream<DocumentSnapshot> publicUserData = userCollection.document(certificate.userID).collection('public').document('data').snapshots();
    return CombineLatestStream([userData, privateUserData, publicUserData], (values) => values.toList()).asBroadcastStream().map((snapshot) => UserModel.fromFirebase(snapshot));
  }
}

这是认证证书

class UserAuthenticationCertificate
{
  String _userID;

  String get userID{
    return _userID;
  }

  UserAuthenticationCertificate._internal(this._userID);

  factory UserAuthenticationCertificate.fromFirebase(FirebaseUser firebaseUser)
  {
    return UserAuthenticationCertificate._internal(
      firebaseUser.uid
    );
  }
}

问题是当我想定义我的流提供者时

StreamProvider(create: (context) {
  return Provider.of<UserProvider>(context).authenticatedUserStream(Provider.of<AuthenticationProvider>(context).userAuthenticationCertificate());
}),

由于 userAuthenticationCertificate() 是异步的,因此出现错误。如果用户未登录,也可能返回 null。我该如何处理他的问题?

编辑

这是我目前拥有的,不会引发智能感知错误

  Stream<UserModel> authenticatedUserStream(Future<UserAuthenticationCertificate> certificateFuture) async* {
    UserAuthenticationCertificate certificate = await certificateFuture;
    Stream<DocumentSnapshot> userData = userCollection.document(certificate.userID).snapshots();
    Stream<DocumentSnapshot> privateUserData = userCollection.document(certificate.userID).collection('private').document('data').snapshots();
    Stream<DocumentSnapshot> publicUserData = userCollection.document(certificate.userID).collection('public').document('data').snapshots();
    yield* CombineLatestStream([userData, privateUserData, publicUserData], (values) => values.toList()).asBroadcastStream().map((snapshot) => UserModel.fromFirebase(snapshot));
  }

但是当我尝试在子小部件中使用提供程序时

class _ProfileListState extends State<ProfileList> {
  @override
  Widget build(BuildContext context) {
    final userModel = Provider.of<UserModel>(context);
    return MediaQuery.removePadding(
        context: context,
        removeTop: true,
        removeLeft: true,
        removeRight: true,
        child: ListView.separated(
            itemCount: 3,
            physics: NeverScrollableScrollPhysics(),
            separatorBuilder: (BuildContext context, int index) =>
                Divider(height: 1.0),
            itemBuilder: (BuildContext context, int index) {
              return [
                ListTile(
                  title: Text(
                    'Name',
                    style: Theme.of(context).textTheme.body1,
                  ),
                  trailing: Text(
                    userModel.name,
                    style: Theme.of(context).textTheme.subtitle,
                  ),
                ),
                ListTile(
                  title: Text(
                    'Phone Number',
                    style: Theme.of(context).textTheme.body1,
                  ),
                  trailing: Text(
                    userModel.phoneNumber,
                    style: Theme.of(context).textTheme.subtitle,
                  ),
                ),
                ListTile(
                  title: Text(
                    'Delete',
                    style: Theme.of(context).textTheme.body1,
                  ),
                  trailing: Icon(
                    Icons.delete,
                    color: Theme.of(context).primaryColor,
                  ),
                  onTap: () => print('delete'),
                ),
              ][index];
            }));
  }
}

该行final userModel = Provider.of<UserModel>(context);引发以下错误

在此处输入图像描述

标签: flutterdartstate

解决方案


尝试从多提供者列表中删除 StreamProvider 并将其包装在创建时需要它的类周围。

class ProfileList extends StatefulWidget {
  static Widget create(BuildContext context) {
    final cert = Provider.of<AuthenticationProvider>(context)
        .userAuthenticationCertificate();

    return StreamProvider(
      create: (context) {
        return Provider.of<UserProvider>(context).authenticatedUserStream(cert);
      },
      child: ProfileList(),
    );
  }

  @override
  _ProfileListState createState() => _ProfileListState();
}

class _ProfileListState extends State<ProfileList> {
  @override
  Widget build(BuildContext context) {
    ...
  }
}

然后你可以调用 ProfileList 就像ProfileList.create(context)ProfileList()


推荐阅读