如何在 riverpod future provider 中抛出错误并在错误发生时捕获它

How to throw error inside riverpod future provider and catch it on error flutter

final loginProvider =
    FutureProvider.family<bool, LoginParam>((ref, param) async {
  if (param.sgId == '' || param.password == '') {
    return false;
  }
  final http.Response response =
      await APIClient().login(param.sgId, param.password);
  if (response.statusCode == 200) {
    await APIClient().saveTokens(response);
    UserDefaultEntity entity =
        await ref.watch(userDefaultsProvider(param.sgId).future);
    //ref.state = AsyncValue.data(true);
    return true;
  } else {
    throw Exception(jsonDecode(response.body)['message'] ?? 'Unknown Error');
  }
});
  void login(String userName, String password) async {
    state = AsyncValue.loading();
    AsyncValue<bool> result;
    try {
      result = await ref.refresh(loginProvider(LoginParam(userName, password)));
      state = result;
    } catch (e) {
      state = AsyncError(e);
    }
  }

我试图在 riverpod future provider 中抛出自定义异常并在其他状态通知程序中捕获异常 类,但未触发 catch 块。 有没有其他方法来处理未来提供者抛出的异常。

首先,您不必手动捕获 FutureProvider 中的错误,它会为您完成。 Refer this example.

一般来说,在某些“用户交互”之后发生的操作,如单击按钮(在本例中为登录操作),并不意味着要写在 FutureProvider 中。您将使用 FutureProvider 的场景如下:

  • 正在通过 HTTP/HTTPS 获取一些数据。
  • 执行读取文件或本地数据库等操作。

所以您的登录用例可以使用 StateNotifier.

来实现
// auth_provider.dart

import 'package:hooks_riverpod/hooks_riverpod.dart';

// Always prefer some strongly typed object to 
// know current status of authentication.
enum AuthState {
  unauthenticated,
  authenticated,
  authenticating,
  failed,
}

// StateNotifier is recommended to encapsulate all your business
// logic into a single class and use it from there.
class AuthStateNotifier extends StateNotifier<AuthState> {
  // Initialize with the default state of "unauthenticated".
  const AuthStateNotifier() : super(AuthState.unauthenticated);

  Future<void> login(LoginParam params) async {
    if (param.sgId.isEmpty || param.password.isEmpty) {
      state = AuthState.failed;
      return;
    }

    final http.Response response = await APIClient().login(param.sgId, param.password);

    if (response.statusCode == 200) {
      await APIClient().saveTokens(response);
      UserDefaultEntity entity = await ref.watch(userDefaultsProvider(param.sgId).future);
      state = AuthState.authenticated;
      return;
    } else {
      state = AuthState.failed;
      throw Exception(jsonDecode(response.body)['message'] ?? 'Unknown Error');
    }
  }
}

// Finally, create a provider that can be consumed in the presentation layer (UI).
final authProvider = StateNotifierProvider<AuthStateNotifier, AuthState>((ref) => const AuthStateNotifier());

然后,在你的UI部分,通常在按钮的onTap / onPressed事件处理程序中,你可以按如下方式使用它。请注意,我们创建了一个扩展 ConsumerWidget 以访问 ref.

的按钮小部件
// login.dart

import 'auth_provider.dart';

class LoginButton extends ConsumerWidget {
  final LoginParam params;

  const LoginButton({
    Key? key,
    required this.params,
  }) : super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    void login() {
      try {
        await ref.read(authProvider.notifier).login(params);
      } catch (e) {
        // Handle error here.
      }
    }

    return ElevatedButton(
      child: Text('Login'),
      // Call the handler here.
      onPressed: login,
    );
  }
}