Design principles behind miniriverpod.

The package keeps its feature set focused so behavior stays explicit: provider identity by args, scoped injection, and predictable disposal.

What Changes from Riverpod

Instead of generated family classes and implicit notifier channels, miniriverpod prefers subclass + args + explicit invoke.

Provider identity

runtimeType + args hash

family alternative

Subclass Provider / AsyncProvider and pass super.args((...))

DI fallback

Scope<T>.required + overrideWithValue

Why this matters

Plain Dart constructors make equality and overrides easy to reason about, keeping debugging and tests straightforward.

Provider Identity with args

args defines the provider key, so equal args resolve to the same cache entry inside a ProviderContainer.

Identity rule

Practical consequences

- No dedicated family type is required.
- Per-argument override is done by creating provider instances.
- Maintain args stable and immutable for predictable caching.

Example: family-like provider + Scope fallback

Choose a constructor argument as identity and inject a fallback instance through Scope. The workflow remains easy to review.

class ProductProvider extends AsyncProvider<List<Product>> {
  ProductProvider({this.search = ''}) : super.args((search,));
  final String search;

  static final fallback = Scope<ProductProvider>.required('product.fallback');

  @override
  FutureOr<List<Product>> build(ref) async {
    final api = ref.watch(productsApiProvider);
    return api.search(q: search);
  }
}

// Inject
ProviderScope(
  overrides: [
    ProductProvider.fallback.overrideWithValue(ProductProvider(search: 'jeans')),
  ],
  child: const App(),
);
Scope keeps dependency wiring explicit and test-friendly.
overrideWithValue works per provider instance, including args-based instances.
autoDispose behavior is unchanged by using subclass + args.

Next Steps

Providers & Reads

See concrete patterns for watch/read/listen and AsyncProvider.future.

Open Providers

Mutations

Implement state updates with mutation tokens, mutate, and ref.invoke.

Open Mutations