在 Dart 中使用 Riverpod 代码生成的问题(addListener)

问题描述 投票:0回答:1

在我做了一个没有注释的相同项目之后,我正在使用带有注释的 Riverpod 做一个项目。我尝试调整第一个项目中的所有提供程序,但在访问 _authNotifier.addListener 时遇到问题,因为它告诉我它尚未定义。

我不确定发生了什么事,所以你可以帮助我,这会对我很有帮助!

这是我的第一个项目的代码,它运行完美

**auth_provider.dart**

final authProvider = StateNotifierProvider<AuthNotifier, AuthState>((ref) {
  final authRepository = AuthRepositoryImpl();
  final keyValueStorageService = KeyValueStorageServiceImpl();

  return AuthNotifier(
    authRepository: authRepository,
    keyValueStorageService: keyValueStorageService,
  );
});

class AuthNotifier extends StateNotifier<AuthState> {
  final AuthRepository authRepository;
  final KeyValueStorageService keyValueStorageService;

  AuthNotifier({
    required this.authRepository,
    required this.keyValueStorageService,
  }) : super(AuthState()) {
    checkAuthStatus();
  }

  Future<void> loginUser(String email, String password) async {
    try {
      final user = await authRepository.login(email, password);
      _setLoggedUser(user);
    } on CustomError catch (e) {
      logout(e.message);
    } catch (e) {
      logout('Error no controlado');
    }
  }

  void registerUser(String email, String password, String fullName) async {}

  void checkAuthStatus() async {
    final token = await keyValueStorageService.getKeyValue<String>('token');
    if (token == null) return logout();

    try {
      final user = await authRepository.checkAuthStatus(token);
      _setLoggedUser(user);
    } catch (e) {
      logout();
    }
  }

  _setLoggedUser(User user) async {
    await keyValueStorageService.setKeyValue('token', user.token);

    state = state.copyWith(
      user: user,
      authStatus: AuthStatus.authenticated,
      errorMessage: '',
    );
  }

  Future<void> logout([String? errorMessage]) async {
    await keyValueStorageService.removeKey('token');

    state = state.copyWith(
      authStatus: AuthStatus.unauthenticated,
      user: null,
      errorMessage: errorMessage,
    );
  }
}

enum AuthStatus { checking, authenticated, unauthenticated }

class AuthState {
  final AuthStatus authStatus;
  final User? user;
  final String errorMessage;

  AuthState({
    this.authStatus = AuthStatus.checking,
    this.user,
    this.errorMessage = '',
  });

  AuthState copyWith({
    AuthStatus? authStatus,
    User? user,
    String? errorMessage,
  }) =>
      AuthState(
        authStatus: authStatus ?? this.authStatus,
        user: user ?? this.user,
        errorMessage: errorMessage ?? this.errorMessage,
      );
}

**app_router_notifier.dart**

final goRouterNotifierProvider = Provider((ref) {
  final authNotifier = ref.read(authProvider.notifier);
  return GoRouterNotifier(authNotifier);
});

class GoRouterNotifier extends ChangeNotifier {
  final AuthNotifier _authNotifier;

  AuthStatus _authStatus = AuthStatus.checking;

  GoRouterNotifier(this._authNotifier) {
    _authNotifier.addListener((state) {
      authStatus = state.authStatus;
    });
  }

  AuthStatus get authStatus => _authStatus;

  set authStatus(AuthStatus value) {
    _authStatus = value;
    notifyListeners();
  }
}

现在这是我使用注释的第二个项目(这是问题)

**auth_provider.dart**

part 'auth_provider.g.dart';

@Riverpod(keepAlive: true)
class Auth extends _$Auth {
  final authRepository = AuthRepositoryImpl();
  final keyValueStorageService = KeyValueStorageServiceImpl();

  @override
  AuthState build() {
    checkAuthStatus();
    return AuthState();
  } 

  Future<void> loginUser(String email, String password) async {
    try {
      final user = await authRepository.login(email, password);

      _setLoggedUser(user);
    } on CustomError catch (e) {
      logout(e.message);
    } catch (e) {
      logout('Error no controlado');
    }
  }

  void registerUser(String email, String password, String username) async {}

  void checkAuthStatus() async {
    final token = await keyValueStorageService.getKeyValue<String>('token');
    if (token == null) return logout();

    try {
      final user = await authRepository.checkAuthStatus(token);
      _setLoggedUser(user);
    } catch (e) {
      logout();
    }
  }

  _setLoggedUser(User user) async {
    await keyValueStorageService.setKeyValue('token', user.token);

    state = state.copyWith(
      user: user,
      authStatus: AuthStatus.authenticated,
      errorMessage: '',
    );
  }

  Future<void> logout([String? errorMessage]) async {
    await keyValueStorageService.removeKey('token');

    state = state.copyWith(
      authStatus: AuthStatus.unauthenticated,
      user: null,
      errorMessage: errorMessage,
    );
  }
}

enum AuthStatus { checking, authenticated, unauthenticated }

class AuthState {
  final AuthStatus authStatus;
  final User? user;
  final String errorMessage;

  AuthState({
    this.authStatus = AuthStatus.checking,
    this.user,
    this.errorMessage = '',
  });

  AuthState copyWith({
    AuthStatus? authStatus,
    User? user,
    String? errorMessage,
  }) =>
      AuthState(
        authStatus: authStatus ?? this.authStatus,
        user: user ?? this.user,
        errorMessage: errorMessage ?? this.errorMessage,
      );
}

**app_router_notifier.dart**

part 'app_router_notifier.g.dart';

@Riverpod(keepAlive: true)
GoRouterNotifier goRouterNotifier(Ref ref) {
  final authNotifier = ref.read(authProvider.notifier);

  return GoRouterNotifier(authNotifier);
}

class GoRouterNotifier extends ChangeNotifier {
  final Auth _authNotifier;

  AuthStatus _authStatus = AuthStatus.checking;

  GoRouterNotifier(this._authNotifier) {
   _authNotifier.addListener((state) {
      authStatus = state.authStatus;
    });
  }

  AuthStatus get authStatus => _authStatus;

  set authStatus(AuthStatus value) {
    _authStatus = value;
    notifyListeners();
  }
}

那么问题出在哪里?

正如你所看到的,我正在尝试做完全相同的事情,但是在这行中的

app_router_notifier.dart
中:

_authNotifier.addListener((state) {
      authStatus = state.authStatus;
    });

我收到此错误: 未为类型“Auth”定义方法“addListener”。 尝试将名称更正为现有方法的名称,或定义名为“addListener”的方法。

即使只使用人工智能,我也尝试过不同的解决方案。我认为我的 auth_provider.dart 有问题,但我不确定它是什么。我相信

.addListener
位于
StateNotifier
中,但它不在生成 Riverpod 的 NotifierProvider 中。

// GENERATED CODE - DO NOT MODIFY BY HAND

part of 'auth_provider.dart';

// **************************************************************************
// RiverpodGenerator
// **************************************************************************

String _$authHash() => r'baf96bdb5085978702d0e2b04c3a2e56893b8a41';

/// See also [Auth].
@ProviderFor(Auth)
final authProvider = NotifierProvider<Auth, AuthState>.internal(
  Auth.new,
  name: r'authProvider',
  debugGetCreateSourceHash:
      const bool.fromEnvironment('dart.vm.product') ? null : _$authHash,
  dependencies: null,
  allTransitiveDependencies: null,
);

typedef _$Auth = Notifier<AuthState>;
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, inference_failure_on_uninitialized_variable, inference_failure_on_function_return_type, inference_failure_on_untyped_parameter

还有其他“解决方案”对我不起作用:

_authNotifier.listen<AuthStatus>((state) {
      authStatus = state.authStatus;
    });
_authNotifier.ref.listen(authProvider.notifier, (_, currentState) {
      authStatus = currentState.state.authStatus;
    });
 _authNotifier.addListener((state) {
      final authState = _authNotifier.build();
      authStatus = authState.authStatus;
    });

应用程序的一些背景

我正在使用 Flutter、Dart、Riverpod 和 Go Router 在我的后端(NestJS + GraphQL)中进行登录和注册,在下面的文件中,如果没有有效令牌,我将尝试重定向用户。这是我使用 goRouterNotifier 的地方,因此它可以在状态更改时监听并重定向用户。

part 'app_router.g.dart';

@Riverpod(keepAlive: false)
GoRouter goRouter(GoRouterRef ref) {
  final goRouterNotifier = ref.read(goRouterNotifierProvider);

  return GoRouter(
    initialLocation: '/login',
    refreshListenable: goRouterNotifier,
    routes: [
      //* First Route
      GoRoute(
        path: '/checking',
        builder: (context, state) => const CheckAuthStatusScreen(),
      ),

      //* Shared Routes
      GoRoute(
          path: '/main/:page',
          builder: (context, state) {
            final pageIndex = state.pathParameters['page'] ?? '0';
            return MainScreen(pageIndex: int.parse(pageIndex));
          }),

      GoRoute(
        path: '/settings',
        builder: (context, state) => const SettingdScreen(),
      ),

      //* Auth Routes
      GoRoute(
        path: '/login',
        builder: (context, state) => const LoginScreen(),
      ),
      GoRoute(
        path: '/signup',
        builder: (context, state) => const SignUpScreen(),
      ),

      //* Home Routes
    ],
    redirect: (context, state) {
      // print(state.matchedLocation);
      final isGoingTo = state.matchedLocation;
      final authStatus = goRouterNotifier.authStatus;

      if (isGoingTo == '/checking' && authStatus == AuthStatus.checking) {
        return null;
      }

      if (authStatus == AuthStatus.unauthenticated) {
        if (isGoingTo == '/login' || isGoingTo == '/signup') return null;

        return '/login';
      }

      if (authStatus == AuthStatus.authenticated) {
        if (isGoingTo == '/login' ||
            isGoingTo == '/signup' ||
            isGoingTo == '/checking') return '/main/0';
      }

      return null;
    },
  );
}
flutter dart listener riverpod flutter-go-router
1个回答
0
投票

事实上,诸如

StateNotifier
.stream
之类的
.addListener
属性并不存在于
Notifier
中。所以你应该像这样听
Notifier

_authNotifier.ref.listenSelf((AuthState? previous, AuthState next) {
      authStatus = next.authStatus;
    });

或者使用

ref.listen
方法(我认为这个选项在这种情况下是最佳的):

ref.listen(_authNotifier, (AuthState? previous, AuthState next) {
      authStatus = next.authStatus;
    });

您可以通过

ref
类的构造函数将
GoRouterNotifier
传递给它。

在您无权访问

ref
的任何其他情况下,请从上下文 (
ProviderScope.containerOf(context)
) 获取当前容器并使用
providerContainer.listen(provider)

© www.soinside.com 2019 - 2024. All rights reserved.