Design principles behind miniriverpod.
The package intentionally narrows features to keep behavior explicit: provider identity by args, scoped injection, and predictable disposal semantics.
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
You can reason about equality and overrides from plain Dart constructors, which keeps debugging and tests straightforward.
Provider Identity with args
args defines the provider key, so equal args means the same cache entry inside a ProviderContainer.
Identity rule
ProviderKey = runtimeType + hash(args)
Practical consequences
- No dedicated family type is required.
- Per-argument override is done by creating provider instances.
- Keep args stable and immutable for predictable caching.
Example: family-like provider + Scope fallback
Use a constructor argument as identity and inject a fallback instance through Scope.
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(),
);
આગલા પગલાં
Providers & Reads
See concrete patterns for watch/read/listen and AsyncProvider.future.
Providers ખોલો