Skip to content

Commit

Permalink
Add overrideWith (#1839)
Browse files Browse the repository at this point in the history
  • Loading branch information
rrousselGit committed Oct 30, 2022
1 parent 1883856 commit b02b333
Show file tree
Hide file tree
Showing 40 changed files with 922 additions and 9 deletions.
Expand Up @@ -40,6 +40,20 @@ class AutoDisposeChangeNotifierProvider<NotifierT extends ChangeNotifier?>

@override
late final Refreshable<NotifierT> notifier = _notifier<NotifierT>(this);

/// {@macro riverpod.overridewith}
Override overrideWith(
Create<NotifierT, AutoDisposeChangeNotifierProviderRef<NotifierT>> create,
) {
return ProviderOverride(
origin: this,
override: AutoDisposeChangeNotifierProvider<NotifierT>(
create,
from: from,
argument: argument,
),
);
}
}

/// The element of [AutoDisposeChangeNotifierProvider].
Expand Down Expand Up @@ -70,4 +84,23 @@ class AutoDisposeChangeNotifierProviderFamily<NotifierT extends ChangeNotifier?,
super.name,
super.dependencies,
}) : super(providerFactory: AutoDisposeChangeNotifierProvider.new);

/// {@macro riverpod.overridewith}
Override overrideWith(
NotifierT Function(
AutoDisposeChangeNotifierProviderRef<NotifierT> ref,
Arg arg,
)
create,
) {
return FamilyOverrideImpl<NotifierT, Arg,
AutoDisposeChangeNotifierProvider<NotifierT>>(
this,
(arg) => AutoDisposeChangeNotifierProvider<NotifierT>(
(ref) => create(ref, arg),
from: from,
argument: arg,
),
);
}
}
Expand Up @@ -110,6 +110,53 @@ class ChangeNotifierProvider<NotifierT extends ChangeNotifier?>
@override
late final AlwaysAliveRefreshable<NotifierT> notifier =
_notifier<NotifierT>(this);

/// {@template riverpod.overridewith}
/// Override the provider with a new initialization function.
///
/// This will also disable the auto-scoping mechanism, meaning that if the
/// overridden provider specified `dependencies`, it will have no effect.
///
/// The override must not specify a `dependencies`.
///
/// Some common use-cases are:
/// - testing, by replacing a service with a fake implementation, or to reach
/// a very specific state easily.
/// - multiple environments, by changing the implementation of a class
/// based on the platform or other parameters.
///
/// This function should be used in combination with `ProviderScope.overrides`
/// or `ProviderContainer.overrides`:
///
/// ```dart
/// final myService = Provider((ref) => MyService());
///
/// runApp(
/// ProviderScope(
/// overrides: [
/// // Replace the implementation of the provider with a different one
/// myService.overrideWithProvider((ref) {
/// ref.watch('other');
/// return MyFakeService(),
/// })),
/// ],
/// child: MyApp(),
/// ),
/// );
/// ```
/// {@endtemplate}
Override overrideWith(
Create<NotifierT, ChangeNotifierProviderRef<NotifierT>> create,
) {
return ProviderOverride(
origin: this,
override: ChangeNotifierProvider<NotifierT>(
create,
from: from,
argument: argument,
),
);
}
}

/// The element of [ChangeNotifierProvider].
Expand Down Expand Up @@ -187,4 +234,20 @@ class ChangeNotifierProviderFamily<NotifierT extends ChangeNotifier?, Arg>
super.name,
super.dependencies,
}) : super(providerFactory: ChangeNotifierProvider.new);

/// {@macro riverpod.overridewith}
Override overrideWith(
NotifierT Function(ChangeNotifierProviderRef<NotifierT> ref, Arg arg)
create,
) {
return FamilyOverrideImpl<NotifierT, Arg,
ChangeNotifierProvider<NotifierT>>(
this,
(arg) => ChangeNotifierProvider<NotifierT>(
(ref) => create(ref, arg),
from: from,
argument: arg,
),
);
}
}
Expand Up @@ -118,6 +118,7 @@ void main() {
final container = createContainer(
parent: root,
overrides: [
// ignore: deprecated_member_use
provider.overrideWithProvider(
ChangeNotifierProvider.autoDispose((ref) => ValueNotifier(42)),
),
Expand Down
@@ -1,4 +1,4 @@
// ignore_for_file: invalid_use_of_internal_member
// ignore_for_file: invalid_use_of_internal_member, avoid_types_on_closure_parameters

import 'package:flutter/widgets.dart' hide Listener;
import 'package:flutter_riverpod/src/internals.dart';
Expand All @@ -8,6 +8,58 @@ import 'package:mockito/mockito.dart';
import '../../utils.dart';

void main() {
test('supports overrideWith', () {
final provider =
ChangeNotifierProvider<ValueNotifier<int>>((ref) => ValueNotifier(0));
final autoDispose = ChangeNotifierProvider.autoDispose<ValueNotifier<int>>(
(ref) => ValueNotifier(0),
);

final container = createContainer(
overrides: [
provider.overrideWith(
(ChangeNotifierProviderRef<ValueNotifier<int>> ref) =>
ValueNotifier(42),
),
autoDispose.overrideWith(
(AutoDisposeChangeNotifierProviderRef<ValueNotifier<int>> ref) =>
ValueNotifier(84),
),
],
);

expect(container.read(provider).value, 42);
expect(container.read(autoDispose).value, 84);
});

test('supports family overrideWith', () {
final family = ChangeNotifierProvider.family<ValueNotifier<String>, int>(
(ref, arg) => ValueNotifier('0 $arg'),
);
final autoDisposeFamily =
ChangeNotifierProvider.autoDispose.family<ValueNotifier<String>, int>(
(ref, arg) => ValueNotifier('0 $arg'),
);
final container = createContainer(
overrides: [
family.overrideWith(
(ChangeNotifierProviderRef<ValueNotifier<String>> ref, int arg) =>
ValueNotifier('42 $arg'),
),
autoDisposeFamily.overrideWith(
(
AutoDisposeChangeNotifierProviderRef<ValueNotifier<String>> ref,
int arg,
) =>
ValueNotifier('84 $arg'),
),
],
);

expect(container.read(family(10)).value, '42 10');
expect(container.read(autoDisposeFamily(10)).value, '84 10');
});

test('ref.listenSelf listens to state changes', () {
final listener = Listener<ValueNotifier<int>>();
final container = createContainer();
Expand Down Expand Up @@ -141,6 +193,7 @@ void main() {
final container = createContainer(
parent: root,
overrides: [
// ignore: deprecated_member_use
provider.overrideWithProvider(
ChangeNotifierProvider((ref) => ValueNotifier(42)),
),
Expand Down Expand Up @@ -363,6 +416,7 @@ void main() {
final notifier2 = TestNotifier();
final container = createContainer(
overrides: [
// ignore: deprecated_member_use
provider.overrideWithProvider(ChangeNotifierProvider((_) => notifier)),
],
);
Expand All @@ -382,6 +436,7 @@ void main() {
expect(callCount, 1);

container.updateOverrides([
// ignore: deprecated_member_use
provider.overrideWithProvider(ChangeNotifierProvider((_) => notifier2)),
]);

Expand Down
4 changes: 3 additions & 1 deletion packages/riverpod/CHANGELOG.md
@@ -1,7 +1,9 @@
## [Unreleased minor]

- Deprecate `StateProvider.state`
- Added `provider.overrideWith((ref) => state`)
- Deprecated `StateProvider.state`
Instead, use either `ref.watch(stateProvider)` or `ref.read(stateProvider.notifier).state =`
- Deprecated `provider.overrideWithProvider`. Instead use `provider.overrideWith`
- Added `Ref.notifyListeners()` to forcibly notify dependents.
This can be useful for mutable state.
- Added `@useResult` to `Ref.refresh`/`WidgetRef.refresh`
Expand Down
1 change: 1 addition & 0 deletions packages/riverpod/lib/riverpod.dart
Expand Up @@ -24,6 +24,7 @@ export 'src/framework.dart'
FamilyCreate,
AsyncSelector,
FamilyBase,
FamilyOverrideImpl,
AutoDisposeProviderElementMixin,
FamilyOverride,
NotifierFamilyBase,
Expand Down
12 changes: 12 additions & 0 deletions packages/riverpod/lib/src/async_notifier/auto_dispose.dart
Expand Up @@ -74,6 +74,18 @@ class AutoDisposeAsyncNotifierProviderImpl<
FutureOr<T> runNotifierBuild(AsyncNotifierBase<T> notifier) {
return (notifier as AutoDisposeAsyncNotifier<T>).build();
}

/// {@macro riverpod.overridewith}
Override overrideWithNotifier(NotifierT Function() create) {
return ProviderOverride(
origin: this,
override: AutoDisposeAsyncNotifierProviderImpl<NotifierT, T>(
create,
from: from,
argument: argument,
),
);
}
}

/// The element of [AutoDisposeAsyncNotifierProvider].
Expand Down
13 changes: 13 additions & 0 deletions packages/riverpod/lib/src/async_notifier/auto_dispose_family.dart
Expand Up @@ -76,4 +76,17 @@ class AutoDisposeAsyncNotifierProviderFamily<
super.name,
super.dependencies,
}) : super(providerFactory: AutoDisposeFamilyAsyncNotifierProvider.new);

/// {@macro riverpod.overridewith}
Override overrideWithNotifier(NotifierT Function() create) {
return FamilyOverrideImpl<AsyncValue<T>, Arg,
AutoDisposeFamilyAsyncNotifierProvider<NotifierT, T, Arg>>(
this,
(arg) => AutoDisposeFamilyAsyncNotifierProvider<NotifierT, T, Arg>(
create,
from: from,
argument: arg,
),
);
}
}
12 changes: 12 additions & 0 deletions packages/riverpod/lib/src/async_notifier/base.dart
Expand Up @@ -99,6 +99,18 @@ class AsyncNotifierProviderImpl<NotifierT extends AsyncNotifierBase<T>, T>
FutureOr<T> runNotifierBuild(AsyncNotifierBase<T> notifier) {
return (notifier as AsyncNotifier<T>).build();
}

/// {@macro riverpod.overridewith}
Override overrideWithNotifier(NotifierT Function() create) {
return ProviderOverride(
origin: this,
override: AsyncNotifierProviderImpl<NotifierT, T>(
create,
from: from,
argument: argument,
),
);
}
}

/// A mixin shared by [AsyncNotifierProvider] and [FutureProvider] for dealing with
Expand Down
13 changes: 13 additions & 0 deletions packages/riverpod/lib/src/async_notifier/family.dart
Expand Up @@ -86,4 +86,17 @@ class AsyncNotifierProviderFamily<NotifierT extends FamilyAsyncNotifier<T, Arg>,
super.name,
super.dependencies,
}) : super(providerFactory: AsyncNotifierFamilyProvider.new);

/// {@macro riverpod.overridewith}
Override overrideWithNotifier(NotifierT Function() create) {
return FamilyOverrideImpl<AsyncValue<T>, Arg,
AsyncNotifierFamilyProvider<NotifierT, T, Arg>>(
this,
(arg) => AsyncNotifierFamilyProvider<NotifierT, T, Arg>(
create,
from: from,
argument: arg,
),
);
}
}
11 changes: 8 additions & 3 deletions packages/riverpod/lib/src/framework/family.dart
Expand Up @@ -41,7 +41,7 @@ mixin _FamilyMixin<State, Arg, FamilyProvider extends ProviderBase<State>>
Override overrideWithProvider(
FamilyProvider Function(Arg argument) override,
) {
return _FamilyOverride<State, Arg, FamilyProvider>(this, override);
return FamilyOverrideImpl<State, Arg, FamilyProvider>(this, override);
}

@visibleForOverriding
Expand Down Expand Up @@ -73,13 +73,18 @@ abstract class FamilyOverride<State> implements Override {
ProviderBase<State> getProviderOverride(ProviderBase<State> provider);
}

class _FamilyOverride<State, Arg, FamilyProvider extends ProviderBase<State>>
/// An [Override] for families
@internal
class FamilyOverrideImpl<State, Arg, FamilyProvider extends ProviderBase<State>>
implements FamilyOverride<State> {
_FamilyOverride(this.overriddenFamily, this._newCreate);
/// An [Override] for families
// ignore: library_private_types_in_public_api
FamilyOverrideImpl(this.overriddenFamily, this._newCreate);

final FamilyProvider Function(Arg arg) _newCreate;

@override
// ignore: library_private_types_in_public_api
final _FamilyMixin<State, Arg, FamilyProvider> overriddenFamily;

@visibleForOverriding
Expand Down
3 changes: 2 additions & 1 deletion packages/riverpod/lib/src/framework/provider_base.dart
Expand Up @@ -249,7 +249,7 @@ mixin OverrideWithValueMixin<State> on ProviderBase<State> {
}
}

/// A mixin to add [overrideWithProvider] capability to providers.
/// A mixin to add `overrideWithProvider` capability to providers.
extension OverrideWithProviderExtension<State,
ProviderType extends ProviderBase<State>> on ProviderType {
/// {@template riverpod.overridewithprovider}
Expand Down Expand Up @@ -288,6 +288,7 @@ extension OverrideWithProviderExtension<State,
/// );
/// ```
/// {@endtemplate}
@Deprecated('Will be removed in 3.0.0. Use overrideWith instead.')
Override overrideWithProvider(ProviderType override) {
assert(
override.dependencies == null,
Expand Down

0 comments on commit b02b333

Please sign in to comment.