AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Riverpod

skill-evanca-flutter-ai-rules-riverpod · by evanca

Use when setting up providers, combining requests, managing state disposal, passing arguments, performing side effects, or testing providers (Riverpod).

No reviews yet
0 installs
13 views
0.0% view→install

Install

$ agentstack add skill-evanca-flutter-ai-rules-riverpod

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-evanca-flutter-ai-rules-riverpod)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
13d ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Riverpod? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Riverpod Skill

This skill defines how to correctly use Riverpod for state management in Flutter and Dart applications.


1. Setup

void main() {
  runApp(const ProviderScope(child: MyApp()));
}
  • Wrap your app with ProviderScope directly in runApp — never inside MyApp.
  • Install and use riverpod_lint to enable IDE refactoring and enforce best practices.

2. Defining Providers

// Functional provider (codegen)
@riverpod
int example(Ref ref) => 0;

// FutureProvider (codegen)
@riverpod
Future> todos(Ref ref) async {
  return ref.watch(repositoryProvider).fetchTodos();
}

// Notifier (codegen)
@riverpod
class TodosNotifier extends _$TodosNotifier {
  @override
  Future> build() async {
    return ref.watch(repositoryProvider).fetchTodos();
  }

  Future addTodo(Todo todo) async { ... }
}
  • Define all providers as final top-level variables.
  • Use Provider, FutureProvider, or StreamProvider based on the return type.
  • Use ConsumerWidget or ConsumerStatefulWidget instead of StatelessWidget/StatefulWidget when accessing providers.

3. Using Ref

| Method | Use for | |---|---| | ref.watch | Reactively listen — rebuilds when value changes. Use during build phase only. | | ref.read | One-time access — use in callbacks/Notifier methods, not in build. | | ref.listen | Imperative subscription — prefer ref.watch where possible. | | ref.onDispose | Cleanup when provider state is destroyed. |

// In a widget
class MyWidget extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final value = ref.watch(myProvider);
    return Text('$value');
  }
}

// Cleanup in a provider
final provider = StreamProvider((ref) {
  final controller = StreamController();
  ref.onDispose(controller.close);
  return controller.stream;
});
  • Never call ref.watch inside callbacks, listeners, or Notifier methods.
  • Use ref.read(yourNotifierProvider.notifier).method() to call Notifier methods from the UI.
  • Check context.mounted before using ref after an await in async callbacks.

4. Combining Providers

@riverpod
Future userGreeting(Ref ref) async {
  final user = await ref.watch(userProvider.future);
  return 'Hello, ${user.name}!';
}
  • Use ref.watch(asyncProvider.future) to await an async provider's resolved value.
  • Providers only execute once and cache the result — multiple widgets listening to the same provider share one computation.

5. Passing Arguments (Families)

@riverpod
Future todo(Ref ref, String id) async {
  return ref.watch(repositoryProvider).fetchTodo(id);
}

// Usage
final todo = ref.watch(todoProvider('some-id'));
  • Always enable autoDispose for parameterized providers to prevent memory leaks.
  • Use Dart 3 records or code generation for multiple parameters — they naturally override ==.
  • Avoid passing plain List or Map as parameters (no == override); use const collections, records, or classes with proper equality.
  • Use the provider_parameters lint rule from riverpod_lint to catch equality mistakes.

6. Auto Dispose & State Lifecycle

  • With codegen: state is destroyed by default when no longer listened to. Opt out with keepAlive: true.
  • Without codegen: state is kept alive by default. Use .autoDispose to enable disposal.
  • State is always destroyed when a provider is recomputed.
// keepAlive with timer
ref.onCancel(() {
  final link = ref.keepAlive();
  Timer(const Duration(minutes: 5), link.close);
});
  • Use ref.onDispose for cleanup; do not trigger side effects or modify providers inside it.
  • Use ref.invalidate(provider) to force destruction; use ref.invalidateSelf() from within the provider.
  • Use ref.refresh(provider) to invalidate and immediately read the new value — always use the return value.

7. Eager Initialization

Providers are lazy by default. To eagerly initialize:

// In MyApp or a dedicated widget under ProviderScope:
Consumer(
  builder: (context, ref, _) {
    ref.watch(myEagerProvider); // forces initialization
    return const MyApp();
  },
)
  • Place eager initialization in a public widget (not main()) for consistent test behavior.
  • Use AsyncValue.requireValue to read data directly and throw clearly if not ready.

8. Performing Side Effects

@riverpod
class TodosNotifier extends _$TodosNotifier {
  Future addTodo(Todo todo) async {
    state = const AsyncLoading();
    state = await AsyncValue.guard(() async {
      await ref.read(repositoryProvider).addTodo(todo);
      return [...?state.value, todo];
    });
  }
}

// In UI:
ElevatedButton(
  onPressed: () => ref.read(todosNotifierProvider.notifier).addTodo(todo),
  child: const Text('Add'),
)
  • Use ref.read (not ref.watch) in event handlers.
  • After a side effect, update state by: setting it directly, calling ref.invalidateSelf(), or manually updating the cache.
  • Always handle loading and error states in the UI.
  • Do not perform side effects in provider constructors or build methods.

9. Provider Observers

class MyObserver extends ProviderObserver {
  @override
  void didUpdateProvider(ProviderObserverContext context, Object? previousValue, Object? newValue) {
    print('[${context.provider}] updated: $previousValue → $newValue');
  }

  @override
  void providerDidFail(ProviderObserverContext context, Object error, StackTrace stackTrace) {
    // Report to error service
  }
}

runApp(ProviderScope(observers: [MyObserver()], child: MyApp()));

10. Testing

// Unit test
final container = ProviderContainer(
  overrides: [repositoryProvider.overrideWith((_) => FakeRepository())],
);
addTearDown(container.dispose);

expect(await container.read(todosProvider.future), isNotEmpty);

// Widget test
await tester.pumpWidget(
  ProviderScope(
    overrides: [repositoryProvider.overrideWith((_) => FakeRepository())],
    child: const MyApp(),
  ),
);
  • Create a new ProviderContainer or ProviderScope for each test — never share state between tests.
  • Use container.listen over container.read for autoDispose providers to keep state alive during the test.
  • Use overrides to inject mocks or fakes.
  • Prefer mocking dependencies (repositories) rather than Notifiers directly.
  • If you must mock a Notifier, subclass the original — don't use implements or with Mock.
  • Place Notifier mocks in the same file as the Notifier if using code generation.
  • Obtain the container in widget tests with ProviderScope.containerOf(tester.element(...)).

References

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.