flutter - 如何拥有需要用户 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);
引发以下错误
解决方案
尝试从多提供者列表中删除 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()
推荐阅读
- scala - 仅使用 Play Json 进行验证
- scala - sbt-buildinfo 插件:如何将编译时生效的 java 版本添加到生成的 BuildInfo.scala 中?
- http - URL 路径和查询字符串和片段的好名称?
- c++ - 通过 ULONG *
- ruby-on-rails - Rails 5 升级后性能大幅下降
- typescript - 将 Firebase 自动生成的 noreply 电子邮件与 Cloud Functions 结合使用
- visual-studio-2017 - Visual Studio 2017 Nuget 安装包构建项目而不是安装/卸载包
- mysql - 避免多重连接以优化 MYSQL 中的查询
- python - 使用直方图进行特征分析——LinAlgError:奇异矩阵
- postgresql - pgadmin4 无法连接到我的 postgres 数据库