Skip to content

Add option to override whole provider/argprovider - #40

Open
manuel-plavsic wants to merge 7 commits into
mainfrom
fix/allow-whole-provider-override
Open

Add option to override whole provider/argprovider#40
manuel-plavsic wants to merge 7 commits into
mainfrom
fix/allow-whole-provider-override

Conversation

@manuel-plavsic

@manuel-plavsic manuel-plavsic commented Mar 23, 2026

Copy link
Copy Markdown
Member

This PR attempts to solve #35.

Tl;dr: I propose to remove overrideWithValue in favour of overrideWithProvider. For example, instead of:

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

one should write

  testWidgets('''ProviderScopeOverride should override providers''', (
    tester,
  ) async {
    final numberProvider = Provider<int>((_) => 0);
    final mockNumberProvider = Provider<int>((_) => 100);
    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);
  });

Explanation: this second variant can do everything the first variant can do. It is just more powerful, as also lazy, a proper create and dispose can be specified.

This is even more useful when working with argument providers, whose mocking functionality is currently not ideal IMO. At the moment, one can only override the whole argument provider, but cannot make argument distinctions. This defeats the point of argument providers. Here is how it currently is on main:

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

So, we definitely need arguments. In my current implementation, they would work like this:

  testWidgets('''ProviderScopeOverride should override argument providers''', (
    tester,
  ) async {
    final numberProvider = Provider.withArgument((_, int arg) => arg);
    await tester.pumpWidget(
      ProviderScopeOverride(
        overrides: [
          numberProvider.overrideWithValue(1, 16), // 1 is the arg, 16 the value
        ],
        child: MaterialApp(
          home: ProviderScope(
            providers: [
              numberProvider(1),
            ],
            child: Builder(
              builder: (context) {
                final number = numberProvider.of(context);
                return Text(number.toString());
              },
            ),
          ),
        ),
      ),
    );
    expect(find.text('16'), findsOneWidget);
  });

However, I don't think this approach is ideal. People will have to write some custom logic somewhere every time they want to use the override (because they need to use specific arguments and provide specific values for that).

We should purse this approach instead:

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

This way, one can just write the custom logic in mockNumberProvider's create and dispose, without polluting the widget tree. This is also what was asked in the linked issue.

The logic for this would still have to be adapted in ProviderScope. Before I invest time there, is this okay for you @nank1ro?
Right now, I added sealed classes and pattern matching to allow both overrideWithValue and overrideWithProvider, but if we get rid of the former, we don't need the sealed classes and pattern matching anymore.

Summary by CodeRabbit

  • New Features
    • Providers now create values lazily and support clearer registration and override patterns.
    • Added circular-dependency detection with actionable error reporting.
    • Improved provider lifecycle management, scope behavior, and disposal ordering.
    • Expanded example app demonstrating provider lifecycles, nested scopes, arguments, portals, and errors.
  • Documentation
    • Updated guidance and examples for provider registration, overrides, testing, laziness, and retrieval.
    • Removed the deprecated configuration page.
  • Breaking Changes
    • Updated provider and override APIs; removed legacy lazy configuration and forward-reference behavior.
    • Requires Flutter 3.38.0 or later.

@manuel-plavsic
manuel-plavsic requested a review from nank1ro March 23, 2026 13:45
@manuel-plavsic manuel-plavsic self-assigned this Mar 23, 2026
@coderabbitai

coderabbitai Bot commented Mar 23, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

Disco 3.0 introduces callable provider bindings, provider-based overrides, always-lazy value creation, recursive scope lookup, circular-dependency detection, reverse-order disposal, updated tests, documentation, examples, and benchmarks.

Changes

Provider API and runtime

Layer / File(s) Summary
Provider bindings and override contracts
packages/disco/lib/src/models/*, packages/disco/pubspec.yaml
Providers now return ValueBinding objects. Lazy creation is always enabled. overrideWithValue is replaced by provider-based overrideWith.
ProviderScope lookup and lifecycle
packages/disco/lib/src/widgets/provider_scope.dart, packages/disco/lib/src/widgets/provider_scope_override.dart
ProviderScope uses private binding state, recursive lookup, lazy creation, circular-dependency errors, override registration, scope immutability checks, and reverse-order disposal.
Lifecycle and dependency regression coverage
packages/disco/test/disco_test.dart, packages/disco/example/test/disco_test.dart, examples/*/test/*
Tests cover provider bindings, overrides, lazy dependencies, portals, circular dependencies, disposal, retries, and scope updates.

Documentation and examples

Layer / File(s) Summary
Documentation and usage migration
docs/src/content/docs/core/*, docs/src/content/docs/examples/*, docs/src/content/docs/miscellaneous/*, docs/src/content/docs/installing.md, docs/src/content/docs/index.mdx, packages/disco/README.md, packages/disco/CHANGELOG.md, examples/*
Documentation and examples use invoked providers and provider-based overrides. They describe lazy creation, recursive lookup, circular dependencies, disposal, and updated installation requirements.
Example application and benchmark updates
packages/disco/example/lib/main.dart, packages/disco/example/README.md, packages/disco/benchmark/*, packages/disco/.gitignore, packages/disco/.metadata
The example demonstrates provider lifecycle scenarios. Benchmarks measure registration, creation, retrieval, dependencies, argument providers, and nested scopes separately.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

Suggested labels: enhancement

Suggested reviewers: nank1ro

Poem

I hop through scopes where providers grow,
Lazy roots wake when injections flow.
Cycles are caught, and values depart,
In reverse order, tidy and smart.
New bindings shine in the moonlit code—
A rabbit applauds the lighter load.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main API change: support for whole-provider and argument-provider overrides.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/allow-whole-provider-override

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Mar 23, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

Impacted file tree graph

@@            Coverage Diff            @@
##              main       #40   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           10        10           
  Lines          288       288           
=========================================
  Hits           288       288           
Files with missing lines Coverage Δ
...kages/disco/lib/src/models/providers/provider.dart 100.00% <100.00%> (ø)
packages/disco/lib/src/utils/extensions.dart 100.00% <100.00%> (ø)
packages/disco/lib/src/widgets/provider_scope.dart 100.00% <100.00%> (ø)
...disco/lib/src/widgets/provider_scope_override.dart 100.00% <100.00%> (ø)

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@manuel-plavsic

manuel-plavsic commented Mar 23, 2026

Copy link
Copy Markdown
Member Author

Technically, we could easily add two wrapper types MockProvider and MockArgProvider (with constructor MockProvider.withArg) to ensure only mocks can be specified in ProviderScopeOverride.overrides and only normal providers can specified in ProviderScope.providers. This would prevent using the wrong kind of provider in the wrong widget.

@nank1ro

nank1ro commented Mar 23, 2026

Copy link
Copy Markdown
Member

@manuel-plavsic If you remove overrideWithValue it's going to be a breaking change and we have to bump a major.
I'd prefer to mark it as deprecated and remove it later in a next major.
So existing users can migrate slowly.

Additionally, what do you think about

numberProvider.overrideWith(mockNumberProvider),
// instead of 
numberProvider.overrideWithProvider(mockNumberProvider),

?

@manuel-plavsic

Copy link
Copy Markdown
Member Author

@nank1ro good idea, I'll rename the method to overrideWith 😉

@manuel-plavsic

manuel-plavsic commented Mar 23, 2026

Copy link
Copy Markdown
Member Author

and yes, it makes sense to keep overrideWithValue and mark it as deprecated

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 9, 2026

Copy link
Copy Markdown

Deploying flutter-disco with  Cloudflare Pages  Cloudflare Pages

Latest commit: 77f8ca8
Status: ✅  Deploy successful!
Preview URL: https://e5b200f1.flutter-disco.pages.dev
Branch Preview URL: https://fix-allow-whole-provider-ove.flutter-disco.pages.dev

View logs

@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown

Provider Benchmark Results

Date: 2026-08-09 16:31:28 UTC

Results

Benchmark Time (ms)
Register 100 providers 233
Create 100 provider values 16
Create 50 values with dependencies 10
Retrieve 100 provider values 0
Create 100 ArgProvider values 13
Access 100 providers in nested scopes 10
Complex dependency chain (30 providers) 11
ArgProviders with dependencies (50) 11
Large scale (500 providers) 14
Deep dependency chain (100 levels) 10
Wide dependency tree (100 dependents) 10
Multiple nested scopes (5 levels) 9

@manuel-plavsic

manuel-plavsic commented Aug 9, 2026

Copy link
Copy Markdown
Member Author

@nank1ro I was partially assisted by Claude Opus for this PR. Multiple changes were made, not only what is in the title. The easiest way to understand all is to read the commit messages for every commit.

Note that #32 introduced this issue #39. That part had to be rewritten differently. Good news: there is no global state anymore at all. Actually, what was very nice of Claude (Opus 5 High) is that it detected the global state and wanted to replace it with a better alternative, which solved the issue.

All providers were made lazy because with eager evaluation there is the problem that if the provider is eager, it will be created even in presence of a mock. This really defeats the idea of providers. There is a workaround to have them eager, which is to inject the provider immediately (a bit like what riverpod does).

Another change is now the values of the normal providers (i.e., without argument) are injected like this: myProvider(). This was done for consistency reasons: with an ArgProvider, you have to do: myArgProvider(5). The approach we had until now was a bit confusing.

Claude recommended using:

class _InheritedProvider extends InheritedModel<Object> {

instead of

class _InheritedProvider extends InheritedWidget {

However, I am not fully sure about that. You are more the expert in this area. Do you think the suggestion is good, or should I revert this part?

@manuel-plavsic
manuel-plavsic marked this pull request as ready for review August 9, 2026 15:08

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 12

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/disco/test/disco_test.dart (1)

729-784: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Two tests share the same name.

The test at Line 729 and the test at Line 757 are both named ProviderScopeOverride should override providers. Duplicate names make a failure report ambiguous. The two bodies also cover the same scenario with different constants.

Rename one test, or delete the redundant one.

♻️ Proposed rename
-  testWidgets('''ProviderScopeOverride should override providers''', (
+  testWidgets('''ProviderScopeOverride should override a provider declared in a nested scope''', (
     tester,
   ) async {
     final numberProvider = Provider<int>((_) => 0);
     final number100Provider = Provider<int>((_) => 100);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/disco/test/disco_test.dart` around lines 729 - 784, Rename one of
the duplicate testWidgets cases around the ProviderScopeOverride tests so each
test name uniquely identifies its scenario, while preserving both test bodies
and their existing assertions.
packages/disco/lib/src/models/providers/arg_provider.dart (1)

79-84: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Forward debugName to the intermediate provider.

Provider._generateIntermediateProvider in packages/disco/lib/src/models/providers/provider.dart (Line 134) forwards debugName. This method does not. The intermediate provider becomes the key of ProviderScopeState._createdValues, and disposal errors are reported with entry.key._debugName in packages/disco/lib/src/widgets/provider_scope.dart (Line 497). Without the forward, a disposal error for an argument provider loses the user-supplied debug name.

🔧 Proposed fix
   Provider<T> _generateIntermediateProvider(A arg) => Provider<T>(
     (context) => _createValue(context, arg),
     dispose: _disposeValue,
+    debugName: debugName,
   );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/disco/lib/src/models/providers/arg_provider.dart` around lines 79 -
84, Update _generateIntermediateProvider in the argument provider implementation
to forward the provider’s existing debugName when constructing the intermediate
Provider, matching Provider._generateIntermediateProvider and preserving the
user-supplied name for disposal error reporting.
🧹 Nitpick comments (5)
examples/solidart/test/widget_test.dart (1)

24-26: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Consider disposing the mocked controller.

The mock provider declares no dispose. The mock's dispose replaces the original one, so todosController is never disposed when the scope unmounts. Signal-based controllers usually need disposal at teardown.

Add a dispose callback if TodosController owns resources.

♻️ Proposed change
         todosControllerProvider.overrideWith(
-          Provider((context) => todosController),
+          Provider(
+            (context) => todosController,
+            dispose: (controller) => controller.dispose(),
+          ),
         ),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/solidart/test/widget_test.dart` around lines 24 - 26, Update the
todosControllerProvider override to declare a dispose callback that disposes
todosController when the provider scope unmounts, preserving the controller’s
teardown behavior.
packages/disco/lib/src/widgets/provider_scope.dart (1)

229-307: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Confirm that the immutability check is debug-only.

didUpdateWidget calls _debugCheckScopeDidNotChange inside an assert. In release builds the check never runs, so a changed provider list is silently ignored. The doc comment explains the reporting choice but not the debug-only scope. Add that fact to the doc comment so users do not expect the report in profile or release builds.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/disco/lib/src/widgets/provider_scope.dart` around lines 229 - 307,
Update the documentation for _debugCheckScopeDidNotChange to explicitly state
that the immutability check and its reported error run only in debug builds, not
profile or release builds. Keep the existing explanation of reporting versus
throwing unchanged.
packages/disco/example/lib/main.dart (1)

115-120: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a dispose callback to modelProvider.

ModelImplementation extends Model, which is a ChangeNotifier. HomePage provides modelProvider in a page-scoped ProviderScope at line 230, so the value is discarded when the page is removed, but dispose() is never called on it. loggerProvider and cartProvider both pass a dispose callback. Making this provider consistent avoids teaching a leak in the canonical example.

♻️ Proposed change
 final modelProvider = Provider<Model>(
   (context) => ModelImplementation(),
+  dispose: (model) => model.dispose(),
   debugName: 'model',
 );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/disco/example/lib/main.dart` around lines 115 - 120, Add a dispose
callback to modelProvider that invokes dispose on its ModelImplementation/Model
value, matching the existing cleanup pattern used by loggerProvider and
cartProvider while preserving lazy creation and the current provider API.
packages/disco/benchmark/provider_benchmark.dart (2)

52-71: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Move provider construction outside the stopwatch.

This benchmark measures registration. The stopwatch starts at line 53, before List.generate builds the 100 Provider instances at lines 55-61, so the reported time also includes provider construction. Every other benchmark builds the providers before starting the stopwatch. Align this one so the numbers stay comparable.

♻️ Proposed change
     testWidgets('Benchmark: Register 100 providers', (tester) async {
-      final stopwatch = Stopwatch()..start();
-
       final providers = List.generate(
         100,
         (i) => Provider(
           (_) => 'Value$i',
           debugName: 'provider$i',
         ),
       );
+
+      final instantiatedProviders = _instantiateAll(providers);
+
+      final stopwatch = Stopwatch()..start();
 
       await tester.pumpWidget(
         MaterialApp(
           home: ProviderScope(
-            providers: _instantiateAll(providers),
+            providers: instantiatedProviders,
             // No value is created, since nothing is injected.
             child: Container(),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/disco/benchmark/provider_benchmark.dart` around lines 52 - 71, Move
the `providers` List.generate construction in the `Benchmark: Register 100
providers` test before `Stopwatch` is started, so the timed section measures
only provider registration through `pumpWidget` and remains comparable with the
other benchmarks.

152-177: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

The "Retrieve" benchmark measures first creation, not retrieval.

Values are created lazily on first injection, as the file comment at lines 10-13 states. The builder at line 166 is the first injection point, so the loop at lines 170-172 creates the 100 values. The result duplicates "Create 100 provider values" and reports nothing about cached lookup cost. Inject the providers once before the timed loop, then measure a second pass.

♻️ Proposed change
             child: Builder(
               builder: (context) {
+                // Create all the values first, so that the loop below
+                // measures the retrieval of already created values.
+                for (final provider in providers) {
+                  provider.of(context);
+                }
+
                 final stopwatch = Stopwatch()..start();
 
-                // Access all providers to trigger creation
+                // Access all providers again: no value is created here.
                 for (final provider in providers) {
                   provider.of(context);
                 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/disco/benchmark/provider_benchmark.dart` around lines 152 - 177,
Update the “Retrieve 100 provider values” benchmark so the providers are first
accessed once before starting the Stopwatch, allowing lazy creation to complete
outside the measurement. Keep the timed loop as a second pass over providers
using provider.of(context), and continue recording the elapsed time under the
existing benchmark result key.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/src/content/docs/core/immutability.mdx`:
- Line 92: Update the comment describing ProviderScope’s debug-mode behavior to
state that it reports an error and continues with the original providers, rather
than saying it throws when showDetails changes.

In `@docs/src/content/docs/core/provider-retrieval-process.mdx`:
- Line 21: Update the complexity aside in the provider retrieval process
documentation to distinguish the O(1) lookup within each ProviderScope from the
overall worst-case traversal cost of O(number of traversed ProviderScope
ancestors).

In `@docs/src/content/docs/core/scoped-di.mdx`:
- Around line 45-49: Update the provider invocation description in the Aside to
state that calling a provider returns the binding used to register it with
ProviderScope, rather than instantiating the provider or creating its value.
Clarify that the provider value is created lazily when injected, while
preserving the explanation about passing arguments and consistent syntax.

In `@docs/src/content/docs/core/testing.mdx`:
- Around line 8-12: Update the testing documentation around overrideWith to
state that the replacement must use the same provider kind as the provider being
overridden, including using an argument provider for Provider.withArgument;
preserve the existing explanation of override lifecycle and behavior.

In `@packages/disco/.metadata`:
- Line 10: Update the metadata configuration for the published packages/disco
package so it does not reference the example app’s lib/main.dart or
ios/Runner.xcodeproj/project.pbxproj paths. If the metadata is intended for the
Dart example app, move it to the example app’s metadata location instead.

In `@packages/disco/benchmark_results.md`:
- Around line 9-21: Regenerate packages/disco/benchmark_results.md from the
current benchmark output produced by _writeBenchmarkResults in
provider_benchmark.dart. Ensure the rows use the emitted keys, including
Register 100 providers, Create 100 provider values, Create 50 values with
dependencies, Retrieve 100 provider values, and Create 100 ArgProvider values,
and remove the obsolete Mixed lazy and eager (100 total) entry; commit the
updated generated results.

In `@packages/disco/CHANGELOG.md`:
- Line 4: Update the changelog entry to state that overrideWithValue is
deprecated rather than removed, and recommend migrating to overrideWith while
preserving the existing explanation of provider overriding behavior.

In `@packages/disco/example/lib/main.dart`:
- Around line 132-141: Update the documentation around analyticsProvider to
remove the ProviderForwardReferenceError reference. Either remove this
forward-reference example or describe only the requirement that loggerProvider
precede analyticsProvider in the providers list, without mentioning the removed
exception.

In `@packages/disco/lib/src/models/override.dart`:
- Around line 3-5: Update the dartdoc description in the override configuration
declaration to change “an mock provider” to “a mock provider,” without altering
the surrounding documentation.

In `@packages/disco/lib/src/models/providers/provider.dart`:
- Around line 72-81: The removed overrideWithValue API must remain available as
deprecated wrappers for gradual migration. In
packages/disco/lib/src/models/providers/provider.dart:72-81, add deprecated
overrideWithValue(T value) forwarding through overrideWith(Provider<T>((_) =>
value)); in packages/disco/lib/src/models/providers/arg_provider.dart:34-38, add
the equivalent deprecated wrapper forwarding through
overrideWith(Provider.withArgument<T, A>((_, _) => value)).

In `@packages/disco/pubspec.yaml`:
- Line 14: Update the Flutter SDK constraint in the pubspec configuration to the
lowest released version supported by the package, unless Disco depends on APIs
introduced in Flutter 3.38.0; if that dependency is intentional, document the
specific requirement instead. Preserve compatibility with the workspace
packages’ broader constraints.

In `@packages/disco/test/disco_test.dart`:
- Around line 2134-2138: Update the comment above the second buildTree call to
replace the stale “InstantiableProviders” type name with the current
“ValueBinding” name, without changing the test behavior.

---

Outside diff comments:
In `@packages/disco/lib/src/models/providers/arg_provider.dart`:
- Around line 79-84: Update _generateIntermediateProvider in the argument
provider implementation to forward the provider’s existing debugName when
constructing the intermediate Provider, matching
Provider._generateIntermediateProvider and preserving the user-supplied name for
disposal error reporting.

In `@packages/disco/test/disco_test.dart`:
- Around line 729-784: Rename one of the duplicate testWidgets cases around the
ProviderScopeOverride tests so each test name uniquely identifies its scenario,
while preserving both test bodies and their existing assertions.

---

Nitpick comments:
In `@examples/solidart/test/widget_test.dart`:
- Around line 24-26: Update the todosControllerProvider override to declare a
dispose callback that disposes todosController when the provider scope unmounts,
preserving the controller’s teardown behavior.

In `@packages/disco/benchmark/provider_benchmark.dart`:
- Around line 52-71: Move the `providers` List.generate construction in the
`Benchmark: Register 100 providers` test before `Stopwatch` is started, so the
timed section measures only provider registration through `pumpWidget` and
remains comparable with the other benchmarks.
- Around line 152-177: Update the “Retrieve 100 provider values” benchmark so
the providers are first accessed once before starting the Stopwatch, allowing
lazy creation to complete outside the measurement. Keep the timed loop as a
second pass over providers using provider.of(context), and continue recording
the elapsed time under the existing benchmark result key.

In `@packages/disco/example/lib/main.dart`:
- Around line 115-120: Add a dispose callback to modelProvider that invokes
dispose on its ModelImplementation/Model value, matching the existing cleanup
pattern used by loggerProvider and cartProvider while preserving lazy creation
and the current provider API.

In `@packages/disco/lib/src/widgets/provider_scope.dart`:
- Around line 229-307: Update the documentation for _debugCheckScopeDidNotChange
to explicitly state that the immutability check and its reported error run only
in debug builds, not profile or release builds. Keep the existing explanation of
reporting versus throwing unchanged.
🪄 Autofix

✅ Autofix completed


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 912adfe3-d119-4d43-809e-9275f6877ac8

📥 Commits

Reviewing files that changed from the base of the PR and between 738b1ea and ea0ea59.

📒 Files selected for processing (46)
  • docs/astro.config.mjs
  • docs/src/content/docs/core/configuration.md
  • docs/src/content/docs/core/immutability.mdx
  • docs/src/content/docs/core/modals.mdx
  • docs/src/content/docs/core/provider-retrieval-process.mdx
  • docs/src/content/docs/core/providers.mdx
  • docs/src/content/docs/core/scoped-di.mdx
  • docs/src/content/docs/core/testing.mdx
  • docs/src/content/docs/examples/auto-route.mdx
  • docs/src/content/docs/examples/basic.mdx
  • docs/src/content/docs/examples/bloc.mdx
  • docs/src/content/docs/examples/solidart.mdx
  • docs/src/content/docs/index.mdx
  • docs/src/content/docs/installing.md
  • docs/src/content/docs/miscellaneous/comparison-with-alternatives.mdx
  • docs/src/content/docs/miscellaneous/reactivity.md
  • examples/auto_route/lib/pages/books.dart
  • examples/bloc/lib/main.dart
  • examples/solidart/lib/pages/todos.dart
  • examples/solidart/lib/widgets/todos_body.dart
  • examples/solidart/test/widget_test.dart
  • packages/disco/.gitignore
  • packages/disco/.metadata
  • packages/disco/CHANGELOG.md
  • packages/disco/README.md
  • packages/disco/benchmark/provider_benchmark.dart
  • packages/disco/benchmark_results.md
  • packages/disco/example/README.md
  • packages/disco/example/analysis_options.yaml
  • packages/disco/example/lib/main.dart
  • packages/disco/example/test/disco_test.dart
  • packages/disco/lib/src/disco_internal.dart
  • packages/disco/lib/src/models/override.dart
  • packages/disco/lib/src/models/overrides/override.dart
  • packages/disco/lib/src/models/overrides/provider_argument_override.dart
  • packages/disco/lib/src/models/overrides/provider_override.dart
  • packages/disco/lib/src/models/providers/arg_provider.dart
  • packages/disco/lib/src/models/providers/instantiable_provider.dart
  • packages/disco/lib/src/models/providers/provider.dart
  • packages/disco/lib/src/models/value_binding.dart
  • packages/disco/lib/src/utils/disco_config.dart
  • packages/disco/lib/src/utils/extensions.dart
  • packages/disco/lib/src/widgets/provider_scope.dart
  • packages/disco/lib/src/widgets/provider_scope_override.dart
  • packages/disco/pubspec.yaml
  • packages/disco/test/disco_test.dart
💤 Files with no reviewable changes (7)
  • docs/src/content/docs/core/configuration.md
  • packages/disco/lib/src/models/overrides/provider_argument_override.dart
  • packages/disco/lib/src/utils/disco_config.dart
  • packages/disco/lib/src/models/providers/instantiable_provider.dart
  • docs/astro.config.mjs
  • packages/disco/lib/src/models/overrides/override.dart
  • packages/disco/lib/src/models/overrides/provider_override.dart

Since this would silently surface much later as a `ProviderWithoutScopeError`, it is reported as an error in debug mode:

```dart
// Throws in debug mode as soon as `showDetails` changes.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Correct the debug-mode behavior.

Line 92 says that this code throws. ProviderScope reports an error and continues with its original providers. Update the comment to say that it reports an error.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/src/content/docs/core/immutability.mdx` at line 92, Update the comment
describing ProviderScope’s debug-mode behavior to state that it reports an error
and continues with the original providers, rather than saying it throws when
showDetails changes.

3. If the provider is found, its value is returned. If the value has not been created yet (i.e. it is the first time the provider is injected), it gets created right before it is returned.

4. Otherwise, the search continues for the first `ProviderScope` ancestor.
4. If the provider is not found, the search proceeds to the next `ProviderScope` ancestor, continuing recursively up the widget tree until the root is reached.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Correct the lookup complexity claim.

This step can traverse multiple ProviderScope ancestors. The map lookup is O(1), but the complete lookup is O(number of traversed scopes) in the worst case. Update the following complexity aside.

🧰 Tools
🪛 LanguageTool

[style] ~21-~21: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...reated right before it is returned. 4. If the provider is not found, the search p...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/src/content/docs/core/provider-retrieval-process.mdx` at line 21, Update
the complexity aside in the provider retrieval process documentation to
distinguish the O(1) lookup within each ProviderScope from the overall
worst-case traversal cost of O(number of traversed ProviderScope ancestors).

Comment on lines +45 to +49
<Aside>
Note the parentheses: a provider is never inserted into a `ProviderScope`
directly. Calling it is what tells the scope to instantiate it, and it is
also what allows an argument to be passed. This way, the syntax is the same
for both kinds of providers.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Correct the provider invocation description.

Calling a provider returns the binding that registers it with ProviderScope. It does not create the provider value. The value is created lazily when it is injected.

Proposed fix
-  directly. Calling it is what tells the scope to instantiate it, and it is
-  also what allows an argument to be passed. This way, the syntax is the same
+  directly. Calling it creates the binding that registers the provider with
+  the scope. The provider value is created lazily when it is injected. Calling
+  it also allows an argument to be passed. This way, the syntax is the same
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<Aside>
Note the parentheses: a provider is never inserted into a `ProviderScope`
directly. Calling it is what tells the scope to instantiate it, and it is
also what allows an argument to be passed. This way, the syntax is the same
for both kinds of providers.
<Aside>
Note the parentheses: a provider is never inserted into a `ProviderScope`
directly. Calling it creates the binding that registers the provider with
the scope. The provider value is created lazily when it is injected. Calling
it also allows an argument to be passed. This way, the syntax is the same
for both kinds of providers.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/src/content/docs/core/scoped-di.mdx` around lines 45 - 49, Update the
provider invocation description in the Aside to state that calling a provider
returns the binding used to register it with ProviderScope, rather than
instantiating the provider or creating its value. Clarify that the provider
value is created lazily when injected, while preserving the explanation about
passing arguments and consistent syntax.

Comment on lines +8 to +12
Testing is done with overrides. You need to place a `ProviderScopeOverride` and then specify the `overrides` argument with a list containing the providers followed by `.overrideWith(provider)`.

An override replaces a provider **entirely**, and not just the value it holds: the provider passed to `overrideWith` is a regular provider, with its own `create` and `dispose`. This means a mock can, for instance, inject other providers through its context, exactly like the provider it replaces. The value of the original provider is never created at all.

An override also takes the place of the provider it replaces in every `ProviderScope` below the `ProviderScopeOverride`. The mock therefore has the very same lifecycle as the original provider: its value is created lazily where the value of the original provider would have been created, it is disposed when that `ProviderScope` is disposed, and there is one value per `ProviderScope` providing it.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Describe the required matching provider kind.

Line 10 states that overrideWith always receives a regular provider. An override for Provider.withArgument must receive an argument provider, as the later example shows. State that the override must use the same provider kind as the provider it replaces.

Proposed fix
-An override replaces a provider **entirely**, and not just the value it holds: the provider passed to `overrideWith` is a regular provider, with its own `create` and `dispose`.
+An override replaces a provider **entirely**, and not just the value it holds: the provider passed to `overrideWith` has the same provider kind as the provider it replaces, with its own `create` and `dispose`.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Testing is done with overrides. You need to place a `ProviderScopeOverride` and then specify the `overrides` argument with a list containing the providers followed by `.overrideWith(provider)`.
An override replaces a provider **entirely**, and not just the value it holds: the provider passed to `overrideWith` is a regular provider, with its own `create` and `dispose`. This means a mock can, for instance, inject other providers through its context, exactly like the provider it replaces. The value of the original provider is never created at all.
An override also takes the place of the provider it replaces in every `ProviderScope` below the `ProviderScopeOverride`. The mock therefore has the very same lifecycle as the original provider: its value is created lazily where the value of the original provider would have been created, it is disposed when that `ProviderScope` is disposed, and there is one value per `ProviderScope` providing it.
Testing is done with overrides. You need to place a `ProviderScopeOverride` and then specify the `overrides` argument with a list containing the providers followed by `.overrideWith(provider)`.
An override replaces a provider **entirely**, and not just the value it holds: the provider passed to `overrideWith` has the same provider kind as the provider it replaces, with its own `create` and `dispose`. This means a mock can, for instance, inject other providers through its context, exactly like the provider it replaces. The value of the original provider is never created at all.
An override also takes the place of the provider it replaces in every `ProviderScope` below the `ProviderScopeOverride`. The mock therefore has the very same lifecycle as the original provider: its value is created lazily where the value of the original provider would have been created, it is disposed when that `ProviderScope` is disposed, and there is one value per `ProviderScope` providing it.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/src/content/docs/core/testing.mdx` around lines 8 - 12, Update the
testing documentation around overrideWith to state that the replacement must use
the same provider kind as the provider being overridden, including using an
argument provider for Provider.withArgument; preserve the existing explanation
of override lifecycle and behavior.

Comment thread packages/disco/.metadata Outdated
revision: "2c9eb20739dfec95e2c74bd3dfa4601b0a8a36aa"
channel: "stable"

project_type: app

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Locate .metadata and pubspec files to confirm package versus app layout.
fd -H '^\.metadata$' --type f
fd -H '^pubspec\.yaml$' packages | xargs -I{} sh -c 'echo "== {}"; rg -n "^name:|^publish_to:|flutter:" {} | head -20'
fd -H 'main.dart' packages/disco --type f

Repository: our-creativity/disco

Length of output: 758


Fix packages/disco/.metadata to match the package layout.

packages/disco is the published package; its Dart app exists at packages/disco/example. This metadata directory should not declare lib/main.dart or ios/Runner.xcodeproj/project.pbxproj, or should be removed/moved to packages/disco/example/.metadata if it is meant to track the example app.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/disco/.metadata` at line 10, Update the metadata configuration for
the published packages/disco package so it does not reference the example app’s
lib/main.dart or ios/Runner.xcodeproj/project.pbxproj paths. If the metadata is
intended for the Dart example app, move it to the example app’s metadata
location instead.

Comment thread packages/disco/example/lib/main.dart Outdated
Comment on lines +132 to +141
/// A provider depending on another provider of the **same** scope. This works
/// only if `loggerProvider` is declared before this provider in the
/// `providers` list, otherwise a `ProviderForwardReferenceError` is thrown.
final analyticsProvider = Provider(
(context) {
final logger = loggerProvider.of(context)..log('Analytics created lazily');
return Analytics(logger);
},
debugName: 'analytics',
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm the error type exists and is exported from the public API.
rg -n 'ForwardReference' packages/disco/lib
rg -n 'class .*Error' packages/disco/lib/src --glob '*.dart'
fd -H 'disco.dart' packages/disco/lib --type f --exec cat {}

Repository: our-creativity/disco

Length of output: 979


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== candidate files mentioning ProviderForwardReferenceError =="
rg -n 'ProviderForwardReferenceError|ForwardReferenceError|ForwardReference' packages || true

echo
echo "== package/lib tree relevant files =="
git ls-files packages/disco/lib | sed -n '1,120p'

echo
echo "== exports with class/type declarations for ProviderForwardReferenceError =="
rg -n 'ProviderForwardReferenceError|class .*Error|type .*Error' packages/disco/lib --glob '*.dart' || true

echo
echo "== analyzer/API symbols availability? =="
if command -v dart >/dev/null 2>&1; then
  dart pub get --offline -C packages/disco 2>/tmp/dart-get.log || true
  grep -n 'ProviderForwardReferenceError' /tmp/dart-get.log || true
else
  echo "dart command not available"
fi

Repository: our-creativity/disco

Length of output: 1958


Replace the removed forward-reference error in the example.

Disco 3.0 removes ProviderForwardReferenceError; forward references are now ordered and cannot be relied upon. Remove this misleading example or document the providers order requirement without naming the removed exception.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/disco/example/lib/main.dart` around lines 132 - 141, Update the
documentation around analyticsProvider to remove the
ProviderForwardReferenceError reference. Either remove this forward-reference
example or describe only the requirement that loggerProvider precede
analyticsProvider in the providers list, without mentioning the removed
exception.

Comment on lines +3 to +5
/// A declarative configuration holding all the data needed to
/// construct and register an mock provider within a
/// [ProviderScope].

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Fix the article in the dartdoc.

Line 4 reads "an mock provider". Use "a mock provider".

📝 Proposed fix
-/// A declarative configuration holding all the data needed to
-/// construct and register an mock provider within a
-/// [ProviderScope].
+/// A declarative configuration holding all the data needed to
+/// construct and register a mock provider within a
+/// [ProviderScope].
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/// A declarative configuration holding all the data needed to
/// construct and register an mock provider within a
/// [ProviderScope].
/// A declarative configuration holding all the data needed to
/// construct and register a mock provider within a
/// [ProviderScope].
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/disco/lib/src/models/override.dart` around lines 3 - 5, Update the
dartdoc description in the override configuration declaration to change “an mock
provider” to “a mock provider,” without altering the surrounding documentation.

Comment on lines +72 to +81
// Override -----------------------------------------------------------------

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

overrideWithValue was removed from both provider types. The PR objectives state that overrideWithValue must remain available and be marked @Deprecated, so that users can migrate gradually. Both provider classes replaced it with overrideWith and deleted the value-based API. Confirm the removal is intentional for the 3.0 major bump, or restore deprecated wrappers at both sites.

  • packages/disco/lib/src/models/providers/provider.dart#L72-L81: add a @Deprecated overrideWithValue(T value) that forwards to overrideWith(Provider<T>((_) => value)).
  • packages/disco/lib/src/models/providers/arg_provider.dart#L34-L38: add a @Deprecated overrideWithValue(T value) that forwards to overrideWith(Provider.withArgument<T, A>((_, _) => value)).
📍 Affects 2 files
  • packages/disco/lib/src/models/providers/provider.dart#L72-L81 (this comment)
  • packages/disco/lib/src/models/providers/arg_provider.dart#L34-L38
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/disco/lib/src/models/providers/provider.dart` around lines 72 - 81,
The removed overrideWithValue API must remain available as deprecated wrappers
for gradual migration. In
packages/disco/lib/src/models/providers/provider.dart:72-81, add deprecated
overrideWithValue(T value) forwarding through overrideWith(Provider<T>((_) =>
value)); in packages/disco/lib/src/models/providers/arg_provider.dart:34-38, add
the equivalent deprecated wrapper forwarding through
overrideWith(Provider.withArgument<T, A>((_, _) => value)).

Comment thread packages/disco/pubspec.yaml Outdated

environment:
sdk: ^3.10.0
flutter: ">=3.38.0"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

Flutter 3.38.0 stable release date

💡 Result:

The Flutter 3.38.0 stable release was officially released on November 12, 2025 [1]. Flutter's official documentation and repository history confirm that this version was part of the stable release cycle [2][3][4]. As of August 9, 2026, this version is no longer the current stable release, having been succeeded by later versions such as Flutter 3.44 [3][5][6].

Citations:


🏁 Script executed:

#!/bin/bash
# Description: Check the declared SDK constraints across the repo for consistency.
fd -t f 'pubspec.yaml' --exec sh -c 'echo "== $1"; sed -n "1,30p" "$1"' _ {}

Repository: our-creativity/disco

Length of output: 4394


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Tracked files under packages/disco:"
git ls-files 'packages/disco/**' | sed -n '1,200p'

echo
echo "Environment constraints in package files:"
rg -n 'environment|flutter| sdk|version:' packages/disco packages/disco/example packages/disco_lint packages/disco_lint/example examples -g 'pubspec.yaml' | sed -n '1,240p'

echo
echo "Imports/dependencies that may indicate API requirements:"
rg -n 'api_key|requireRuntimePermission|permission|permission_handler|permission_handler_android|permission_handler|permission_handler_platform_interface' packages/disco packages/disco_lint examples -g '*.dart' -g 'pubspec.yaml' | sed -n '1,300p'

Repository: our-creativity/disco

Length of output: 5427


Lower or document the Flutter constraint.

packages/disco/pubspec.yaml pins Flutter to ">=3.38.0" while other workspace packages do not declare a Flutter constraint. If this package does not use APIs added in Flutter 3.38.0, lower the bound to keep the package compatible with released Flutter versions.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/disco/pubspec.yaml` at line 14, Update the Flutter SDK constraint in
the pubspec configuration to the lowest released version supported by the
package, unless Disco depends on APIs introduced in Flutter 3.38.0; if that
dependency is intentional, document the specific requirement instead. Preserve
compatibility with the workspace packages’ broader constraints.

Comment on lines +2134 to +2138
await tester.pumpWidget(buildTree(reversed: false));
// A brand new list, holding brand new InstantiableProviders, is built
// every time: only the providers they refer to matter. Since the order
// does not matter either, reordering them is not a change.
await tester.pumpWidget(buildTree(reversed: true));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Update the stale type name in the comment.

The comment says "brand new InstantiableProviders". The commit messages state that InstantiableProvider was renamed to ValueBinding. Use the current name.

📝 Proposed fix
-        // A brand new list, holding brand new InstantiableProviders, is built
+        // A brand new list, holding brand new ValueBindings, is built
         // every time: only the providers they refer to matter. Since the order
         // does not matter either, reordering them is not a change.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
await tester.pumpWidget(buildTree(reversed: false));
// A brand new list, holding brand new InstantiableProviders, is built
// every time: only the providers they refer to matter. Since the order
// does not matter either, reordering them is not a change.
await tester.pumpWidget(buildTree(reversed: true));
await tester.pumpWidget(buildTree(reversed: false));
// A brand new list, holding brand new ValueBindings, is built
// every time: only the providers they refer to matter. Since the order
// does not matter either, reordering them is not a change.
await tester.pumpWidget(buildTree(reversed: true));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/disco/test/disco_test.dart` around lines 2134 - 2138, Update the
comment above the second buildTree call to replace the stale
“InstantiableProviders” type name with the current “ValueBinding” name, without
changing the test behavior.

@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown

Fixes Applied Successfully

Fixed 13 file(s) based on 12 unresolved review comments.

Files modified:

  • docs/src/content/docs/core/immutability.mdx
  • docs/src/content/docs/core/provider-retrieval-process.mdx
  • docs/src/content/docs/core/scoped-di.mdx
  • docs/src/content/docs/core/testing.mdx
  • packages/disco/.metadata
  • packages/disco/CHANGELOG.md
  • packages/disco/benchmark_results.md
  • packages/disco/example/lib/main.dart
  • packages/disco/lib/src/models/override.dart
  • packages/disco/lib/src/models/providers/arg_provider.dart
  • packages/disco/lib/src/models/providers/provider.dart
  • packages/disco/pubspec.yaml
  • packages/disco/test/disco_test.dart

Commit: 77f8ca822908cea9a1aca3a5549911ddccf5b0f4

The changes have been pushed to the fix/allow-whole-provider-override branch.

Time taken: 6m 2s

Fixed 13 file(s) based on 12 unresolved review comments.

Co-authored-by: CodeRabbit <noreply@coderabbit.ai>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Introduces Disco 3.0’s provider-binding API and full-provider overrides, including dynamic argument-provider mocks.

Changes:

  • Adds overrideWith, lazy-only creation, and explicit provider bindings.
  • Adds circular-dependency detection and revised lifecycle handling.
  • Updates tests, examples, benchmarks, and documentation.

Reviewed changes

Copilot reviewed 46 out of 46 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
packages/disco/test/disco_test.dart Expands provider lifecycle and override tests.
packages/disco/README.md Documents provider bindings.
packages/disco/pubspec.yaml Bumps package to 3.0.0.
packages/disco/lib/src/widgets/provider_scope.dart Reworks registration, lookup, creation, and disposal.
packages/disco/lib/src/widgets/provider_scope_override.dart Privatizes override-scope state access.
packages/disco/lib/src/utils/extensions.dart Makes debug names non-nullable.
packages/disco/lib/src/utils/disco_config.dart Removes global lazy configuration.
packages/disco/lib/src/models/value_binding.dart Adds provider binding models.
packages/disco/lib/src/models/providers/provider.dart Adds bindings and provider overrides.
packages/disco/lib/src/models/providers/instantiable_provider.dart Removes the previous binding abstraction.
packages/disco/lib/src/models/providers/arg_provider.dart Adds argument-provider overrides and bindings.
packages/disco/lib/src/models/overrides/provider_override.dart Removes the old value override model.
packages/disco/lib/src/models/overrides/provider_argument_override.dart Removes the old argument override model.
packages/disco/lib/src/models/overrides/override.dart Removes the old override base class.
packages/disco/lib/src/models/override.dart Consolidates provider override models.
packages/disco/lib/src/disco_internal.dart Updates library parts.
packages/disco/example/test/disco_test.dart Migrates example override tests.
packages/disco/example/README.md Documents example features.
packages/disco/example/lib/main.dart Expands the lifecycle demonstration app.
packages/disco/example/analysis_options.yaml Adjusts example analysis settings.
packages/disco/CHANGELOG.md Records the 3.0.0 changes.
packages/disco/benchmark/provider_benchmark.dart Updates benchmarks for lazy bindings.
packages/disco/benchmark_results.md Adds current benchmark results.
packages/disco/.metadata Adds Flutter project metadata.
packages/disco/.gitignore Adds package-level exclusions.
examples/solidart/test/widget_test.dart Migrates Solidart overrides.
examples/solidart/lib/widgets/todos_body.dart Migrates provider registration.
examples/solidart/lib/pages/todos.dart Migrates provider registration.
examples/bloc/lib/main.dart Migrates provider registration.
examples/auto_route/lib/pages/books.dart Migrates provider registration.
docs/src/content/docs/miscellaneous/reactivity.md Updates registration syntax.
docs/src/content/docs/miscellaneous/comparison-with-alternatives.mdx Updates registration syntax.
docs/src/content/docs/installing.md Updates SDK and package versions.
docs/src/content/docs/index.mdx Updates introductory syntax.
docs/src/content/docs/examples/solidart.mdx Updates the Solidart guide.
docs/src/content/docs/examples/bloc.mdx Updates the BLoC guide.
docs/src/content/docs/examples/basic.mdx Updates the basic guide.
docs/src/content/docs/examples/auto-route.mdx Updates the AutoRoute guide.
docs/src/content/docs/core/testing.mdx Documents full-provider overrides.
docs/src/content/docs/core/scoped-di.mdx Documents dependency and disposal behavior.
docs/src/content/docs/core/providers.mdx Documents lazy creation and overrides.
docs/src/content/docs/core/provider-retrieval-process.mdx Explains the revised lookup model.
docs/src/content/docs/core/modals.mdx Updates registration syntax.
docs/src/content/docs/core/immutability.mdx Documents fixed provider sets.
docs/src/content/docs/core/configuration.md Removes obsolete lazy configuration.
docs/astro.config.mjs Removes the configuration page from navigation.

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +515 to +519
final cycleStart = _idsBeingCreated.indexOf(id);
if (cycleStart >= 0) {
throw ProviderCircularDependencyError([
..._idsBeingCreated.skip(cycleStart),
id,
Comment on lines +31 to +32
- **CHORE**: The bookkeeping of `ProviderScopeState` (its maps of providers and values, and its lookup and creation methods) and `ProviderScopeOverrideState.providerScopeState` are now library-private.
- **CHORE**: Rename `InstantiableProvider` to `ValueBinding`.
Comment on lines +264 to 270
for (final item in scope._overrides!) {
if (item is ProviderOverride) {
ids.add(item._originalProvider);
} else if (item is ArgProviderOverride) {
ids.add(item._originalArgProvider);
}
}

### Disposal order

Because the values are created lazily, a value is always created *after* the values it depends on. When a `ProviderScope` is disposed, its values are therefore disposed in the reverse order of creation, so that a value is always disposed *before* its own dependencies. This means that the `dispose` of a provider can safely use the values it injected in its `create`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants