Как получить доступ к значениям и функциям внутри statenotifier в riverpod

Я реализовал аутентификацию пользователя с statenotifier в riverpod, он работает, но я не знаю, как он работает таким образом. Мне нужно создать двух провайдеров: 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),
    );
  }
}

Вот как я получаю доступ к уведомителю о состоянии

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

Это нормально работает, но мне нужно знать, правильный ли это метод или я ошибаюсь. Есть ли другой способ добиться того же, что и я? Пожалуйста, дайте решение.

И то, что я делаю, правильно, пожалуйста, расскажите мне, как это работает таким образом. Мне нужно четкое понимание того, как это работает.

Заранее спасибо :)


person Pythonhub    schedule 02.02.2021    source источник


Ответы (1)


Я считаю signInProvider лишним. Он ничего особенного не делает, а только слушает signInStateNotifierProvider.

Внутри виджета HomePage вы можете вызвать функцию внутри провайдера, не наблюдая за состоянием, используя context.read(provider), чтобы вы могли изменить:

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

to

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

or

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

Кроме того, если вы стремитесь к максимальной производительности, вам не нужно слушать (смотреть) signInStateNotifierProvider при проверке значения. (Потому что вы ничего не перестраиваете в дереве виджетов, а просто извлекаете данные)

validator: (_) => context.read(signInStateNotifierProvider).emailAddress.validatedObject...
person yellowgray    schedule 03.02.2021
comment
Спасибо за это. Я исправил свою ошибку. Но не могли бы вы объяснить, как это работает и почему я не могу просто получить доступ к функциям внутри statenotifier одним этим. final signInStateNotifierProvider =StateNotifierProvider((ref) => SignInStateNotifier(authentication)); - person Pythonhub; 03.02.2021
comment
Привет ~ Вы имеете в виду внутри строки в своем комментарии? Эта строка похожа на начальный конструктор провайдера. Это касается только конструкции самого себя. ref можно использовать для прослушивания другого провайдера и восстановления самого себя. Riverpod Doc, context.read - person yellowgray; 04.02.2021
comment
Мой вопрос в том, почему я не мог просто сделать это с final stateNotifierState = watch(signInStateNotifierProvider.state);, почему я должен читать statenotifier отдельно, чтобы получить доступ к функции внутри него. - person Pythonhub; 04.02.2021
comment
Короче (мое мнение), я думаю, это потому, что Доступность и Производительность. 1. Для использования watch(provider) можно использовать только при построении (в методе сборки), в то время как вы можете использовать чтение где угодно (например, onPress, onTap). 2. Когда вы используете только чтение, вы можете избежать ненужной перестройки. В противном случае виджет перестраивается каждый раз при изменении состояния. - person yellowgray; 05.02.2021