.agents/skills/flutter-notifier-pattern/SKILL.md
Use when migrating Riverpod class-based providers from legacy controller naming to notifier naming and enforcing notifier vs usecase boundaries.
npx skillsauth add auravibes-apps/auravibes flutter-notifier-patternInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
In this repo, Riverpod class-based providers that own mutable app/runtime state should be named *Notifier, live under a notifiers/ folder, and expose state-focused intent methods.
This pattern replaces legacy *Controller naming for Riverpod classes with build() methods.
build() loads initial state for a feature or flowstatebuild()try/finally around async state transitionsMove logic out of a notifier when it includes:
If a method only updates notifier state around an already-defined domain action, it can stay in the notifier.
Keep plain providers for:
Do not rename every provider to a notifier. Only Riverpod classes that own state should become notifiers.
apps/<app_name>/lib/features/<feature_name>/notifiers/<name>_notifier.dartapps/<app_name>/lib/notifiers/<name>_notifier.dartapps/<app_name>/lib/features/<feature_name>/usecases/<name>_usecase.dartproviders/*Controller to *Notifier.notifiers/ folder.ThemeNotifier generates themeProvider, not themeNotifierProvider).build() focused on loading initial state.Use cases must not import or depend on notifier classes. When a use case needs to trigger notifier behavior (start/stop streaming, enqueue, check state), use a runtime adapter — a plain class with Function fields that wraps notifier method references.
Provider that captures ref.watch(...Provider.notifier) methods.Runtime adapter files live in providers/:
apps/<app_name>/lib/features/<feature_name>/providers/<name>_runtime_provider.dart// providers/streaming_runtime_provider.dart
class ConversationStreamingRuntime {
const ConversationStreamingRuntime({
required this.start,
required this.isStreaming,
required this.remove,
});
final void Function(String conversationId) start;
final bool Function(String conversationId) isStreaming;
final void Function(String conversationId) remove;
}
final conversationStreamingRuntimeProvider =
Provider<ConversationStreamingRuntime>((ref) {
final notifier = ref.watch(conversationStreamingProvider.notifier);
return ConversationStreamingRuntime(
start: notifier.start,
isStreaming: notifier.isStreaming,
remove: notifier.remove,
);
});
Method references are captured once per provider rebuild. The adapter stays valid as long as the underlying notifier instance is not disposed and recreated between uses. For @Riverpod(keepAlive: true) notifiers this is guaranteed. For auto-dispose notifiers (@riverpod), the adapter's ref.watch subscription keeps the notifier alive while the adapter is watched. If a notifier adds mutable local fields beyond ref/state, review whether captured method references remain valid across the notifier's lifecycle.
notifiers/tools
Convert tasks from tasks.md into GitHub issues. Use after task breakdown to track work items in GitHub project management.
tools
Break down implementation plans into actionable task lists. Use after planning to create a structured task breakdown. Generates tasks.md with ordered, dependency-aware tasks.
development
Create or update feature specifications from natural language descriptions. Use when starting new features or refining requirements. Generates spec.md with user stories, functional requirements, and acceptance criteria following spec-driven development methodology.
testing
Generate technical implementation plans from feature specifications. Use after creating a spec to define architecture, tech stack, and implementation phases. Creates plan.md with detailed technical design.