如何在 riverpod 中访问 statenotifier 内的值和函数

How to access values and functions inside statenotifier in riverpod

我在 riverpod 中使用 statenotifier 实现了用户身份验证,它可以工作,但我不知道它是如何以这种方式工作的。我必须创建两个提供程序,一个 StateNotifierProvider 用于读取值,一个 Provider 用于相同的 statenotifier 以访问 statenotifier.

中的函数
final signInStateNotifierProvider =
    StateNotifierProvider((ref) => SignInStateNotifier(authentication));

final signInProvider =
    Provider((ref) => ref.watch(signInStateNotifierProvider));

这是我的状态通知器class

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'))
                    ],
                  ));
            },
          ),
        ),
      ),
    );
  }
}

这很好用,但我需要知道这是正确的方法还是我错了。有没有其他方法可以实现我正在做的事情?请给出解决方案

而且我这样做是对的,请告诉我它是如何以这种方式工作的。我需要清楚地了解它是如何工作的。

提前致谢:)

我认为signInProvider是多余的。它没有什么特别的,只是听 signInStateNotifierProvider.

在 widget HomePage 内部,您实际上可以调用 provider 内部的函数,而无需使用 context.read(provider) 查看状态,因此您可以更改:

final providerState = watch(signInProvider);
providerState.mapEventToState(...)

final providerState = context.read(signInStateNotifierProvider);
providerState.mapEventToState(...)

context.read(signInStateNotifierProvider).mapEventToState(...)

另外如果你追求最好的性能,验证值的时候不需要听(看)signInStateNotifierProvider。 (因为你没有在小部件树中重建任何东西,只是获取数据)

validator: (_) => context.read(signInStateNotifierProvider).emailAddress.validatedObject...