Skip to content

Async Data

useAsyncData helps manage loading, success, and error states for async work.

dart
final query = signal(context, 1);
final result = useAsyncData<Map<String, dynamic>>(
  context,
  () async {
    final id = query();
    // fetch data here
    return {'id': id};
  },
  defaults: () => const {},
);

Render with when:

dart
final view = result.when(
  context: context,
  idle: (data) => Text('Idle: $data'),
  pending: (_) => const CircularProgressIndicator(),
  success: (data) => Text('Loaded: $data'),
  error: (err) => Text('Error: ${err?.error}'),
);

Trigger refresh:

dart
await result.refresh();

Flutter Example (from the example app)

dart
class AsyncDataSection extends StatelessWidget {
  const AsyncDataSection({super.key});

  @override
  Widget build(BuildContext context) {
    final requestId = signal<int>(context, 1);
    final result = useAsyncData<String>(context, () async {
      final id = requestId();
      await Future<void>.delayed(const Duration(milliseconds: 500));
      return 'Result #$id';
    }, defaults: () => 'Idle');

    final status = result.when(
      context: context,
      idle: (data) => 'Idle: ${data ?? '-'}',
      pending: (data) => 'Loading... ${data ?? ''}',
      success: (data) => 'Success: ${data ?? '-'}',
      error: (error) => 'Error: ${error?.error ?? 'unknown'}',
    );

    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text('request id: ${requestId()}'),
        Text(status),
        const SizedBox(height: 8),
        Wrap(
          spacing: 8,
          children: [
            ElevatedButton(
              onPressed: () => requestId.set(requestId() + 1),
              child: const Text('Next request'),
            ),
            OutlinedButton(
              onPressed: () async {
                await result.refresh();
              },
              child: const Text('Refresh'),
            ),
          ],
        ),
      ],
    );
  }
}