?? ??

The package intentionally narrows features ? keep behavior explicit: provider identity by args, scoped injection, ? predictable disposal semantics.

What Changes ?? Riverpod

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

Provider identity

runtimeType + args hash

family alternative

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

DI fallback

Scope<T>.required + overrideWithValue

Why this matters

You can reason about equality ? overrides ?? plain Dart constructors, which keeps debugging ? tests straightforward.

Provider Identity ?? 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.

??: family-like provider + Scope fallback

Use a constructor argument as identity ? inject a fallback instance through Scope.

product_provider.dart
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 ? test-friendly.
overrideWithValue works per provider instance, including args-based instances.
autoDispose behavior is unchanged by using subclass + args.

?? ??

????? & Reads

See concrete patterns ? watch/read/listen ? AsyncProvider.future.

Open ?????

Mutations

Implement state updates ?? mutation tokens, mutate, ? ref.invoke.

Open ????