我使用 Riverpod 中的 statenotifier
实现了用户身份验证,但我不知道它是如何以这种方式工作的。我必须创建两个提供程序,一个 StateNotifierProvider
用于读取值,一个 Provider
用于相同的 statenotifier
以访问 statenotifier
中的函数。
final signInStateNotifierProvider =
StateNotifierProvider((ref) => SignInStateNotifier(authentication));
final signInProvider =
Provider((ref) => ref.watch(signInStateNotifierProvider));
这是我的 statenotifier 类
class SignInStateNotifier extends StateNotifier<AuthFormStates> {
SignInStateNotifier(this._authFacade) : super(AuthFormStates.initial());
final SignInAuthFacade _authFacade;
Future mapEventToState(SignInFormEvents event) async {
event.map(
// email changed
emailChanged: (event) {
state = state.copyWith(
emailAddress: EmailAddress(event.email),
authFailureOrSuccess: none(),
);
},
// password changed
passwordChanged: (event) {
state = state.copyWith(
password: Password(event.password),
authFailureOrSuccess: none(),
);
},
signInWithEmailAndPasswordPressed: (event) async {
await _performActionWithEmailAndPassword(
_authFacade.signInWithEmailAndPassword,
);
},
);
}
Future _performActionWithEmailAndPassword(
Future<Either<AuthFailure, Unit>> Function({
@required EmailAddress emailAddress,
@required Password password,
})
action,
) async {
Either<AuthFailure, Unit> result;
final isEmailValid = state.emailAddress.isValid();
final isPasswordValid = state.password.isValid();
if (isEmailValid && isPasswordValid) {
state = state.copyWith(
isSubmitting: true,
authFailureOrSuccess: none(),
);
result = await action(
emailAddress: state.emailAddress,
password: state.password,
);
state = state.copyWith(
authFailureOrSuccess: some(result),
);
}
state = state.copyWith(
isSubmitting: false,
showErrorMessage: true,
authFailureOrSuccess: optionOf(result),
);
}
}
这就是我访问 statenotifier 的方式
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: ProviderListener<AuthFormStates>(
provider: signInStateNotifierProvider.state,
onChange: (context, state) {
state.authFailureOrSuccess.fold(
() {},
(either) => either.fold(
(failure) {
return null;
},
(success) => null,
),
);
},
child: Consumer(
builder: (context, watch, child) {
final providerState = watch(signInProvider);
final stateNotifierState =
watch(signInStateNotifierProvider.state);
return Form(
autovalidateMode: AutovalidateMode.onUserInteraction,
child: Column(
children: [
TextFormField(
onChanged: (value) => providerState.mapEventToState(
SignInFormEvents.emailChanged(value)),
validator: (_) => stateNotifierState
.emailAddress.validatedObject
.fold(
(l) => l.maybeMap(
orElse: () => null,
invalidEmail: (_) => 'Invalid Email'),
(_) => null),
),
TextFormField(
onChanged: (value) => providerState.mapEventToState(
SignInFormEvents.passwordChanged(value)),
validator: (_) =>
stateNotifierState.password.validatedObject.fold(
(l) => l.maybeMap(
orElse: () => null,
invalidPassword: (_) => 'Invalid Password'),
(_) => null),
),
TextButton(
onPressed: () {
providerState.mapEventToState(const SignInFormEvents
.signInWithEmailAndPasswordPressed());
},
child: const Text('Hello World'))
],
));
},
),
),
),
);
}
}
这很好用,但我需要知道这是正确的方法还是我错了。有没有其他方法可以实现我正在做的事情?请给出解决方案。
而且我所做的也是正确的,请告诉我它是如何以这种方式工作的。我需要清楚地了解它的工作原理。
提前致谢:)
答案 0 :(得分:1)
我认为 signInProvider
是多余的。它没有什么特别的,只是听signInStateNotifierProvider
。
在小部件 HomePage 中,您实际上可以使用 context.read(provider)
调用提供程序内部的函数而无需查看状态,因此您可以更改:
final providerState = watch(signInProvider);
providerState.mapEventToState(...)
到
final providerState = context.read(signInStateNotifierProvider);
providerState.mapEventToState(...)
或
context.read(signInStateNotifierProvider).mapEventToState(...)
另外,如果你追求最好的表现,你在验证价值时不需要听(看)signInStateNotifierProvider
。 (因为你没有在widget树中重建任何东西,只是获取数据)
validator: (_) => context.read(signInStateNotifierProvider).emailAddress.validatedObject...