Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,28 +1,67 @@
part of '../../disco_internal.dart';

sealed class _ArgProviderOverrideType<T extends Object, A> {}

// TODO (manuel-plavsic): I would get rid of this (and remove sealed class)
final class _ArgProviderOverrideWithValue<T extends Object, A>
extends _ArgProviderOverrideType<T, A> {
_ArgProviderOverrideWithValue._(this.argument, this.value, this.debugName);

final A argument;

final T value;

/// {@macro Provider.debugName}
final String? debugName;
}

final class _ArgProviderOverrideWithProvider<T extends Object, A>
extends _ArgProviderOverrideType<T, A> {
_ArgProviderOverrideWithProvider._(this.mockProvider);
final ArgProvider<T, A> mockProvider;
}

/// Override that, if inserted into the widget tree, takes precedence over
/// [_argProvider].
/// [_originalArgProvider].
@immutable
class ArgProviderOverride<T extends Object, A> extends Override {
ArgProviderOverride._(this._argProvider, T value, {this.debugName})
: _value = value,
ArgProviderOverride._withValue(
this._originalArgProvider,
A arg,
T value,
String? debugName,
) : _overrideType = _ArgProviderOverrideWithValue._(
arg,
value,
debugName,
),
super._();

ArgProviderOverride._withArgProvider(
this._originalArgProvider,
ArgProvider<T, A> mockProvider,
) : _overrideType = _ArgProviderOverrideWithProvider._(mockProvider),
super._();

/// The reference of the argument provider to override.
final ArgProvider<T, A> _argProvider;
final ArgProvider<T, A> _originalArgProvider;

/// The overridden value.
final T _value;
final _ArgProviderOverrideType<T, A> _overrideType;

// Utils leveraged by ProviderScope -----------------------------------------

/// Given an argument, creates a [Provider] with that argument.
/// This method is used internally by [ProviderScope].
Provider<T> _generateIntermediateProvider() => Provider<T>(
(_) => _value,
lazy: false,
);

/// {@macro Provider.debugName}
final String? debugName;
Provider<T> _generateIntermediateProvider() => switch (_overrideType) {
// TODO (manuel-plavsic): I would get rid of this
_ArgProviderOverrideWithValue(:final T value) => Provider<T>(
(_) => value,
lazy: false,
),
// TODO (manuel-plavsic): I would keep only this part below (and remove pattern matching)
// TODO (manuel-plavsic): this requires a major change in ProviderScope: a new layer of intermediate providers should be added (this time of type ArgProvider, not just Provider)
_ArgProviderOverrideWithProvider(:final ArgProvider<T, A> mockProvider) =>
mockProvider,
};
}
51 changes: 41 additions & 10 deletions packages/disco/lib/src/models/overrides/provider_override.dart
Original file line number Diff line number Diff line change
@@ -1,26 +1,57 @@
part of '../../disco_internal.dart';

sealed class _ProviderOverrideType<T extends Object> {}

// TODO (manuel-plavsic): I would get rid of this (and remove sealed class)
final class _ProviderOverrideWithValue<T extends Object>
extends _ProviderOverrideType<T> {
_ProviderOverrideWithValue._(this.value, this.debugName);
final T value;

/// {@macro Provider.debugName}
final String? debugName;
}

final class _ProviderOverrideWithProvider<T extends Object>
extends _ProviderOverrideType<T> {
_ProviderOverrideWithProvider._(this.mockProvider);
final Provider<T> mockProvider;
}

/// Override that, if inserted into the widget tree, takes precedence over
/// [_provider].
/// [_originalProvider].
@immutable
class ProviderOverride<T extends Object> extends Override {
ProviderOverride._(
this._provider,
ProviderOverride._withValue(
this._originalProvider,
T value,
) : _value = value,
String? debugName,
) : _overrideType = _ProviderOverrideWithValue._(value, debugName),
super._();

ProviderOverride._withProvider(
this._originalProvider,
Provider<T> mockProvider,
) : _overrideType = _ProviderOverrideWithProvider._(mockProvider),
super._();

/// The reference of the provider to override.
final Provider<T> _provider;
final Provider<T> _originalProvider;

final T _value;
final _ProviderOverrideType<T> _overrideType;

// Utils leveraged by ProviderScope -----------------------------------------

/// Creates a [Provider].
/// This method is used internally by [ProviderScope].
Provider<T> _generateIntermediateProvider() => Provider<T>(
(_) => _value,
lazy: false,
);
Provider<T> _generateIntermediateProvider() => switch (_overrideType) {
// TODO (manuel-plavsic): I would get rid of this
_ProviderOverrideWithValue(:final T value) => Provider<T>(
(_) => value,
lazy: false,
),
// TODO (manuel-plavsic): I would keep only this (and remove pattern matching)
_ProviderOverrideWithProvider(:final Provider<T> mockProvider) =>
mockProvider,
};
}
13 changes: 11 additions & 2 deletions packages/disco/lib/src/models/providers/provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,17 @@ class Provider<T extends Object> extends InstantiableProvider {
/// [ProviderScopeOverride].
/// {@endtemplate}
@visibleForTesting
ProviderOverride<T> overrideWithValue(T value) =>
ProviderOverride._(this, value);
ProviderOverride<T> overrideWithValue(T value, {String? debugName}) =>
ProviderOverride._withValue(this, value, debugName);

/// {@template Provider.overrideWithValue}
/// It creates an override of this provider to be passed to
/// [ProviderScopeOverride].
/// {@endtemplate}
@visibleForTesting
ProviderOverride<T> overrideWithProvider(
Provider<T> override,
) => ProviderOverride._withProvider(this, override);

// DI methods ---------------------------------------------------------------

Expand Down
12 changes: 10 additions & 2 deletions packages/disco/lib/src/models/providers/provider_argument.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,16 @@ class ArgProvider<T extends Object, A> {

/// {@macro Provider.overrideWithValue}
@visibleForTesting
ArgProviderOverride<T, A> overrideWithValue(T value) =>
ArgProviderOverride._(this, value, debugName: debugName);
ArgProviderOverride<T, A> overrideWithValue(
A arg,
T value, {
String? debugName,
}) => ArgProviderOverride._withValue(this, arg, value, debugName);

@visibleForTesting
ArgProviderOverride<T, A> overrideWithProvider(
ArgProvider<T, A> override,
) => ArgProviderOverride._withArgProvider(this, override);

// ---
// DI methods
Expand Down
9 changes: 5 additions & 4 deletions packages/disco/lib/src/widgets/provider_scope.dart
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@ class ProviderScopeState extends State<ProviderScope> {
// check if there are multiple providers of the same type
final ids = <Provider>[];
for (final override in providerOverrides) {
final id = override._provider; // the instance of the provider
final id = override._originalProvider; // the instance of the provider
if (ids.contains(id)) {
throw MultipleProviderOverrideOfSameInstance();
}
Expand All @@ -367,7 +367,7 @@ class ProviderScopeState extends State<ProviderScope> {
);

for (final override in providerOverrides) {
final id = override._provider;
final id = override._originalProvider;

allProvidersInScope[id] = override._generateIntermediateProvider();

Expand All @@ -388,7 +388,8 @@ class ProviderScopeState extends State<ProviderScope> {
// check if there are multiple providers of the same type
final ids = <ArgProvider>[];
for (final override in argProviderOverrides) {
final id = override._argProvider; // the instance of the provider
final id =
override._originalArgProvider; // the instance of the provider
if (ids.contains(id)) {
throw MultipleProviderOverrideOfSameInstance();
}
Expand All @@ -400,7 +401,7 @@ class ProviderScopeState extends State<ProviderScope> {
);

for (final override in argProviderOverrides) {
final id = override._argProvider;
final id = override._originalArgProvider;

allArgProvidersInScope[id] = override._generateIntermediateProvider();

Expand Down
38 changes: 35 additions & 3 deletions packages/disco/test/disco_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -698,6 +698,36 @@ void main() {
expect(textFinder('number: 1'), findsOneWidget);
});

testWidgets('''ProviderScopeOverride should override providers''', (
tester,
) async {
final numberProvider = Provider<int>((_) => 0);
final mockNumberProvider = Provider<int>((_) => 9);
await tester.pumpWidget(
ProviderScopeOverride(
overrides: [
numberProvider.overrideWithProvider(mockNumberProvider),
],
child: MaterialApp(
home: ProviderScope(
providers: [
numberProvider,
],
child: Builder(
builder: (context) {
final number = numberProvider.of(context);
return Text(number.toString());
},
),
),
),
),
);
expect(find.text('9'), findsOneWidget);
});

// TODO(manuel): add other test (check also that debug is different, etc.)

testWidgets('''ProviderScopeOverride should override providers''', (
tester,
) async {
Expand Down Expand Up @@ -729,10 +759,12 @@ void main() {
tester,
) async {
final numberProvider = Provider.withArgument((_, int arg) => arg);
final mockNumberProvider = Provider.withArgument((_, int arg) => 4);
await tester.pumpWidget(
ProviderScopeOverride(
overrides: [
numberProvider.overrideWithValue(16),
// numberProvider.overrideWithValue(1, 16),
numberProvider.overrideWithProvider(mockNumberProvider),
],
child: MaterialApp(
home: ProviderScope(
Expand Down Expand Up @@ -864,8 +896,8 @@ void main() {
home: Scaffold(
body: ProviderScopeOverride(
overrides: [
numberProvider.overrideWithValue(1),
numberProvider.overrideWithValue(2),
numberProvider.overrideWithValue(0, 1),
numberProvider.overrideWithValue(1, 2),
],
child: Builder(
builder: (context) {
Expand Down
Loading