flutterdartriverpod

Riverpod AsyncValue.when is not triggering the error builder


I am using AsyncValue for async state as per docs but the error response from server is crashing the app. My riverpod is


final bookDetailsProvider = AutoDisposeAsyncNotifierProviderFamily<
    BookDetailsNotifier, AsyncValue<VirtualBook>, int>(
  () => BookDetailsNotifier(),
);

class BookDetailsNotifier
    extends AutoDisposeFamilyAsyncNotifier<AsyncValue<VirtualBook>, int> {
  @override
  Future<AsyncValue<VirtualBook>> build(int arg) async {
    return _loadBookDetails(arg);
  }

  Future<AsyncValue<VirtualBook>> _loadBookDetails(int id) async {
    try {
      final res = await ref.read(virualBooksRepositoryProvider).getBookById(id);
      return AsyncValue.data(res);
    } on ApiResponseError catch (e, s) {
      printRed('Error Response');
      printRed(e);
      return AsyncValue.error(e.message, s);
    } catch (e, s) {
      printRed(e);
      printRed(s);
      return AsyncValue.error('Something went wrong', s);
    }
  }
}

My UI is


class BookDetailsPage extends ConsumerWidget {
  const BookDetailsPage(this.bookId, {super.key});
  final int bookId;

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final bookDetails = ref.watch(bookDetailsProvider(bookId));

    return AppScaffold(
      titleIcon: Icons.book,
      title: 'Book Details',
      body: Padding(
        padding: const EdgeInsets.all(d_2),
        child: bookDetails.when(
          error: (error, stackTrace) {
            printYellow('Error');
            return Center(child: Text(error.toString()));
          },
          loading: () {
            printYellow('Loading');
            return const Center(child: CircularProgressIndicator());
          },
          data: (data) {
            printYellow('Got Data');
            printGreen('Data Type: ${data.runtimeType}');
            printGreen(' hasValue: ${data.hasValue}');
            printGreen(' hasError: ${data.hasError}');
            printGreen(' value: ${data.value}');
            final book = data.value;
            if (book == null) {
              return const Center(child: Text('Book not found'));
            }
            return ListView.separated(
              itemCount: book.chapters.length,
              separatorBuilder: (context, index) => const SizedBox(height: d_2),
              itemBuilder: (context, index) {
                final chapter = book.chapters[index];
                return ChapterTile(
                  index: index,
                  chapter: chapter,
                );
              },
            );
          },
        ),
      ),
    );
  }
}

Logs is

Error Logs

Clearly, I got 500 error from server, My state is AsyncError but the when method is calling the data callback instead of error callback. What may be the exact issue here?


Solution

  • My bad, The AsyncNotifier automatically wraps the state into AsyncValue. So in my code, its a AsyncValue inside a AsyncValue which gave this behaviour. Converting state of AsyncValue<VirtualBook> to just VirtualBook solved the issue.