From cd2205ac3c3c17a5f140756e5414948ec1508e4e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 2 Mar 2026 10:20:22 +0000 Subject: [PATCH 1/5] Initial plan From 4d790f44744dba4dfa63a65ca12d16190e24a74f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 2 Mar 2026 10:25:53 +0000 Subject: [PATCH 2/5] Add overrideWithFunction and resetOverride to ArgProvider Co-authored-by: nank1ro <60045235+nank1ro@users.noreply.github.com> --- .../models/providers/provider_argument.dart | 37 ++++++- packages/disco/test/disco_test.dart | 97 +++++++++++++++++++ 2 files changed, 132 insertions(+), 2 deletions(-) diff --git a/packages/disco/lib/src/models/providers/provider_argument.dart b/packages/disco/lib/src/models/providers/provider_argument.dart index c64d52e..4a9d69f 100644 --- a/packages/disco/lib/src/models/providers/provider_argument.dart +++ b/packages/disco/lib/src/models/providers/provider_argument.dart @@ -11,7 +11,6 @@ typedef CreateArgProviderValueFn = /// A [Provider] that needs to be given an initial argument before /// it can be used. /// {@endtemplate} -@immutable class ArgProvider { /// {@macro ArgProvider} ArgProvider._( @@ -32,6 +31,11 @@ class ArgProvider { /// {@macro Provider.dispose} final DisposeProviderValueFn? _disposeValue; + /// An optional function that overrides [_createValue] during testing. + /// When set, any argument passed to the provider is forwarded to this + /// function instead of the original [_createValue]. + CreateArgProviderValueFn? _overrideFn; + // --- // Overrides // --- @@ -41,6 +45,33 @@ class ArgProvider { ArgProviderOverride overrideWithValue(T value) => ArgProviderOverride._(this, value, debugName: debugName); + /// Overrides the create function of this provider with [fn] for testing. + /// + /// Any argument passed to this provider in the widget tree will be forwarded + /// to [fn] instead of the original create function. This allows testing the + /// argument being passed to the provider while using a custom implementation. + /// + /// Example: + /// ```dart + /// final numberProvider = Provider.withArgument((context, int arg) => arg * 2); + /// + /// testWidgets('test', (tester) async { + /// numberProvider.overrideWithFunction((context, arg) => arg * 4); + /// addTearDown(numberProvider.resetOverride); + /// // numberProvider(1) will now return 4 instead of 2 + /// }); + /// ``` + @visibleForTesting + void overrideWithFunction(CreateArgProviderValueFn fn) { + _overrideFn = fn; + } + + /// Resets the function override set by [overrideWithFunction]. + @visibleForTesting + void resetOverride() { + _overrideFn = null; + } + // --- // DI methods // --- @@ -78,8 +109,10 @@ class ArgProvider { /// Given an argument, creates a [Provider] with that argument. /// This method is used internally by [ProviderScope]. + /// If [_overrideFn] is set (via [overrideWithFunction]), it is used instead + /// of [_createValue]. Provider _generateIntermediateProvider(A arg) => Provider( - (context) => _createValue(context, arg), + (context) => (_overrideFn ?? _createValue)(context, arg), dispose: _disposeValue, lazy: _lazy, ); diff --git a/packages/disco/test/disco_test.dart b/packages/disco/test/disco_test.dart index 5b90b53..d5001c2 100644 --- a/packages/disco/test/disco_test.dart +++ b/packages/disco/test/disco_test.dart @@ -752,6 +752,103 @@ void main() { expect(find.text('16'), findsOneWidget); }); + testWidgets( + 'overrideWithFunction replaces the create function of an ArgProvider', + (tester) async { + final numberProvider = Provider.withArgument( + (_, int arg) => arg * 2, + ); + + numberProvider.overrideWithFunction((_, arg) => arg * 4); + addTearDown(numberProvider.resetOverride); + + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: ProviderScope( + providers: [numberProvider(1)], + child: Builder( + builder: (context) { + final number = numberProvider.of(context); + return Text(number.toString()); + }, + ), + ), + ), + ), + ); + + // Without override: 1 * 2 = 2. With override: 1 * 4 = 4. + expect(find.text('4'), findsOneWidget); + }, + ); + + testWidgets( + 'overrideWithFunction forwards the arg to the overriding function', + (tester) async { + int? capturedArg; + final numberProvider = Provider.withArgument( + (_, int arg) => arg * 2, + ); + + numberProvider.overrideWithFunction((_, arg) { + capturedArg = arg; + return arg * 10; + }); + addTearDown(numberProvider.resetOverride); + + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: ProviderScope( + providers: [numberProvider(7)], + child: Builder( + builder: (context) { + final number = numberProvider.of(context); + return Text(number.toString()); + }, + ), + ), + ), + ), + ); + + expect(find.text('70'), findsOneWidget); + expect(capturedArg, 7); + }, + ); + + testWidgets( + 'resetOverride restores the original create function', + (tester) async { + final numberProvider = Provider.withArgument( + (_, int arg) => arg * 2, + ); + + numberProvider.overrideWithFunction((_, arg) => arg * 4); + numberProvider.resetOverride(); + + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: ProviderScope( + providers: [numberProvider(3)], + child: Builder( + builder: (context) { + final number = numberProvider.of(context); + return Text(number.toString()); + }, + ), + ), + ), + ), + ); + + // After reset, original function (arg * 2) is used: 3 * 2 = 6. + expect(find.text('6'), findsOneWidget); + }, + ); + testWidgets('Only one ProviderScopeOverride can be present', (tester) async { final numberProvider = Provider((_) => 0); await tester.pumpWidget( From 47c075ad068f9e0f5b587d9616dfa14bff968fce Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 2 Mar 2026 10:35:25 +0000 Subject: [PATCH 3/5] Remove resetOverride from ArgProvider Co-authored-by: nank1ro <60045235+nank1ro@users.noreply.github.com> --- .../models/providers/provider_argument.dart | 7 ---- packages/disco/test/disco_test.dart | 33 ------------------- 2 files changed, 40 deletions(-) diff --git a/packages/disco/lib/src/models/providers/provider_argument.dart b/packages/disco/lib/src/models/providers/provider_argument.dart index 4a9d69f..3741dab 100644 --- a/packages/disco/lib/src/models/providers/provider_argument.dart +++ b/packages/disco/lib/src/models/providers/provider_argument.dart @@ -57,7 +57,6 @@ class ArgProvider { /// /// testWidgets('test', (tester) async { /// numberProvider.overrideWithFunction((context, arg) => arg * 4); - /// addTearDown(numberProvider.resetOverride); /// // numberProvider(1) will now return 4 instead of 2 /// }); /// ``` @@ -66,12 +65,6 @@ class ArgProvider { _overrideFn = fn; } - /// Resets the function override set by [overrideWithFunction]. - @visibleForTesting - void resetOverride() { - _overrideFn = null; - } - // --- // DI methods // --- diff --git a/packages/disco/test/disco_test.dart b/packages/disco/test/disco_test.dart index d5001c2..9935b9a 100644 --- a/packages/disco/test/disco_test.dart +++ b/packages/disco/test/disco_test.dart @@ -760,7 +760,6 @@ void main() { ); numberProvider.overrideWithFunction((_, arg) => arg * 4); - addTearDown(numberProvider.resetOverride); await tester.pumpWidget( MaterialApp( @@ -795,7 +794,6 @@ void main() { capturedArg = arg; return arg * 10; }); - addTearDown(numberProvider.resetOverride); await tester.pumpWidget( MaterialApp( @@ -818,37 +816,6 @@ void main() { }, ); - testWidgets( - 'resetOverride restores the original create function', - (tester) async { - final numberProvider = Provider.withArgument( - (_, int arg) => arg * 2, - ); - - numberProvider.overrideWithFunction((_, arg) => arg * 4); - numberProvider.resetOverride(); - - await tester.pumpWidget( - MaterialApp( - home: Scaffold( - body: ProviderScope( - providers: [numberProvider(3)], - child: Builder( - builder: (context) { - final number = numberProvider.of(context); - return Text(number.toString()); - }, - ), - ), - ), - ), - ); - - // After reset, original function (arg * 2) is used: 3 * 2 = 6. - expect(find.text('6'), findsOneWidget); - }, - ); - testWidgets('Only one ProviderScopeOverride can be present', (tester) async { final numberProvider = Provider((_) => 0); await tester.pumpWidget( From 2b53dc4a8968dd9d615fbf8b86761a88a7361cd0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 2 Mar 2026 10:37:28 +0000 Subject: [PATCH 4/5] Bump version to 2.1.0 and update CHANGELOG Co-authored-by: nank1ro <60045235+nank1ro@users.noreply.github.com> --- packages/disco/CHANGELOG.md | 4 ++++ packages/disco/pubspec.yaml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/disco/CHANGELOG.md b/packages/disco/CHANGELOG.md index 68d8b21..1641093 100644 --- a/packages/disco/CHANGELOG.md +++ b/packages/disco/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.1.0 + +- **FEAT**: Add `overrideWithFunction` to `ArgProvider` for dynamic test overrides. This allows passing a custom create function that receives the original argument, enabling inspection and custom logic during testing. + ## 2.0.0 - **FEAT**: Allow providers in the same `ProviderScope` to depend on previously declared providers. This simplifies the development experience. This friendlier syntax does not introduce circular dependencies. diff --git a/packages/disco/pubspec.yaml b/packages/disco/pubspec.yaml index 3b90aa6..8e32359 100644 --- a/packages/disco/pubspec.yaml +++ b/packages/disco/pubspec.yaml @@ -1,6 +1,6 @@ name: disco description: A Flutter library bringing a new concept of scoped providers for dependency injection, which are independent of any specific state management solution. -version: 2.0.0 +version: 2.1.0 repository: https://github.com/our-creativity/disco homepage: https://disco.mariuti.com documentation: https://disco.mariuti.com From 0daf85ac1988c8336883fa87674f0d5a32306b03 Mon Sep 17 00:00:00 2001 From: Alexandru Mariuti Date: Mon, 2 Mar 2026 11:44:47 +0100 Subject: [PATCH 5/5] Update disco dependency version to 2.1.0 --- docs/src/content/docs/installing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/content/docs/installing.md b/docs/src/content/docs/installing.md index 5a1be71..da6cebd 100644 --- a/docs/src/content/docs/installing.md +++ b/docs/src/content/docs/installing.md @@ -25,7 +25,7 @@ environment: flutter: ">=3.27.0" dependencies: - disco: ^1.0.0 + disco: ^2.1.0 flutter: sdk: flutter ```