diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index 666296cf..20065d84 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -3,6 +3,8 @@ name: Bug report
 about: There is a problem in how provider behaves
 title: ""
 labels: bug, needs triage
+assignees:
+  - rrousselGit
 ---
 
 **Describe the bug**
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index c5059cc0..b9479133 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -1,5 +1,5 @@
 blank_issues_enabled: false
 contact_links:
   - name: I have a problem and I need help
-    url: https://stackoverflow.com/questions/tagged/flutter
-    about: Please ask and answer questions here.
\ No newline at end of file
+    url: https://github.com/rrousselGit/provider/discussions
+    about: Please ask and answer questions here.
diff --git a/.github/ISSUE_TEMPLATE/example_request.md b/.github/ISSUE_TEMPLATE/example_request.md
new file mode 100644
index 00000000..332337ba
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/example_request.md
@@ -0,0 +1,20 @@
+---
+name: Documentation improvement request
+about: >-
+  Suggest a new example/documentation or ask for clarification about an
+  existing one.
+title: ""
+labels: documentation, needs triage
+assignees:
+  - rrousselGit
+---
+
+**Describe what scenario you think is uncovered by the existing examples/articles**
+A clear and concise description of the problem that you want explained.
+
+**Describe why existing examples/articles do not cover this case**
+Explain which examples/articles you have seen before making this request, and
+why they did not help you with your problem.
+
+**Additional context**
+Add any other context or screenshots about the documentation request here.
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
index b6157e79..65c5ae35 100644
--- a/.github/ISSUE_TEMPLATE/feature_request.md
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -3,6 +3,8 @@ name: Feature request
 about: Suggest an idea for this project
 title: ""
 labels: enhancement, needs triage
+assignees:
+  - rrousselGit
 ---
 
 **Is your feature request related to a problem? Please describe.**
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 46238bb9..c50311c0 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -1,8 +1,14 @@
 name: Build
 
 on:
-  push:
   pull_request:
+    paths-ignore:
+      - "**.md"
+  push:
+    branches:
+      - master
+    paths-ignore:
+      - "**.md"
   schedule:
     # runs the CI everyday at 10AM
     - cron: "0 10 * * *"
@@ -10,16 +16,34 @@ on:
 jobs:
   flutter:
     runs-on: ubuntu-latest
-    container: cirrusci/flutter:${{matrix.channel}}
+
+    defaults:
+      run:
+        working-directory: packages/provider
 
     strategy:
       matrix:
         channel:
-          - dev
-          - beta
+          - master
 
     steps:
-      - uses: actions/checkout@v2
+      - uses: actions/checkout@v3.1.0
+
+      - uses: subosito/flutter-action@v2.7.1
+        with:
+          channel: ${{ matrix.channel }}
+
+      - name: Add pub cache bin to PATH
+        run: echo "$HOME/.pub-cache/bin" >> $GITHUB_PATH
+      - name: Add pub cache to PATH
+        run: echo "PUB_CACHE="$HOME/.pub-cache"" >> $GITHUB_ENV
+
+      - name: Install dependencies
+        run: flutter pub get
+
+      - run: dart format lib test --set-exit-if-changed
+        if: matrix.channel == 'master'
+
+      - run: flutter analyze --no-current-package
 
-      - name: Execute test script
-        run: curl -s https://raw.githubusercontent.com/rrousselGit/ci/master/scripts/ci.sh | bash -s nnbd
+      - run: flutter test --no-pub --coverage test/null_safe
diff --git a/.github/workflows/project.yml b/.github/workflows/project.yml
new file mode 100644
index 00000000..902ec6dc
--- /dev/null
+++ b/.github/workflows/project.yml
@@ -0,0 +1,21 @@
+name: Add new issues to project
+
+on:
+  issues:
+    types:
+      - opened
+      - reopened
+  pull_request:
+    types:
+      - opened
+      - reopened
+
+jobs:
+  add-to-project:
+    name: Add issue to project
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/add-to-project@v0.5.0
+        with:
+          project-url: https://github.com/users/rrousselGit/projects/8
+          github-token: ${{ secrets.ADD_TO_PROJECT_PAT }}
diff --git a/.gitignore b/.gitignore
index f568fbec..b5e820eb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,12 +4,13 @@ android/
 ios/
 macos/
 .packages
+build
 
 # Remove the following pattern if you wish to check in your lock file
 pubspec.lock
 
 # Conventional directory for build outputs
-build/
+/build/
 coverage/
 
 # Directory created by dartdoc
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 00000000..f938df15
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,74 @@
+{
+    // Use IntelliSense to learn about possible attributes.
+    // Hover to view descriptions of existing attributes.
+    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+    "version": "0.2.0",
+    "configurations": [
+        {
+            "name": "provider",
+            "request": "launch",
+            "type": "dart"
+        },
+        {
+            "name": "provider (profile mode)",
+            "request": "launch",
+            "type": "dart",
+            "flutterMode": "profile"
+        },
+        {
+            "name": "provider (release mode)",
+            "request": "launch",
+            "type": "dart",
+            "flutterMode": "release"
+        },
+        {
+            "name": "example",
+            "cwd": "example",
+            "request": "launch",
+            "type": "dart"
+        },
+        {
+            "name": "example (profile mode)",
+            "cwd": "example",
+            "request": "launch",
+            "type": "dart",
+            "flutterMode": "profile"
+        },
+        {
+            "name": "example (release mode)",
+            "cwd": "example",
+            "request": "launch",
+            "type": "dart",
+            "flutterMode": "release"
+        },
+        {
+            "name": "provider_devtools_extension",
+            "cwd": "packages/provider_devtools_extension",
+            "request": "launch",
+            "type": "dart"
+        },
+        {
+            "name": "provider_devtools_extension + simulated environment",
+            "program": "packages/provider_devtools_extension/lib/main.dart",
+            "request": "launch",
+            "type": "dart",
+            "args": [
+                "--dart-define=use_simulated_environment=true"
+            ],
+        },
+        {
+            "name": "provider_devtools_extension (profile mode)",
+            "cwd": "packages/provider_devtools_extension",
+            "request": "launch",
+            "type": "dart",
+            "flutterMode": "profile"
+        },
+        {
+            "name": "provider_devtools_extension (release mode)",
+            "cwd": "packages/provider_devtools_extension",
+            "request": "launch",
+            "type": "dart",
+            "flutterMode": "release"
+        }
+    ]
+}
\ No newline at end of file
diff --git a/analysis_options.yaml b/analysis_options.yaml
index ce207534..07d178a6 100644
--- a/analysis_options.yaml
+++ b/analysis_options.yaml
@@ -2,9 +2,10 @@ include: all_lint_rules.yaml
 analyzer:
   exclude:
     - "**/*.g.dart"
-  strong-mode:
-    implicit-casts: false
-    implicit-dynamic: false
+  language:
+    strict-casts: true
+    strict-inference: true
+    strict-raw-types: true
   errors:
     # Otherwise cause the import of all_lint_rules to warn because of some rules conflicts.
     # We explicitly enabled even conflicting rules and are fixing the conflict
diff --git a/build_devtool.sh b/build_devtool.sh
new file mode 100755
index 00000000..13eda0af
--- /dev/null
+++ b/build_devtool.sh
@@ -0,0 +1,15 @@
+pushd packages/provider
+
+rm -rf extension/devtools/build
+mkdir extension/devtools/build
+
+popd
+
+pushd packages/provider_devtools_extension
+
+flutter pub get &&
+dart run devtools_extensions build_and_copy \
+  --source=. \
+  --dest=../provider/extension/devtools
+
+popd
diff --git a/example/.metadata b/example/.metadata
deleted file mode 100644
index 170cb555..00000000
--- a/example/.metadata
+++ /dev/null
@@ -1,10 +0,0 @@
-# This file tracks properties of this Flutter project.
-# Used by Flutter tool to assess capabilities and perform upgrades etc.
-#
-# This file should be version controlled and should not be manually edited.
-
-version:
-  revision: b593f5167bce84fb3cad5c258477bf3abc1b14eb
-  channel: unknown
-
-project_type: app
diff --git a/packages/provider/.gitignore b/packages/provider/.gitignore
new file mode 100644
index 00000000..23648f3d
--- /dev/null
+++ b/packages/provider/.gitignore
@@ -0,0 +1 @@
+extension/devtools/build
\ No newline at end of file
diff --git a/CHANGELOG.md b/packages/provider/CHANGELOG.md
similarity index 93%
rename from CHANGELOG.md
rename to packages/provider/CHANGELOG.md
index 810f7832..13587b07 100644
--- a/CHANGELOG.md
+++ b/packages/provider/CHANGELOG.md
@@ -1,24 +1,50 @@
-# 6.0.4
+## 6.1.2 - 2024-02-28
+
+- Fixed an issue with `Selector` not rebuilding when the `builder` listens
+  to InheritedWidgets (thanks to @spkersten)
+
+## 6.1.1
+
+- Fix missing devtool assets
+
+## 6.1.0
+
+- The package now comes with a devtool extension enabled.
+
+## 6.1.0-dev.1
+
+- Fix missing folder in devtool extension
+
+## 6.1.0-dev.0
+
+- Moved the implementation of the devtool extension
+  in the source of Provider (thanks to @kenzieschmoll)
+
+## 6.0.5
+
+- Fix broken links on pub.dev
+
+## 6.0.4
 
 Fix typos and broken links in the documentation
 
-# 6.0.3
+## 6.0.3
 
 - fix late initialization error when using `debugPrintRebuildDirtyWidgets`
 - slightly reduced the binary size of release mode applications using provider
 - Fix typos in the error message of ProviderNotFoundException
 - improve performances for reading providers (thanks to @jiahaog)
 
-# 6.0.2
+## 6.0.2
 
 Added error details for provider that threw during the creation (thanks to @jmewes)
 
-# 6.0.1
+## 6.0.1
 
 Removed the assert that prevented from using `ChangeNotifierProvider`
 with notifiers that already had listeners.
 
-# 6.0.0
+## 6.0.0
 
 - **Breaking**: It is no longer possible to define providers where their
   only difference is the nullability of the exposed value:
@@ -65,7 +91,7 @@ with notifiers that already had listeners.
   which will try to obtain a matching provider. But if none are found,
   `null` will be returned instead of throwing.
 
-# 6.0.0-dev
+## 6.0.0-dev
 
 - **Breaking**: It is no longer possible to define providers where their
   only difference is the nullability of the exposed value:
@@ -112,21 +138,21 @@ with notifiers that already had listeners.
   which will try to obtain a matching provider. But if none are found,
   `null` will be returned instead of throwing.
 
-# 5.0.0
+## 5.0.0
 
 - Stable, null-safe release.
 - pre-release of the mechanism for state-inspection using the Flutter devtool
 - Updated oudated doc in `StreamProvider`
 
-# 5.0.0-nullsafety.5
+## 5.0.0-nullsafety.5
 
 Fixed an issue where providers with an `update` parameter in sound null-safety mode could throw null exceptions.
 
-# 5.0.0-nullsafety.4
+## 5.0.0-nullsafety.4
 
 - Upgraded `nested` dependency to 1.0.0 and `collection` to 1.15.0
 
-# 5.0.0-nullsafety.3
+## 5.0.0-nullsafety.3
 
 - Improved the error message of `ProviderNotFoundException` to mention hot-reload. (#595)
 - Removed the asserts that prevented `ChangeNotifier`s in `ChangeNotifierProvider()`
@@ -134,15 +160,15 @@ Fixed an issue where providers with an `update` parameter in sound null-safety m
 - Removed the opinionated asserts in `context.watch`/`context.read`
   that prevented them to be used inside specific conditions (#585)
 
-# 5.0.0-nullsafety.2
+## 5.0.0-nullsafety.2
 
 - Improved the error message when an exception is thrown inside `create` of a provider`
 
-# 5.0.0-nullsafety.1
+## 5.0.0-nullsafety.1
 
 - Reintroduced `ValueListenableProvider.value` (the default constructor is still removed).
 
-# 5.0.0-nullsafety.0
+## 5.0.0-nullsafety.0
 
 Migrated Provider to non-nullable types:
 
@@ -194,7 +220,7 @@ Migrated Provider to non-nullable types:
   )
   ```
 
-# 4.3.3
+## 4.3.3
 
 - Improved the error message of `ProviderNotFoundException` to mention hot-reload. (#595)
 - Removed the asserts that prevented `ChangeNotifier`s in `ChangeNotifierProvider()`
@@ -202,37 +228,37 @@ Migrated Provider to non-nullable types:
 - Removed the opinionated asserts in `context.watch`/`context.read`
   that prevented them to be used inside specific conditions (#585)
 
-# 4.3.2+4
+## 4.3.2+4
 
 `ValueListenableProvider` is no-longer deprecated.
 Only its default constructor is deprecated (the `.value` constructor is kept)
 
-# 4.3.2+3
+## 4.3.2+3
 
 Marked `ValueListenableProvider` as deprecated
 
-# 4.3.2+2
+## 4.3.2+2
 
 Improve pub score
 
-# 4.3.2+1
+## 4.3.2+1
 
 Documentation improvement about the `builder` parameter of Providers.
 
-# 4.3.2
+## 4.3.2
 
 Fixed typo in the error message of `ProviderNotFoundException`
 
-# 4.3.1
+## 4.3.1
 
 - Fixed a bug where hot-reload forced all lazy-loaded providers to be computed.
 
-# 4.3.0
+## 4.3.0
 
 - Added `ReassembleHandler` interface, for objects to implement so that
   `provider` let them handle hot-reload.
 
-# 4.2.0
+## 4.2.0
 
 - Added a `builder` parameter on `MultiProvider` (thanks to @joaomarcos96):
 
@@ -248,11 +274,11 @@ Fixed typo in the error message of `ProviderNotFoundException`
   );
   ```
 
-# 4.1.3+1
+## 4.1.3+1
 
 - Small Readme changes
 
-# 4.1.3
+## 4.1.3
 
 - Improved the error message of `ProviderNotFoundException` with instructions
   that better fit what is usually the problem.
@@ -271,16 +297,16 @@ Fixed typo in the error message of `ProviderNotFoundException`
 - Improve the error message when calling `context.read/watch/select`/`Provider.of` with
   a `context` that is `null`.
 
-# 4.1.2
+## 4.1.2
 
 - Loosened the constraint on Flutter's version to be compatible with `beta` channel.
 
-# 4.1.1
+## 4.1.1
 
 - Fixes an "aspect" leak with `context.select`, leading to memory leaks and unnecessary rebuilds
 - Fixes the `builder` parameter of providers not working (thanks to @passsy)
 
-# 4.1.0
+## 4.1.0
 
 - Now requires:
   - Flutter >= 1.6.0
@@ -352,41 +378,41 @@ Fixed typo in the error message of `ProviderNotFoundException`
 - Added a `Locator` typedef and an extension on [BuildContext], to help with
   being able to read providers from a class that doesn't depend on Flutter.
 
-# 4.0.5+1
+## 4.0.5+1
 
 - Added Português translation of the readme file (thanks to @robsonsilv4)
 
-# 4.0.5
+## 4.0.5
 
 - Improve error message when forgetting to pass a `child` when using a provider outside of `MultiProvider` (thanks to @felangel)
 
-# 4.0.4
+## 4.0.4
 
 - Update the ProviderNotFoundException to remove outdated solution. (thanks @augustinreille)
 
-# 4.0.3
+## 4.0.3
 
 - improved error message when `Provider.of` is called without specifying
   `listen: false` outside of the widget tree.
 
-# 4.0.2
+## 4.0.2
 
 - fix `Provider.of` returning the previous value instead of the new value
   if called inside `didChangeDependencies`.
 - fixed an issue where `update` was unnecessarily called.
 
-# 4.0.1
+## 4.0.1
 
 - stable release of 4.0.0-hotfix+1
 - fix some typos
 
-# 4.0.0-hotfix.1
+## 4.0.0-hotfix.1
 
 - removed the inference of the `listen` flag of `Provider.of` in favor of an exception in debug mode if `listen` is true when it shouldn't.
 
   This is because it caused a critical performance issue. See https://github.com/rrousselGit/provider/issues/305
 
-# 4.0.0
+## 4.0.0
 
 - `Selector` now deeply compares collections by default, and offers a `shouldRebuild`
   to customize the rebuild behavior.
@@ -404,13 +430,13 @@ Fixed typo in the error message of `ProviderNotFoundException`
 - renamed `builder` of `*Provider` to `create`
 - added a `*ProxyProvider0` variant
 
-# 3.2.0
+## 3.2.0
 
 - Deprecated "builder" of providers in favor to "create"
 - Deprecated "initialBuilder"/"builder" of proxy providers in favor of respectively
   "create" and "update"
 
-# 3.1.0
+## 3.1.0
 
 - Added `Selector`, similar to `Consumer` but can filter unneeded updates
 - improved the overall documentation
@@ -430,9 +456,9 @@ Fixed typo in the error message of `ProviderNotFoundException`
   );
   ```
 
-# 3.0.0
+## 3.0.0
 
-## breaking (see the readme for migration steps)
+### breaking (see the readme for migration steps)
 
 - `Provider` now throws if used with a `Listenable`/`Stream`. This can be disabled by setting
   `Provider.debugCheckInvalidValueType` to `null`.
@@ -442,7 +468,7 @@ Fixed typo in the error message of `ProviderNotFoundException`
 - Added `FutureProvider`, which takes a future and updates dependents when the future completes.
 - Providers can no longer be instantiated using `const` constructors.
 
-## non-breaking
+### non-breaking
 
 - Added `ProxyProvider`, `ListenableProxyProvider`, and `ChangeNotifierProxyProvider`.
   These providers allows building values that depends on other providers,
@@ -450,7 +476,7 @@ Fixed typo in the error message of `ProviderNotFoundException`
 - Added `DelegateWidget` and a few related classes to help building custom providers.
 - Exposed the internal generic `InheritedWidget` to help building custom providers.
 
-# 2.0.1
+## 2.0.1
 
 - fix a bug where `ListenableProvider.value`/`ChangeNotifierProvider.value`
   /`StreamProvider.value`/`ValueListenableProvider.value` subscribed/unsubscribed
@@ -458,7 +484,7 @@ Fixed typo in the error message of `ProviderNotFoundException`
 - fix a bug where `ListenableProvider.value`/`ChangeNotifierProvider.value` may
   rebuild too often or skip some.
 
-# 2.0.0
+## 2.0.0
 
 - `Consumer` now takes an optional `child` argument for optimization purposes.
 - merged `Provider` and `StatefulProvider`
@@ -466,46 +492,46 @@ Fixed typo in the error message of `ProviderNotFoundException`
 - normalized providers constructors such that the default constructor is a "builder",
   and offer a `value` named constructor.
 
-# 1.6.1
+## 1.6.1
 
 - `Provider.of<T>` now crashes with a `ProviderNotFoundException` when no `Provider<T>`
   are found in the ancestors of the context used.
 
-# 1.6.0
+## 1.6.0
 
 - new: `ChangeNotifierProvider`, similar to scoped_model that exposes `ChangeNotifer` subclass and
   rebuilds dependents only when `notifyListeners` is called.
 - new: `ValueListenableProvider`, a provider that rebuilds whenever the value passed
   to a `ValueNotifier` change.
 
-# 1.5.0
+## 1.5.0
 
 - new: Add `Consumer` with up to 6 parameters.
 - new: `MultiProvider`, a provider that makes a tree of provider more readable
 - new: `StreamProvider`, a stream that exposes to its descendants the current value of a `Stream`.
 
-# 1.4.0
+## 1.4.0
 
 - Reintroduced `StatefulProvider` with a modified prototype.
   The second argument of `valueBuilder` and `didChangeDependencies` have been removed.
   And `valueBuilder` is now called only once for the whole life-cycle of `StatefulProvider`.
 
-# 1.3.0
+## 1.3.0
 
 - Added `Consumer`, useful when we need to both expose and consume a value simultaneously.
 
-# 1.2.0
+## 1.2.0
 
 - Added: `HookProvider`, a `Provider` that creates its value from a `Hook`.
 - Deprecated `StatefulProvider`. Either make a `StatefulWidget` or use `HookProvider`.
 - Integrated the widget inspector, so that `Provider` widget shows the current value.
 
-# 1.1.1
+## 1.1.1
 
 - add `didChangeDependencies` callback to allow updating the value based on an `InheritedWidget`
 - add `updateShouldNotify` method to both `Provider` and `StatefulProvider`
 
-# 1.1.0
+## 1.1.0
 
 - `onDispose` has been added to `StatefulProvider`
 - [BuildContext] is now passed to `valueBuilder` callback
diff --git a/LICENSE b/packages/provider/LICENSE
similarity index 100%
rename from LICENSE
rename to packages/provider/LICENSE
diff --git a/README.md b/packages/provider/README.md
similarity index 95%
rename from README.md
rename to packages/provider/README.md
index f390db7b..053d991e 100644
--- a/README.md
+++ b/packages/provider/README.md
@@ -1,4 +1,4 @@
-[English](https://github.com/rrousselGit/provider/blob/master/README.md) | [French](https://github.com/rrousselGit/provider/blob/master/resources/translations/fr_FR/README.md) | [Português](https://github.com/rrousselGit/provider/blob/master/resources/translations/pt_br/README.md) | [简体中文](https://github.com/rrousselGit/provider/blob/master/resources/translations/zh-CN/README.md) | [Español](https://github.com/rrousselGit/provider/blob/master/resources/translations/es_MX/README.md) | [한국어](https://github.com/rrousselGit/provider/blob/master/resources/translations/ko-KR/README.md) | [বাংলা](/resources/translations/bn_BD/README.md) | [日本語](https://github.com/rrousselGit/provider/blob/master/resources/translations/ja_JP/README.md)
+[English](https://github.com/rrousselGit/provider/blob/master/packages/provider/README.md) | [French](https://github.com/rrousselGit/provider/blob/master/resources/translations/fr_FR/README.md) | [Português](https://github.com/rrousselGit/provider/blob/master/resources/translations/pt_br/README.md) | [简体中文](https://github.com/rrousselGit/provider/blob/master/resources/translations/zh-CN/README.md) | [Español](https://github.com/rrousselGit/provider/blob/master/resources/translations/es_MX/README.md) | [한국어](https://github.com/rrousselGit/provider/blob/master/resources/translations/ko-KR/README.md) | [বাংলা](https://github.com/rrousselGit/provider/blob/master/resources/translations/bn_BD/README.md) | [日本語](https://github.com/rrousselGit/provider/blob/master/resources/translations/ja_JP/README.md) | [Turkish](https://github.com/rrousselGit/provider/blob/master/resources/translations/tr_TR/README.md) | [Italian](https://github.com/rrousselGit/provider/blob/master/resources/translations/it_IT/README.md)
 
 <a href="https://github.com/rrousselGit/provider/actions"><img src="https://github.com/rrousselGit/provider/workflows/Build/badge.svg" alt="Build Status"></a>
 [![codecov](https://codecov.io/gh/rrousselGit/provider/branch/master/graph/badge.svg)](https://codecov.io/gh/rrousselGit/provider) <a href="https://discord.gg/Bbumvej"><img src="https://img.shields.io/discord/765557403865186374.svg?logo=discord&color=blue" alt="Discord"></a>
diff --git a/packages/provider/analysis_options.yaml b/packages/provider/analysis_options.yaml
new file mode 100644
index 00000000..db4f7f6b
--- /dev/null
+++ b/packages/provider/analysis_options.yaml
@@ -0,0 +1,5 @@
+include: ../../analysis_options.yaml
+linter:
+  rules:
+    public_member_api_docs: false
+    lines_longer_than_80_chars: false
\ No newline at end of file
diff --git a/example/.gitignore b/packages/provider/example/.gitignore
similarity index 100%
rename from example/.gitignore
rename to packages/provider/example/.gitignore
diff --git a/packages/provider/example/.metadata b/packages/provider/example/.metadata
new file mode 100644
index 00000000..b6e02ec2
--- /dev/null
+++ b/packages/provider/example/.metadata
@@ -0,0 +1,45 @@
+# This file tracks properties of this Flutter project.
+# Used by Flutter tool to assess capabilities and perform upgrades etc.
+#
+# This file should be version controlled and should not be manually edited.
+
+version:
+  revision: "8bc223e3e5cc919bada837ecc0afdc546969a79e"
+  channel: "master"
+
+project_type: app
+
+# Tracks metadata for the flutter migrate command
+migration:
+  platforms:
+    - platform: root
+      create_revision: 8bc223e3e5cc919bada837ecc0afdc546969a79e
+      base_revision: 8bc223e3e5cc919bada837ecc0afdc546969a79e
+    - platform: android
+      create_revision: 8bc223e3e5cc919bada837ecc0afdc546969a79e
+      base_revision: 8bc223e3e5cc919bada837ecc0afdc546969a79e
+    - platform: ios
+      create_revision: 8bc223e3e5cc919bada837ecc0afdc546969a79e
+      base_revision: 8bc223e3e5cc919bada837ecc0afdc546969a79e
+    - platform: linux
+      create_revision: 8bc223e3e5cc919bada837ecc0afdc546969a79e
+      base_revision: 8bc223e3e5cc919bada837ecc0afdc546969a79e
+    - platform: macos
+      create_revision: 8bc223e3e5cc919bada837ecc0afdc546969a79e
+      base_revision: 8bc223e3e5cc919bada837ecc0afdc546969a79e
+    - platform: web
+      create_revision: 8bc223e3e5cc919bada837ecc0afdc546969a79e
+      base_revision: 8bc223e3e5cc919bada837ecc0afdc546969a79e
+    - platform: windows
+      create_revision: 8bc223e3e5cc919bada837ecc0afdc546969a79e
+      base_revision: 8bc223e3e5cc919bada837ecc0afdc546969a79e
+
+  # User provided section
+
+  # List of Local paths (relative to this file) that should be
+  # ignored by the migrate tool.
+  #
+  # Files that are not part of the templates will be ignored by default.
+  unmanaged_files:
+    - 'lib/main.dart'
+    - 'ios/Runner.xcodeproj/project.pbxproj'
diff --git a/packages/provider/example/devtools_options.yaml b/packages/provider/example/devtools_options.yaml
new file mode 100644
index 00000000..5c27c3e3
--- /dev/null
+++ b/packages/provider/example/devtools_options.yaml
@@ -0,0 +1,2 @@
+extensions:
+  - provider: true
\ No newline at end of file
diff --git a/example/lib/main.dart b/packages/provider/example/lib/main.dart
similarity index 95%
rename from example/lib/main.dart
rename to packages/provider/example/lib/main.dart
index 537d86aa..d9f50d64 100644
--- a/example/lib/main.dart
+++ b/packages/provider/example/lib/main.dart
@@ -1,4 +1,3 @@
-// ignore_for_file: public_member_api_docs, lines_longer_than_80_chars
 import 'package:flutter/foundation.dart';
 import 'package:flutter/material.dart';
 import 'package:provider/provider.dart';
@@ -58,11 +57,11 @@ class MyHomePage extends StatelessWidget {
       appBar: AppBar(
         title: const Text('Example'),
       ),
-      body: Center(
+      body: const Center(
         child: Column(
           mainAxisSize: MainAxisSize.min,
           mainAxisAlignment: MainAxisAlignment.center,
-          children: const <Widget>[
+          children: <Widget>[
             Text('You have pushed the button this many times:'),
 
             /// Extracted as a separate widget for performance optimization.
diff --git a/example/pubspec.yaml b/packages/provider/example/pubspec.yaml
similarity index 100%
rename from example/pubspec.yaml
rename to packages/provider/example/pubspec.yaml
diff --git a/example/test/widget_test.dart b/packages/provider/example/test/widget_test.dart
similarity index 100%
rename from example/test/widget_test.dart
rename to packages/provider/example/test/widget_test.dart
diff --git a/example/test_driver/app.dart b/packages/provider/example/test_driver/app.dart
similarity index 100%
rename from example/test_driver/app.dart
rename to packages/provider/example/test_driver/app.dart
diff --git a/example/test_driver/app_test.dart b/packages/provider/example/test_driver/app_test.dart
similarity index 95%
rename from example/test_driver/app_test.dart
rename to packages/provider/example/test_driver/app_test.dart
index eceed86b..e9250f53 100644
--- a/example/test_driver/app_test.dart
+++ b/packages/provider/example/test_driver/app_test.dart
@@ -1,4 +1,3 @@
-// ignore: import_of_legacy_library_into_null_safe
 import 'package:flutter_driver/flutter_driver.dart';
 import 'package:test/test.dart';
 
diff --git a/packages/provider/extension/devtools/.pubignore b/packages/provider/extension/devtools/.pubignore
new file mode 100644
index 00000000..684ae5d8
--- /dev/null
+++ b/packages/provider/extension/devtools/.pubignore
@@ -0,0 +1 @@
+!build
\ No newline at end of file
diff --git a/packages/provider/extension/devtools/config.yaml b/packages/provider/extension/devtools/config.yaml
new file mode 100644
index 00000000..e0da96ef
--- /dev/null
+++ b/packages/provider/extension/devtools/config.yaml
@@ -0,0 +1,4 @@
+name: provider
+issueTracker: https://github.com/rrousselGit/provider/issues
+version: 0.0.1
+materialIconCodePoint: "0xe0b1"
diff --git a/lib/provider.dart b/packages/provider/lib/provider.dart
similarity index 100%
rename from lib/provider.dart
rename to packages/provider/lib/provider.dart
diff --git a/lib/single_child_widget.dart b/packages/provider/lib/single_child_widget.dart
similarity index 100%
rename from lib/single_child_widget.dart
rename to packages/provider/lib/single_child_widget.dart
diff --git a/lib/src/async_provider.dart b/packages/provider/lib/src/async_provider.dart
similarity index 100%
rename from lib/src/async_provider.dart
rename to packages/provider/lib/src/async_provider.dart
diff --git a/lib/src/change_notifier_provider.dart b/packages/provider/lib/src/change_notifier_provider.dart
similarity index 100%
rename from lib/src/change_notifier_provider.dart
rename to packages/provider/lib/src/change_notifier_provider.dart
diff --git a/lib/src/consumer.dart b/packages/provider/lib/src/consumer.dart
similarity index 100%
rename from lib/src/consumer.dart
rename to packages/provider/lib/src/consumer.dart
diff --git a/lib/src/deferred_inherited_provider.dart b/packages/provider/lib/src/deferred_inherited_provider.dart
similarity index 100%
rename from lib/src/deferred_inherited_provider.dart
rename to packages/provider/lib/src/deferred_inherited_provider.dart
diff --git a/lib/src/devtool.dart b/packages/provider/lib/src/devtool.dart
similarity index 94%
rename from lib/src/devtool.dart
rename to packages/provider/lib/src/devtool.dart
index c50d55b0..d3144900 100644
--- a/lib/src/devtool.dart
+++ b/packages/provider/lib/src/devtool.dart
@@ -60,13 +60,13 @@ class ProviderNode {
     required this.id,
     required this.childrenNodeIds,
     required this.type,
-    required _InheritedProviderScopeElement element,
+    required _InheritedProviderScopeElement<Object?> element,
   }) : _element = element;
 
   final String id;
   final String type;
   final List<String> childrenNodeIds;
-  final _InheritedProviderScopeElement _element;
+  final _InheritedProviderScopeElement<Object?> _element;
 
   Object? get value => _element._delegateState.value;
 }
diff --git a/lib/src/inherited_provider.dart b/packages/provider/lib/src/inherited_provider.dart
similarity index 100%
rename from lib/src/inherited_provider.dart
rename to packages/provider/lib/src/inherited_provider.dart
diff --git a/lib/src/listenable_provider.dart b/packages/provider/lib/src/listenable_provider.dart
similarity index 99%
rename from lib/src/listenable_provider.dart
rename to packages/provider/lib/src/listenable_provider.dart
index c7764258..69ec90e6 100644
--- a/lib/src/listenable_provider.dart
+++ b/packages/provider/lib/src/listenable_provider.dart
@@ -55,7 +55,7 @@ class ListenableProvider<T extends Listenable?> extends InheritedProvider<T> {
         );
 
   static VoidCallback _startListening(
-    InheritedContext e,
+    InheritedContext<Listenable?> e,
     Listenable? value,
   ) {
     value?.addListener(e.markNeedsNotifyDependents);
diff --git a/lib/src/provider.dart b/packages/provider/lib/src/provider.dart
similarity index 99%
rename from lib/src/provider.dart
rename to packages/provider/lib/src/provider.dart
index 35f2b4eb..2ec44fab 100644
--- a/lib/src/provider.dart
+++ b/packages/provider/lib/src/provider.dart
@@ -518,7 +518,7 @@ extension ReadContext on BuildContext {
   /// Obtain a value from the nearest ancestor provider of type [T].
   ///
   /// This method is the opposite of [watch].\
-  /// It will _not_ make widget rebuild when the value changes and cannot be
+  /// It will _not_ make widget rebuild when the value changes and should not be
   /// called inside [StatelessWidget.build]/[State.build].\
   /// On the other hand, it can be freely called _outside_ of these methods.
   ///
@@ -665,9 +665,9 @@ extension WatchContext on BuildContext {
   /// ```dart
   /// runApp(
   ///   Builder(builder: (context) {
-  ///     final value = context.watch<Movie?>();
+  ///     final movie = context.watch<Movie?>();
   ///
-  ///     if (value == null) Text('no Movie found');
+  ///     if (movie == null) Text('no Movie found');
   ///     return Text(movie.title);
   ///   }),
   /// );
diff --git a/lib/src/proxy_provider.dart b/packages/provider/lib/src/proxy_provider.dart
similarity index 100%
rename from lib/src/proxy_provider.dart
rename to packages/provider/lib/src/proxy_provider.dart
diff --git a/lib/src/reassemble_handler.dart b/packages/provider/lib/src/reassemble_handler.dart
similarity index 100%
rename from lib/src/reassemble_handler.dart
rename to packages/provider/lib/src/reassemble_handler.dart
diff --git a/lib/src/selector.dart b/packages/provider/lib/src/selector.dart
similarity index 89%
rename from lib/src/selector.dart
rename to packages/provider/lib/src/selector.dart
index 80f0a1b6..d5e7462f 100644
--- a/lib/src/selector.dart
+++ b/packages/provider/lib/src/selector.dart
@@ -82,10 +82,12 @@ class _Selector0State<T> extends SingleChildState<Selector0<T>> {
     if (shouldInvalidateCache) {
       value = selected;
       oldWidget = widget;
-      cache = widget.builder(
-        context,
-        selected,
-        child,
+      cache = Builder(
+        builder: (context) => widget.builder(
+          context,
+          selected,
+          child,
+        ),
       );
     }
     return cache!;
@@ -119,20 +121,37 @@ class _Selector0State<T> extends SingleChildState<Selector0<T>> {
 /// As such, it `selector` should return either a collection ([List]/[Map]/[Set]/[Iterable])
 /// or a class that override `==`.
 ///
+/// Here's an example:
+///
+/// Example 1:
+///
+///```dart
+/// Selector<Foo, Bar>(
+///   selector: (_, foo) => foo.bar,  // will rebuild only when `bar` changes
+///   builder: (_, data, __) {
+///     return Text('${data.item}');
+///   }
+/// )
+///```
+///In this example `builder` will be called only when `foo.bar` changes.
+///
+/// Example 2:
+///
 /// To select multiple values without having to write a class that implements `==`,
-/// the easiest solution is to use a "Tuple" from [tuple](https://pub.dev/packages/tuple):
+/// the easiest solution is to use "Records," available from Dart version 3.0.
+/// For more information on Records, refer to the [records](https://dart.dev/language/records).
 ///
 /// ```dart
-/// Selector<Foo, Tuple2<Bar, Baz>>(
-///   selector: (_, foo) => Tuple2(foo.bar, foo.baz),
+/// Selector<Foo, ({String item1, String item2})>(
+///   selector: (_, foo) => (item1: foo.item1, item2: foo.item2),
 ///   builder: (_, data, __) {
 ///     return Text('${data.item1}  ${data.item2}');
-///   }
-/// )
+///   },
+/// );
 /// ```
 ///
-/// In that example, `builder` will be called again only if `foo.bar` or
-/// `foo.baz` changes.
+/// In this example, `builder` will be called again only if `foo.item1` or
+/// `foo.item2` changes.
 ///
 /// For generic usage information, see [Consumer].
 /// {@endtemplate}
diff --git a/lib/src/value_listenable_provider.dart b/packages/provider/lib/src/value_listenable_provider.dart
similarity index 100%
rename from lib/src/value_listenable_provider.dart
rename to packages/provider/lib/src/value_listenable_provider.dart
diff --git a/pubspec.yaml b/packages/provider/pubspec.yaml
similarity index 77%
rename from pubspec.yaml
rename to packages/provider/pubspec.yaml
index 67a23adf..1971eaba 100644
--- a/pubspec.yaml
+++ b/packages/provider/pubspec.yaml
@@ -1,11 +1,11 @@
 name: provider
 description: A wrapper around InheritedWidget to make them easier to use and more reusable.
-version: 6.0.4
+version: 6.1.2
 repository: https://github.com/rrousselGit/provider
 issue_tracker: https://github.com/rrousselGit/provider/issues
 
 environment:
-  sdk: ">=2.12.0 <3.0.0"
+  sdk: ">=2.12.0 <4.0.0"
   flutter: ">=1.16.0"
 
 dependencies:
@@ -17,5 +17,7 @@ dependencies:
 dev_dependencies:
   flutter_test:
     sdk: flutter
+  # The version is `any` because it is defined by Flutter SDK.
+  leak_tracker: any
   mockito: ^5.0.0
   test: ^1.15.5
diff --git a/packages/provider/test/flutter_test_config.dart b/packages/provider/test/flutter_test_config.dart
new file mode 100644
index 00000000..091adab1
--- /dev/null
+++ b/packages/provider/test/flutter_test_config.dart
@@ -0,0 +1,27 @@
+import 'dart:async';
+
+import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart';
+
+FutureOr<void> testExecutable(FutureOr<void> Function() testMain) {
+  LeakTesting.enable();
+  LeakTesting.settings = LeakTesting.settings.withIgnored(
+    allNotGCed: true,
+    createdByTestHelpers: true,
+    classes: [
+      'RenderObject',
+      'RenderParagraph',
+      'StatefulElement',
+      '_StatefulTestState',
+      'StatelessElement',
+      'SingleChildRenderObjectElement',
+      '_InheritedProviderScopeElement',
+      '_InheritedProviderScopeElement<String?>',
+      '_InheritedProviderScopeElement<ValueNotifier<int>?>',
+      '_InheritedProviderScopeElement<int?>',
+      'MultiChildRenderObjectElement',
+      'TextPainter',
+    ],
+  );
+
+  return testMain();
+}
diff --git a/test/builder_test.dart b/packages/provider/test/null_safe/builder_test.dart
similarity index 96%
rename from test/builder_test.dart
rename to packages/provider/test/null_safe/builder_test.dart
index c3ae23a3..f3c1c26e 100644
--- a/test/builder_test.dart
+++ b/packages/provider/test/null_safe/builder_test.dart
@@ -22,9 +22,12 @@ void main() {
     });
 
     testWidgets('.value', (tester) async {
+      final notifier = ValueNotifier(0);
+      addTearDown(notifier.dispose);
+
       await tester.pumpWidget(
         ChangeNotifierProvider.value(
-          value: ValueNotifier(0),
+          value: notifier,
           builder: (context, child) {
             context.watch<ValueNotifier<int>>();
             return child!;
@@ -41,7 +44,12 @@ void main() {
     testWidgets('default', (tester) async {
       await tester.pumpWidget(
         ListenableProvider(
-          create: (_) => ValueNotifier(0),
+          create: (_) {
+            final valueNotifier = ValueNotifier(0);
+            addTearDown(valueNotifier.dispose);
+
+            return valueNotifier;
+          },
           builder: (context, child) {
             context.watch<ValueNotifier<int>>();
             return child!;
@@ -54,9 +62,12 @@ void main() {
     });
 
     testWidgets('.value', (tester) async {
+      final valueNotifier = ValueNotifier(0);
+      addTearDown(valueNotifier.dispose);
+
       await tester.pumpWidget(
         ListenableProvider.value(
-          value: ValueNotifier(0),
+          value: valueNotifier,
           builder: (context, child) {
             context.watch<ValueNotifier<int>>();
             return child!;
@@ -326,7 +337,11 @@ void main() {
         MultiProvider(
           providers: [
             ListenableProvider(
-              create: (_) => ValueNotifier(0),
+              create: (_) {
+                final notifier = ValueNotifier(0);
+                addTearDown(notifier.dispose);
+                return notifier;
+              },
             ),
           ],
           builder: (context, child) {
diff --git a/test/change_notifier_provider_test.dart b/packages/provider/test/null_safe/change_notifier_provider_test.dart
similarity index 99%
rename from test/change_notifier_provider_test.dart
rename to packages/provider/test/null_safe/change_notifier_provider_test.dart
index 060fe099..4745b265 100644
--- a/test/change_notifier_provider_test.dart
+++ b/packages/provider/test/null_safe/change_notifier_provider_test.dart
@@ -8,6 +8,7 @@ void main() {
   group('ChangeNotifierProvider', () {
     testWidgets('value', (tester) async {
       final myNotifier = ValueNotifier<int>(0);
+      addTearDown(myNotifier.dispose);
 
       await tester.pumpWidget(
         MultiProvider(
diff --git a/test/common.dart b/packages/provider/test/null_safe/common.dart
similarity index 98%
rename from test/common.dart
rename to packages/provider/test/null_safe/common.dart
index 79fb621b..5e35249d 100644
--- a/test/common.dart
+++ b/packages/provider/test/null_safe/common.dart
@@ -3,7 +3,6 @@ import 'dart:async';
 import 'package:flutter/foundation.dart';
 import 'package:flutter/widgets.dart';
 import 'package:flutter_test/flutter_test.dart';
-// ignore: import_of_legacy_library_into_null_safe
 import 'package:mockito/mockito.dart';
 import 'package:provider/provider.dart';
 
@@ -27,7 +26,7 @@ Type typeOf<T>() => T;
 ///
 /// For use in legacy tests: they can't instantiate a `Provider<T?>` directly
 /// because they can't write `<T?>`. But, they can pass around a `Provider<T?`>.
-Provider<T?> nullableProviderOfValue<T>(T value, Provider? child) =>
+Provider<T?> nullableProviderOfValue<T>(T value, Provider<dynamic>? child) =>
     Provider<T?>.value(
       value: value,
       child: child,
@@ -36,7 +35,7 @@ Provider<T?> nullableProviderOfValue<T>(T value, Provider? child) =>
 /// Given `T`, returns a `Provider<T>`.
 ///
 /// For legacy tests to get a `Provider<T>`.
-Provider<T> nullSafeProviderOfValue<T>(T value, Provider? child) =>
+Provider<T> nullSafeProviderOfValue<T>(T value, Provider<dynamic>? child) =>
     Provider<T>.value(
       value: value,
       child: child,
@@ -249,8 +248,7 @@ class DeferredStartListeningMock<T, R> extends Mock {
       void Function(R value) setState,
       T controller,
       R? value,
-    )?
-        call,
+    )? call,
   ]) {
     if (call != null) {
       when(this(any, any, any, any)).thenAnswer((invoc) {
diff --git a/test/consumer_test.dart b/packages/provider/test/null_safe/consumer_test.dart
similarity index 99%
rename from test/consumer_test.dart
rename to packages/provider/test/null_safe/consumer_test.dart
index 20fbfb83..de04ffde 100644
--- a/test/consumer_test.dart
+++ b/packages/provider/test/null_safe/consumer_test.dart
@@ -1,6 +1,5 @@
 import 'package:flutter/material.dart';
 import 'package:flutter_test/flutter_test.dart';
-// ignore: import_of_legacy_library_into_null_safe
 import 'package:mockito/mockito.dart';
 import 'package:provider/provider.dart';
 
diff --git a/test/context_test.dart b/packages/provider/test/null_safe/context_test.dart
similarity index 97%
rename from test/context_test.dart
rename to packages/provider/test/null_safe/context_test.dart
index 44316a66..f922cb89 100644
--- a/test/context_test.dart
+++ b/packages/provider/test/null_safe/context_test.dart
@@ -1,6 +1,5 @@
 import 'package:flutter/widgets.dart';
 import 'package:flutter_test/flutter_test.dart';
-// ignore: import_of_legacy_library_into_null_safe
 import 'package:mockito/mockito.dart';
 import 'package:provider/provider.dart';
 
@@ -10,6 +9,7 @@ void main() {
   group('context.watch<T?>', () {
     testWidgets('can watch T', (tester) async {
       final notifier = ValueNotifier(0);
+      addTearDown(notifier.dispose);
 
       await tester.pumpWidget(
         ChangeNotifierProvider<ValueNotifier<int>>.value(
@@ -37,6 +37,7 @@ void main() {
 
     testWidgets('can watch T?', (tester) async {
       final notifier = ValueNotifier(0);
+      addTearDown(notifier.dispose);
 
       await tester.pumpWidget(
         ChangeNotifierProvider<ValueNotifier<int>?>.value(
@@ -99,6 +100,8 @@ void main() {
       expect(find.text(''), findsOneWidget);
 
       final notifier = ValueNotifier(0);
+      addTearDown(notifier.dispose);
+
       await tester.pumpWidget(
         ChangeNotifierProvider<ValueNotifier<int>?>.value(
           value: notifier,
@@ -118,6 +121,7 @@ void main() {
   group('context.watch<T>', () {
     testWidgets('can watch T?', (tester) async {
       final notifier = ValueNotifier(0);
+      addTearDown(notifier.dispose);
 
       await tester.pumpWidget(
         ChangeNotifierProvider<ValueNotifier<int>?>.value(
@@ -176,6 +180,7 @@ void main() {
   group('context.select<T?>', () {
     testWidgets('can watch T', (tester) async {
       final notifier = ValueNotifier(0);
+      addTearDown(notifier.dispose);
 
       await tester.pumpWidget(
         ChangeNotifierProvider<ValueNotifier<int>>.value(
@@ -204,6 +209,7 @@ void main() {
 
     testWidgets('can watch T?', (tester) async {
       final notifier = ValueNotifier(0);
+      addTearDown(notifier.dispose);
 
       await tester.pumpWidget(
         ChangeNotifierProvider<ValueNotifier<int>?>.value(
@@ -269,6 +275,8 @@ void main() {
       expect(find.text(''), findsOneWidget);
 
       final notifier = ValueNotifier(0);
+      addTearDown(notifier.dispose);
+
       await tester.pumpWidget(
         ChangeNotifierProvider<ValueNotifier<int>?>.value(
           value: notifier,
@@ -288,6 +296,7 @@ void main() {
   group('context.select<T>', () {
     testWidgets('can watch T?', (tester) async {
       final notifier = ValueNotifier(0);
+      addTearDown(notifier.dispose);
 
       await tester.pumpWidget(
         ChangeNotifierProvider<ValueNotifier<int>?>.value(
@@ -316,6 +325,7 @@ void main() {
 
     testWidgets('can watch T', (tester) async {
       final notifier = ValueNotifier(0);
+      addTearDown(notifier.dispose);
 
       await tester.pumpWidget(
         ChangeNotifierProvider<ValueNotifier<int>>.value(
@@ -387,9 +397,12 @@ void main() {
         },
       );
 
+      final notifier = ValueNotifier(0);
+      addTearDown(notifier.dispose);
+
       await tester.pumpWidget(
         ChangeNotifierProvider<ValueNotifier<int>?>.value(
-          value: ValueNotifier(0),
+          value: notifier,
           child: child,
         ),
       );
@@ -692,10 +705,13 @@ void main() {
     testWidgets("don't call old selectors if the child rebuilds individually",
         (tester) async {
       final notifier = ValueNotifier(0);
+      addTearDown(notifier.dispose);
 
       var buildCount = 0;
+      final selectedNotifier = ValueNotifier(0);
+      addTearDown(selectedNotifier.dispose);
       final selector =
-          MockSelector.identity<ValueNotifier<int>>(ValueNotifier(0));
+          MockSelector.identity<ValueNotifier<int>>(selectedNotifier);
       final child = Builder(builder: (c) {
         buildCount++;
         c.select<ValueNotifier<int>, ValueNotifier<int>>(selector);
@@ -1058,6 +1074,7 @@ void main() {
 
     testWidgets('context.select deeply compares maps', (tester) async {
       final notifier = ValueNotifier(<int, int>{});
+      addTearDown(notifier.dispose);
 
       var buildCount = 0;
       final selector = MockSelector.identity<Map<int, int>>({});
@@ -1101,6 +1118,7 @@ void main() {
 
     testWidgets('context.select deeply compares lists', (tester) async {
       final notifier = ValueNotifier(<int>[]);
+      addTearDown(notifier.dispose);
 
       var buildCount = 0;
       final selector = MockSelector.identity<List<int>>([]);
@@ -1143,6 +1161,7 @@ void main() {
 
     testWidgets('context.select deeply compares iterables', (tester) async {
       final notifier = ValueNotifier<Iterable<int>>(<int>[]);
+      addTearDown(notifier.dispose);
 
       var buildCount = 0;
       final selector = MockSelector.identity<Iterable<int>>({});
@@ -1185,6 +1204,7 @@ void main() {
 
     testWidgets('context.select deeply compares sets', (tester) async {
       final notifier = ValueNotifier<Set<int>>(<int>{});
+      addTearDown(notifier.dispose);
 
       var buildCount = 0;
       final selector = MockSelector.identity<Set<int>>({});
diff --git a/test/devtool_test.dart b/packages/provider/test/null_safe/devtool_test.dart
similarity index 99%
rename from test/devtool_test.dart
rename to packages/provider/test/null_safe/devtool_test.dart
index 6c5493ea..2338c724 100644
--- a/test/devtool_test.dart
+++ b/packages/provider/test/null_safe/devtool_test.dart
@@ -16,6 +16,7 @@ void main() {
 
   testWidgets('calls postEvent whenever a provider is updated', (tester) async {
     final notifier = ValueNotifier(42);
+    addTearDown(notifier.dispose);
 
     await tester.pumpWidget(
       MultiProvider(
diff --git a/test/future_provider_test.dart b/packages/provider/test/null_safe/future_provider_test.dart
similarity index 99%
rename from test/future_provider_test.dart
rename to packages/provider/test/null_safe/future_provider_test.dart
index 3f3beebd..ee00e112 100644
--- a/test/future_provider_test.dart
+++ b/packages/provider/test/null_safe/future_provider_test.dart
@@ -2,7 +2,6 @@ import 'dart:async';
 
 import 'package:flutter/widgets.dart';
 import 'package:flutter_test/flutter_test.dart';
-// ignore: import_of_legacy_library_into_null_safe
 import 'package:mockito/mockito.dart';
 import 'package:provider/provider.dart';
 
diff --git a/test/inherited_provider_test.dart b/packages/provider/test/null_safe/inherited_provider_test.dart
similarity index 98%
rename from test/inherited_provider_test.dart
rename to packages/provider/test/null_safe/inherited_provider_test.dart
index a5759a26..963fbbaa 100644
--- a/test/inherited_provider_test.dart
+++ b/packages/provider/test/null_safe/inherited_provider_test.dart
@@ -1,7 +1,6 @@
 import 'package:flutter/widgets.dart';
 import 'package:flutter_test/flutter_test.dart';
 
-// ignore: import_of_legacy_library_into_null_safe
 import 'package:mockito/mockito.dart';
 import 'package:provider/provider.dart';
 import 'package:provider/single_child_widget.dart';
@@ -272,6 +271,8 @@ void main() {
       'Provider.of(listen: false) outside of build works when it loads a provider',
       (tester) async {
     final notifier = ValueNotifier(42);
+    addTearDown(notifier.dispose);
+
     await tester.pumpWidget(
       MultiProvider(
         providers: [
@@ -330,9 +331,10 @@ void main() {
   testWidgets(
       'builder receives the current value and updates independently from `update`',
       (tester) async {
-    final child = Container();
-
     final notifier = ValueNotifier(0);
+    addTearDown(notifier.dispose);
+
+    final child = Container();
     final builder = TransitionBuilderMock((c, child) {
       final notifier = Provider.of<ValueNotifier<int>>(c);
       return Text(
@@ -366,6 +368,8 @@ void main() {
     final child = Container();
 
     final notifier = ValueNotifier(0);
+    addTearDown(notifier.dispose);
+
     final builder = TransitionBuilderMock((c, child) {
       return const Text('foo', textDirection: TextDirection.ltr);
     });
@@ -393,6 +397,8 @@ void main() {
     final child = Container();
 
     final notifier = ValueNotifier(0);
+    addTearDown(notifier.dispose);
+
     final builder = TransitionBuilderMock((c, child) {
       return const Text('foo', textDirection: TextDirection.ltr);
     });
@@ -554,7 +560,8 @@ The context used was: Context
         ),
       );
 
-      final rootElement = tester.element(find.bySubtype<InheritedProvider>());
+      final rootElement =
+          tester.element(find.byWidgetPredicate((w) => w is InheritedProvider));
 
       expect(
         rootElement.toString(),
@@ -583,7 +590,8 @@ The context used was: Context
         ),
       );
 
-      final rootElement = tester.element(find.bySubtype<InheritedProvider>());
+      final rootElement =
+          tester.element(find.byWidgetPredicate((w) => w is InheritedProvider));
 
       expect(
         rootElement.toString(),
@@ -607,7 +615,8 @@ The context used was: Context
         ),
       );
 
-      final rootElement = tester.element(find.bySubtype<InheritedProvider>());
+      final rootElement =
+          tester.element(find.byWidgetPredicate((w) => w is InheritedProvider));
 
       expect(
         rootElement.toString(),
@@ -634,7 +643,8 @@ The context used was: Context
         ),
       );
 
-      final rootElement = tester.element(find.bySubtype<DeferredInheritedProvider>());
+      final rootElement = tester.element(
+          find.byWidgetPredicate((w) => w is DeferredInheritedProvider));
 
       expect(
         rootElement.toString(),
@@ -665,7 +675,8 @@ DeferredInheritedProvider<int, int>(controller: 42, value: 24)'''),
         ),
       );
 
-      final rootElement = tester.element(find.bySubtype<InheritedProvider>());
+      final rootElement =
+          tester.element(find.byWidgetPredicate((w) => w is InheritedProvider));
 
       expect(
         rootElement.toString(),
@@ -1659,6 +1670,7 @@ DeferredInheritedProvider<int, int>(controller: 42, value: 24)'''),
         },
       );
       final controller = ValueNotifier<int>(0);
+      addTearDown(controller.dispose);
 
       await tester.pumpWidget(
         DeferredInheritedProvider<ValueNotifier<int>, int>.value(
@@ -1710,6 +1722,7 @@ DeferredInheritedProvider<int, int>(controller: 42, value: 24)'''),
         },
       );
       final controller = ValueNotifier<int>(0);
+      addTearDown(controller.dispose);
 
       await tester.pumpWidget(
         DeferredInheritedProvider<ValueNotifier<int>, int>.value(
@@ -1725,6 +1738,7 @@ DeferredInheritedProvider<int, int>(controller: 42, value: 24)'''),
           DeferredStartListeningMock<ValueNotifier<int>, int>();
       when(startListening2(any, any, any, any)).thenReturn(() {});
       final controller2 = ValueNotifier<int>(0);
+      addTearDown(controller2.dispose);
 
       await tester.pumpWidget(
         DeferredInheritedProvider<ValueNotifier<int>, int>.value(
@@ -1845,6 +1859,7 @@ DeferredInheritedProvider<int, int>(controller: 42, value: 24)'''),
           DeferredStartListeningMock<ValueNotifier<int>, int>();
       when(startListening(any, any, any, any)).thenReturn(() {});
       final controller = ValueNotifier<int>(0);
+      addTearDown(controller.dispose);
 
       await tester.pumpWidget(
         DeferredInheritedProvider<ValueNotifier<int>, int>.value(
@@ -1878,6 +1893,7 @@ DeferredInheritedProvider<int, int>(controller: 42, value: 24)'''),
         },
       );
       final controller = ValueNotifier<int>(0);
+      addTearDown(controller.dispose);
 
       await tester.pumpWidget(
         DeferredInheritedProvider<ValueNotifier<int>, int>.value(
@@ -1901,6 +1917,7 @@ DeferredInheritedProvider<int, int>(controller: 42, value: 24)'''),
         },
       );
       final controller2 = ValueNotifier<int>(1);
+      addTearDown(controller2.dispose);
 
       await tester.pumpWidget(
         DeferredInheritedProvider<ValueNotifier<int>, int>.value(
diff --git a/test/listenable_provider_test.dart b/packages/provider/test/null_safe/listenable_provider_test.dart
similarity index 97%
rename from test/listenable_provider_test.dart
rename to packages/provider/test/null_safe/listenable_provider_test.dart
index 0574d07b..f744cf96 100644
--- a/test/listenable_provider_test.dart
+++ b/packages/provider/test/null_safe/listenable_provider_test.dart
@@ -1,7 +1,6 @@
 // ignore_for_file: invalid_use_of_protected_member
 import 'package:flutter/widgets.dart';
 import 'package:flutter_test/flutter_test.dart';
-// ignore: import_of_legacy_library_into_null_safe
 import 'package:mockito/mockito.dart';
 import 'package:provider/provider.dart';
 
@@ -12,6 +11,7 @@ void main() {
     testWidgets('works with MultiProvider', (tester) async {
       final key = GlobalKey();
       final listenable = ChangeNotifier();
+      addTearDown(listenable.dispose);
 
       await tester.pumpWidget(
         MultiProvider(
@@ -31,6 +31,7 @@ void main() {
       (tester) async {
         final key = GlobalKey();
         final notifier = ValueNotifier(0)..addListener(() {});
+        addTearDown(notifier.dispose);
 
         await tester.pumpWidget(
           ListenableProvider(
@@ -259,6 +260,8 @@ void main() {
       when(builder(any)).thenReturn(Container());
 
       var listenable = ChangeNotifier();
+      addTearDown(listenable.dispose);
+
       Widget build() {
         return ListenableProvider.value(
           value: listenable,
@@ -277,6 +280,7 @@ void main() {
 
       final previousNotifier = listenable;
       listenable = ChangeNotifier();
+      addTearDown(listenable.dispose);
 
       await tester.pumpWidget(build());
 
@@ -293,6 +297,7 @@ void main() {
     testWidgets("rebuilding with the same provider don't rebuilds descendants",
         (tester) async {
       final listenable = ChangeNotifier();
+      addTearDown(listenable.dispose);
 
       var buildCount = 0;
       final child = Consumer<ChangeNotifier>(
@@ -350,6 +355,8 @@ void main() {
 
     testWidgets('notifylistener rebuilds descendants', (tester) async {
       final listenable = ChangeNotifier();
+      addTearDown(listenable.dispose);
+
       final keyChild = GlobalKey();
       final builder = BuilderMock();
       when(builder(any)).thenReturn(Container());
diff --git a/test/listenable_proxy_provider_test.dart b/packages/provider/test/null_safe/listenable_proxy_provider_test.dart
similarity index 88%
rename from test/listenable_proxy_provider_test.dart
rename to packages/provider/test/null_safe/listenable_proxy_provider_test.dart
index b9b12caf..958845f7 100644
--- a/test/listenable_proxy_provider_test.dart
+++ b/packages/provider/test/null_safe/listenable_proxy_provider_test.dart
@@ -1,14 +1,30 @@
 // ignore_for_file: invalid_use_of_protected_member
 import 'package:flutter/widgets.dart';
 import 'package:flutter_test/flutter_test.dart';
-// ignore: import_of_legacy_library_into_null_safe
 import 'package:mockito/mockito.dart';
 import 'package:provider/provider.dart';
 
 import 'common.dart';
 
 // ignore: prefer_mixin, must_be_immutable
-class _ListenableCombined = Combined with ChangeNotifier;
+class _ListenableCombined extends Combined implements Listenable {
+  const _ListenableCombined([
+    BuildContext? context,
+    Combined? previous,
+    A? a,
+    B? b,
+    C? c,
+    D? d,
+    E? e,
+    F? f,
+  ]) : super(context, previous, a, b, c, d, e, f);
+
+  @override
+  void addListener(VoidCallback listener) {}
+
+  @override
+  void removeListener(VoidCallback listener) {}
+}
 
 void main() {
   final a = A();
@@ -32,6 +48,8 @@ void main() {
     testWidgets('rebuilds dependendents when listeners are called',
         (tester) async {
       final notifier = ValueNotifier(0);
+      addTearDown(notifier.dispose);
+
       await tester.pumpWidget(
         MultiProvider(
           providers: [
@@ -118,6 +136,7 @@ void main() {
     testWidgets('disposes of created value', (tester) async {
       final dispose = DisposeMock<ValueNotifier<int>>();
       final notifier = ValueNotifier(0);
+      addTearDown(notifier.dispose);
       final key = GlobalKey();
 
       await tester.pumpWidget(
@@ -158,7 +177,7 @@ void main() {
             Provider.value(value: e),
             Provider.value(value: f),
             ListenableProxyProvider0<_ListenableCombined>(
-              create: (_) => _ListenableCombined(),
+              create: (_) => const _ListenableCombined(),
               update: (context, previous) => _ListenableCombined(
                 context,
                 previous,
@@ -181,7 +200,7 @@ void main() {
         combinedConsumerMock(
           _ListenableCombined(
             context,
-            _ListenableCombined(),
+            const _ListenableCombined(),
             a,
             b,
             c,
@@ -204,7 +223,7 @@ void main() {
             Provider.value(value: e),
             Provider.value(value: f),
             ListenableProxyProvider2<A, B, _ListenableCombined>(
-              create: (_) => _ListenableCombined(),
+              create: (_) => const _ListenableCombined(),
               update: (context, a, b, previous) =>
                   _ListenableCombined(context, previous, a, b),
             )
@@ -217,7 +236,7 @@ void main() {
 
       verify(
         combinedConsumerMock(
-          _ListenableCombined(context, _ListenableCombined(), a, b),
+          _ListenableCombined(context, const _ListenableCombined(), a, b),
         ),
       ).called(1);
     });
@@ -233,7 +252,7 @@ void main() {
             Provider.value(value: e),
             Provider.value(value: f),
             ListenableProxyProvider3<A, B, C, _ListenableCombined>(
-              create: (_) => _ListenableCombined(),
+              create: (_) => const _ListenableCombined(),
               update: (context, a, b, c, previous) =>
                   _ListenableCombined(context, previous, a, b, c),
             )
@@ -246,7 +265,7 @@ void main() {
 
       verify(
         combinedConsumerMock(
-          _ListenableCombined(context, _ListenableCombined(), a, b, c),
+          _ListenableCombined(context, const _ListenableCombined(), a, b, c),
         ),
       ).called(1);
     });
@@ -262,7 +281,7 @@ void main() {
             Provider.value(value: e),
             Provider.value(value: f),
             ListenableProxyProvider4<A, B, C, D, _ListenableCombined>(
-              create: (_) => _ListenableCombined(),
+              create: (_) => const _ListenableCombined(),
               update: (context, a, b, c, d, previous) =>
                   _ListenableCombined(context, previous, a, b, c, d),
             )
@@ -275,7 +294,7 @@ void main() {
 
       verify(
         combinedConsumerMock(
-          _ListenableCombined(context, _ListenableCombined(), a, b, c, d),
+          _ListenableCombined(context, const _ListenableCombined(), a, b, c, d),
         ),
       ).called(1);
     });
@@ -291,7 +310,7 @@ void main() {
             Provider.value(value: e),
             Provider.value(value: f),
             ListenableProxyProvider5<A, B, C, D, E, _ListenableCombined>(
-              create: (_) => _ListenableCombined(),
+              create: (_) => const _ListenableCombined(),
               update: (context, a, b, c, d, e, previous) =>
                   _ListenableCombined(context, previous, a, b, c, d, e),
             )
@@ -304,7 +323,8 @@ void main() {
 
       verify(
         combinedConsumerMock(
-          _ListenableCombined(context, _ListenableCombined(), a, b, c, d, e),
+          _ListenableCombined(
+              context, const _ListenableCombined(), a, b, c, d, e),
         ),
       ).called(1);
     });
@@ -320,7 +340,7 @@ void main() {
             Provider.value(value: e),
             Provider.value(value: f),
             ListenableProxyProvider6<A, B, C, D, E, F, _ListenableCombined>(
-              create: (_) => _ListenableCombined(),
+              create: (_) => const _ListenableCombined(),
               update: (context, a, b, c, d, e, f, previous) =>
                   _ListenableCombined(context, previous, a, b, c, d, e, f),
             )
@@ -332,7 +352,8 @@ void main() {
       final context = findInheritedProvider();
       verify(
         combinedConsumerMock(
-          _ListenableCombined(context, _ListenableCombined(), a, b, c, d, e, f),
+          _ListenableCombined(
+              context, const _ListenableCombined(), a, b, c, d, e, f),
         ),
       ).called(1);
     });
diff --git a/test/matchers.dart b/packages/provider/test/null_safe/matchers.dart
similarity index 100%
rename from test/matchers.dart
rename to packages/provider/test/null_safe/matchers.dart
diff --git a/test/multi_provider_test.dart b/packages/provider/test/null_safe/multi_provider_test.dart
similarity index 100%
rename from test/multi_provider_test.dart
rename to packages/provider/test/null_safe/multi_provider_test.dart
diff --git a/test/provider_test.dart b/packages/provider/test/null_safe/provider_test.dart
similarity index 100%
rename from test/provider_test.dart
rename to packages/provider/test/null_safe/provider_test.dart
diff --git a/test/proxy_provider_test.dart b/packages/provider/test/null_safe/proxy_provider_test.dart
similarity index 99%
rename from test/proxy_provider_test.dart
rename to packages/provider/test/null_safe/proxy_provider_test.dart
index b8319901..ce933630 100644
--- a/test/proxy_provider_test.dart
+++ b/packages/provider/test/null_safe/proxy_provider_test.dart
@@ -1,6 +1,5 @@
 import 'package:flutter/widgets.dart';
 import 'package:flutter_test/flutter_test.dart';
-// ignore: import_of_legacy_library_into_null_safe
 import 'package:mockito/mockito.dart';
 import 'package:provider/provider.dart';
 
diff --git a/test/reassemble_test.dart b/packages/provider/test/null_safe/reassemble_test.dart
similarity index 100%
rename from test/reassemble_test.dart
rename to packages/provider/test/null_safe/reassemble_test.dart
diff --git a/test/selector_test.dart b/packages/provider/test/null_safe/selector_test.dart
similarity index 91%
rename from test/selector_test.dart
rename to packages/provider/test/null_safe/selector_test.dart
index 9598a2fd..4435aa24 100644
--- a/test/selector_test.dart
+++ b/packages/provider/test/null_safe/selector_test.dart
@@ -1,9 +1,7 @@
 import 'package:flutter/foundation.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_test/flutter_test.dart';
-// ignore: import_of_legacy_library_into_null_safe
 import 'package:mockito/mockito.dart' as mockito show when;
-// ignore: import_of_legacy_library_into_null_safe
 import 'package:mockito/mockito.dart';
 import 'package:provider/provider.dart';
 import 'package:provider/single_child_widget.dart';
@@ -336,6 +334,36 @@ void main() {
     expect(find.text('24'), findsOneWidget);
   });
 
+  testWidgets('rebuild when inherited widget changes', (tester) async {
+    final selectorWidget = Directionality(
+      textDirection: TextDirection.ltr,
+      child: Selector0<int>(
+        selector: (_) => 0,
+        builder: (context, _, __) =>
+            Text('${DummyInheritedWidget.of(context).data}'),
+      ),
+    );
+
+    await tester.pumpWidget(
+      DummyInheritedWidget(
+        data: 1,
+        child: selectorWidget,
+      ),
+    );
+
+    expect(find.text('1'), findsOneWidget);
+
+    await tester.pumpWidget(
+      DummyInheritedWidget(
+        data: 2,
+        child: selectorWidget,
+      ),
+    );
+
+    expect(find.text('1'), findsNothing);
+    expect(find.text('2'), findsOneWidget);
+  });
+
   testWidgets('debugFillProperties', (tester) async {
     final builder = DiagnosticPropertiesBuilder();
     final key = UniqueKey();
@@ -526,3 +554,23 @@ class MockBuilder<T> extends Mock {
     ) as Widget;
   }
 }
+
+class DummyInheritedWidget extends InheritedWidget {
+  const DummyInheritedWidget({
+    required this.data,
+    required Widget child,
+    Key? key,
+  }) : super(child: child, key: key);
+
+  final int data;
+
+  static DummyInheritedWidget of(BuildContext context) {
+    final result =
+        context.dependOnInheritedWidgetOfExactType<DummyInheritedWidget>();
+    assert(result != null, 'No DummyInheritedWidget found in context');
+    return result!;
+  }
+
+  @override
+  bool updateShouldNotify(DummyInheritedWidget old) => old.data != data;
+}
diff --git a/test/stateful_provider_test.dart b/packages/provider/test/null_safe/stateful_provider_test.dart
similarity index 96%
rename from test/stateful_provider_test.dart
rename to packages/provider/test/null_safe/stateful_provider_test.dart
index beee96d2..daf1b2f7 100644
--- a/test/stateful_provider_test.dart
+++ b/packages/provider/test/null_safe/stateful_provider_test.dart
@@ -1,6 +1,5 @@
 import 'package:flutter/widgets.dart';
 import 'package:flutter_test/flutter_test.dart';
-// ignore: import_of_legacy_library_into_null_safe
 import 'package:mockito/mockito.dart';
 import 'package:provider/provider.dart';
 import 'package:provider/src/provider.dart';
diff --git a/test/stream_provider_test.dart b/packages/provider/test/null_safe/stream_provider_test.dart
similarity index 98%
rename from test/stream_provider_test.dart
rename to packages/provider/test/null_safe/stream_provider_test.dart
index 3e831ae4..763df853 100644
--- a/test/stream_provider_test.dart
+++ b/packages/provider/test/null_safe/stream_provider_test.dart
@@ -2,7 +2,6 @@ import 'dart:async';
 
 import 'package:flutter/widgets.dart';
 import 'package:flutter_test/flutter_test.dart';
-// ignore: import_of_legacy_library_into_null_safe
 import 'package:mockito/mockito.dart';
 import 'package:provider/provider.dart';
 
diff --git a/test/value_listenable_test.dart b/packages/provider/test/null_safe/value_listenable_test.dart
similarity index 90%
rename from test/value_listenable_test.dart
rename to packages/provider/test/null_safe/value_listenable_test.dart
index 073b2e94..ded95df2 100644
--- a/test/value_listenable_test.dart
+++ b/packages/provider/test/null_safe/value_listenable_test.dart
@@ -1,7 +1,6 @@
 import 'package:flutter/foundation.dart';
 import 'package:flutter/widgets.dart';
 import 'package:flutter_test/flutter_test.dart';
-// ignore: import_of_legacy_library_into_null_safe
 import 'package:mockito/mockito.dart';
 import 'package:provider/provider.dart';
 
@@ -38,10 +37,16 @@ void main() {
   group('valueListenableProvider', () {
     testWidgets('rebuilds when value change', (tester) async {
       final listenable = ValueNotifier(0);
+      addTearDown(listenable.dispose);
 
       final child = Builder(
-          builder: (context) => Text(Provider.of<int>(context).toString(),
-              textDirection: TextDirection.ltr));
+        builder: (context) {
+          return Text(
+            Provider.of<int>(context).toString(),
+            textDirection: TextDirection.ltr,
+          );
+        },
+      );
 
       await tester.pumpWidget(
         ValueListenableProvider.value(
@@ -59,6 +64,8 @@ void main() {
     testWidgets("don't rebuild dependents by default", (tester) async {
       var buildCount = 0;
       final listenable = ValueNotifier(0);
+      addTearDown(listenable.dispose);
+
       final child = Builder(builder: (context) {
         buildCount++;
         return Container();
@@ -85,10 +92,13 @@ void main() {
 
     testWidgets('pass keys', (tester) async {
       final key = GlobalKey();
+      final valueNotifier = ValueNotifier(42);
+      addTearDown(valueNotifier.dispose);
+
       await tester.pumpWidget(
         ValueListenableProvider.value(
           key: key,
-          value: ValueNotifier(42),
+          value: valueNotifier,
           child: Container(),
         ),
       );
@@ -118,10 +128,12 @@ void main() {
     });
 
     testWidgets('pass updateShouldNotify', (tester) async {
+      final notifier = ValueNotifier(0);
+      addTearDown(notifier.dispose);
+
       final shouldNotify = UpdateShouldNotifyMock<int>();
       when(shouldNotify(0, 1)).thenReturn(true);
 
-      final notifier = ValueNotifier(0);
       await tester.pumpWidget(
         ValueListenableProvider.value(
           value: notifier,
diff --git a/packages/provider_devtools_extension/.gitignore b/packages/provider_devtools_extension/.gitignore
new file mode 100644
index 00000000..24476c5d
--- /dev/null
+++ b/packages/provider_devtools_extension/.gitignore
@@ -0,0 +1,44 @@
+# Miscellaneous
+*.class
+*.log
+*.pyc
+*.swp
+.DS_Store
+.atom/
+.buildlog/
+.history
+.svn/
+migrate_working_dir/
+
+# IntelliJ related
+*.iml
+*.ipr
+*.iws
+.idea/
+
+# The .vscode folder contains launch configuration and tasks you configure in
+# VS Code which you may wish to be included in version control, so this line
+# is commented out by default.
+#.vscode/
+
+# Flutter/Dart/Pub related
+**/doc/api/
+**/ios/Flutter/.last_build_id
+.dart_tool/
+.flutter-plugins
+.flutter-plugins-dependencies
+.packages
+.pub-cache/
+.pub/
+/build/
+
+# Symbolication related
+app.*.symbols
+
+# Obfuscation related
+app.*.map.json
+
+# Android Studio will place build artifacts here
+/android/app/debug
+/android/app/profile
+/android/app/release
diff --git a/packages/provider_devtools_extension/.metadata b/packages/provider_devtools_extension/.metadata
new file mode 100644
index 00000000..86e2f966
--- /dev/null
+++ b/packages/provider_devtools_extension/.metadata
@@ -0,0 +1,30 @@
+# This file tracks properties of this Flutter project.
+# Used by Flutter tool to assess capabilities and perform upgrades etc.
+#
+# This file should be version controlled and should not be manually edited.
+
+version:
+  revision: "972d36c4efeee7e3bb16c050fd233389625a6470"
+  channel: "[user-branch]"
+
+project_type: app
+
+# Tracks metadata for the flutter migrate command
+migration:
+  platforms:
+    - platform: root
+      create_revision: 972d36c4efeee7e3bb16c050fd233389625a6470
+      base_revision: 972d36c4efeee7e3bb16c050fd233389625a6470
+    - platform: web
+      create_revision: 972d36c4efeee7e3bb16c050fd233389625a6470
+      base_revision: 972d36c4efeee7e3bb16c050fd233389625a6470
+
+  # User provided section
+
+  # List of Local paths (relative to this file) that should be
+  # ignored by the migrate tool.
+  #
+  # Files that are not part of the templates will be ignored by default.
+  unmanaged_files:
+    - 'lib/main.dart'
+    - 'ios/Runner.xcodeproj/project.pbxproj'
diff --git a/packages/provider_devtools_extension/CHANGELOG.md b/packages/provider_devtools_extension/CHANGELOG.md
new file mode 100644
index 00000000..edfacc94
--- /dev/null
+++ b/packages/provider_devtools_extension/CHANGELOG.md
@@ -0,0 +1,3 @@
+## 0.0.1
+
+Initial release
\ No newline at end of file
diff --git a/packages/provider_devtools_extension/README.md b/packages/provider_devtools_extension/README.md
new file mode 100644
index 00000000..de2f4129
--- /dev/null
+++ b/packages/provider_devtools_extension/README.md
@@ -0,0 +1,7 @@
+This is the source of the provider's devtool.
+
+You can locally run it with:
+
+```
+flutter run -d chrome --dart-define=use_simulated_environment=true
+```
\ No newline at end of file
diff --git a/packages/provider_devtools_extension/analysis_options.yaml b/packages/provider_devtools_extension/analysis_options.yaml
new file mode 100644
index 00000000..1bcc7f2f
--- /dev/null
+++ b/packages/provider_devtools_extension/analysis_options.yaml
@@ -0,0 +1,32 @@
+# This file configures the analyzer, which statically analyzes Dart code to
+# check for errors, warnings, and lints.
+#
+# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
+# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
+# invoked from the command line by running `flutter analyze`.
+
+# The following line activates a set of recommended lints for Flutter apps,
+# packages, and plugins designed to encourage good coding practices.
+include: package:flutter_lints/flutter.yaml
+
+linter:
+  # The lint rules applied to this project can be customized in the
+  # section below to disable rules from the `package:flutter_lints/flutter.yaml`
+  # included above or to enable additional rules. A list of all available lints
+  # and their documentation is published at https://dart.dev/lints.
+  #
+  # Instead of disabling a lint rule for the entire project in the
+  # section below, it can also be suppressed for a single line of code
+  # or a specific dart file by using the `// ignore: name_of_lint` and
+  # `// ignore_for_file: name_of_lint` syntax on the line or in the file
+  # producing the lint.
+  rules:
+    # avoid_print: false  # Uncomment to disable the `avoid_print` rule
+    # prefer_single_quotes: true  # Uncomment to enable the `prefer_single_quotes` rule
+
+# Additional information about this file can be found at
+# https://dart.dev/guides/language/analysis-options
+
+analyzer:
+  exclude:
+    - "**/*.freezed.dart"
\ No newline at end of file
diff --git a/packages/provider_devtools_extension/integration_test/provider_integration_test.dart b/packages/provider_devtools_extension/integration_test/provider_integration_test.dart
new file mode 100644
index 00000000..d3c5a680
--- /dev/null
+++ b/packages/provider_devtools_extension/integration_test/provider_integration_test.dart
@@ -0,0 +1,3 @@
+// TODO: write integration test as proper integration test.
+// See legacy test here:
+// https://github.com/flutter/devtools/blob/master/packages/devtools_app/test/provider/provider_screen_integration_test.dart
diff --git a/packages/provider_devtools_extension/lib/main.dart b/packages/provider_devtools_extension/lib/main.dart
new file mode 100644
index 00000000..e90a5fe2
--- /dev/null
+++ b/packages/provider_devtools_extension/lib/main.dart
@@ -0,0 +1,24 @@
+import 'package:devtools_extensions/devtools_extensions.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+
+import 'src/provider_screen.dart';
+import 'src/utils/riverpod_error_logger_observer.dart';
+
+void main() {
+  runApp(const ProviderScope(
+    observers: [ErrorLoggerObserver()],
+    child: ProviderDevToolsExtension(),
+  ));
+}
+
+class ProviderDevToolsExtension extends StatelessWidget {
+  const ProviderDevToolsExtension({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return const DevToolsExtension(
+      child: ProviderScreenBody(),
+    );
+  }
+}
diff --git a/packages/provider_devtools_extension/lib/src/instance_viewer/eval.dart b/packages/provider_devtools_extension/lib/src/instance_viewer/eval.dart
new file mode 100644
index 00000000..1699b3a7
--- /dev/null
+++ b/packages/provider_devtools_extension/lib/src/instance_viewer/eval.dart
@@ -0,0 +1,73 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/// A few utilities related to evaluating dart code
+
+library eval;
+
+import 'dart:async';
+
+import 'package:devtools_app_shared/service.dart';
+import 'package:devtools_extensions/devtools_extensions.dart';
+import 'package:flutter/foundation.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:vm_service/vm_service.dart';
+
+Stream<VmService> get _serviceConnectionStream =>
+    _serviceConnectionStreamController.stream;
+final _serviceConnectionStreamController =
+    StreamController<VmService>.broadcast();
+void setServiceConnectionForProviderScreen(VmService service) {
+  _serviceConnectionStreamController.add(service);
+}
+
+/// Exposes the current VmServiceWrapper.
+/// By listening to this provider instead of directly accessing `serviceManager.service`,
+/// this ensures that providers reload properly when the devtool is connected
+/// to a different application.
+final serviceProvider = StreamProvider<VmService>((ref) async* {
+  yield serviceManager.service!;
+  yield* _serviceConnectionStream;
+});
+
+/// An [EvalOnDartLibrary] that has access to no specific library in particular
+///
+/// Not suitable to be used when evaluating third-party objects, as it would
+/// otherwise not be possible to read private properties.
+final evalProvider = libraryEvalProvider('dart:io');
+
+/// An [EvalOnDartLibrary] that has access to `provider`
+final providerEvalProvider =
+    libraryEvalProvider('package:provider/src/provider.dart');
+
+/// An [EvalOnDartLibrary] for custom objects.
+final libraryEvalProvider =
+    FutureProviderFamily<EvalOnDartLibrary, String>((ref, libraryPath) async {
+  final service = await ref.watch(serviceProvider.future);
+
+  final eval = EvalOnDartLibrary(
+    libraryPath,
+    service,
+    serviceManager: serviceManager,
+  );
+  ref.onDispose(eval.dispose);
+  return eval;
+});
+
+final hotRestartEventProvider =
+    ChangeNotifierProvider<ValueNotifier<void>>((ref) {
+  final selectedIsolateListenable =
+      serviceManager.isolateManager.selectedIsolate;
+
+  // Since ChangeNotifierProvider calls `dispose` on the returned ChangeNotifier
+  // when the provider is destroyed, we can't simply return `selectedIsolateListenable`.
+  // So we're making a copy of it instead.
+  final notifier = ValueNotifier<IsolateRef?>(selectedIsolateListenable.value);
+
+  void listener() => notifier.value = selectedIsolateListenable.value;
+  selectedIsolateListenable.addListener(listener);
+  ref.onDispose(() => selectedIsolateListenable.removeListener(listener));
+
+  return notifier;
+});
diff --git a/packages/provider_devtools_extension/lib/src/instance_viewer/fake_freezed_annotation.dart b/packages/provider_devtools_extension/lib/src/instance_viewer/fake_freezed_annotation.dart
new file mode 100644
index 00000000..e27aa470
--- /dev/null
+++ b/packages/provider_devtools_extension/lib/src/instance_viewer/fake_freezed_annotation.dart
@@ -0,0 +1,26 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file contains annotations that behaves like package:freezed_annotation.
+// This allows using Freezed without having the devtool depend on the package.
+// We could instead remove the annotations, but that would make the process of
+// updating the generated files tedious.
+
+const nullable = Object();
+const freezed = Object();
+
+class Default {
+  const Default(Object value);
+}
+
+class Assert {
+  const Assert(String exp);
+}
+
+class JsonKey {
+  const JsonKey({
+    bool? ignore,
+    Object? defaultValue,
+  });
+}
diff --git a/packages/provider_devtools_extension/lib/src/instance_viewer/instance_details.dart b/packages/provider_devtools_extension/lib/src/instance_viewer/instance_details.dart
new file mode 100644
index 00000000..78578601
--- /dev/null
+++ b/packages/provider_devtools_extension/lib/src/instance_viewer/instance_details.dart
@@ -0,0 +1,203 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:collection/collection.dart';
+import 'package:devtools_app_shared/service.dart';
+import 'package:flutter/foundation.dart';
+import 'package:vm_service/vm_service.dart';
+
+import 'fake_freezed_annotation.dart';
+import 'result.dart';
+
+// This part is generated using `package:freezed`, but without the devtool depending
+// on the package.
+// To update the generated files, temporarily add freezed/freezed_annotation/build_runner
+// as dependencies; replace the `fake_freezed_annotation.dart` import with the
+// real annotation package, then execute `pub run build_runner build`.
+part 'instance_details.freezed.dart';
+
+typedef Setter = Future<void> Function(String newValue);
+
+@freezed
+class PathToProperty with _$PathToProperty {
+  const factory PathToProperty.listIndex(int index) = ListIndexPath;
+
+  // TODO test that mutating a Map does not collapse previously expanded keys
+  const factory PathToProperty.mapKey({
+    required String? ref,
+  }) = MapKeyPath;
+
+  /// Must not depend on [InstanceRef] and its ID, as they may change across
+  /// re-evaluations of the object.
+  /// Depending on those would lead to the UI collapsing previously expanded objects
+  /// because the new path to a property would be different.
+  ///
+  /// We can't just rely on the property name either, because in some cases
+  /// an object can have multiple properties with the same name (private properties
+  /// defined in different libraries)
+  const factory PathToProperty.objectProperty({
+    required String name,
+
+    /// Path to the class/mixin that defined this property
+    required String ownerUri,
+
+    /// Name of the class/mixin that defined this property
+    required String ownerName,
+  }) = PropertyPath;
+
+  factory PathToProperty.fromObjectField(ObjectField field) {
+    return PathToProperty.objectProperty(
+      name: field.name,
+      ownerUri: field.ownerUri,
+      ownerName: field.ownerName,
+    );
+  }
+}
+
+@freezed
+class ObjectField with _$ObjectField {
+  factory ObjectField({
+    required String name,
+    required bool isFinal,
+    required String ownerName,
+    required String ownerUri,
+    required Result<InstanceRef> ref,
+
+    /// An [EvalOnDartLibrary] that can access this field from the owner object
+    required EvalOnDartLibrary eval,
+
+    /// Whether this field was defined by the inspected app or by one of its dependencies
+    ///
+    /// This is used by the UI to hide variables that are not useful for the user.
+    required bool isDefinedByDependency,
+  }) = _ObjectField;
+
+  ObjectField._();
+
+  bool get isPrivate => name.startsWith('_');
+}
+
+@freezed
+class InstanceDetails with _$InstanceDetails {
+  InstanceDetails._();
+
+  factory InstanceDetails.nill({
+    required Setter? setter,
+  }) = NullInstance;
+
+  factory InstanceDetails.boolean(
+    String displayString, {
+    required String instanceRefId,
+    required Setter? setter,
+  }) = BoolInstance;
+
+  factory InstanceDetails.number(
+    String displayString, {
+    required String instanceRefId,
+    required Setter? setter,
+  }) = NumInstance;
+
+  factory InstanceDetails.string(
+    String displayString, {
+    required String instanceRefId,
+    required Setter? setter,
+  }) = StringInstance;
+
+  factory InstanceDetails.map(
+    List<InstanceDetails> keys, {
+    required int hash,
+    required String instanceRefId,
+    required Setter? setter,
+  }) = MapInstance;
+
+  factory InstanceDetails.list({
+    required int length,
+    required int hash,
+    required String instanceRefId,
+    required Setter? setter,
+  }) = ListInstance;
+
+  factory InstanceDetails.object(
+    List<ObjectField> fields, {
+    required String type,
+    required int hash,
+    required String instanceRefId,
+    required Setter? setter,
+
+    /// An [EvalOnDartLibrary] associated with the library of this object
+    ///
+    /// This allows to edit private properties.
+    required EvalOnDartLibrary evalForInstance,
+  }) = ObjectInstance;
+
+  factory InstanceDetails.enumeration({
+    required String type,
+    required String value,
+    required Setter? setter,
+    required String instanceRefId,
+  }) = EnumInstance;
+
+  bool get isExpandable {
+    bool falsy(Object _) => false;
+
+    return map(
+      nill: falsy,
+      boolean: falsy,
+      number: falsy,
+      string: falsy,
+      enumeration: falsy,
+      map: (instance) => instance.keys.isNotEmpty,
+      list: (instance) => instance.length > 0,
+      object: (instance) => instance.fields.isNotEmpty,
+    );
+  }
+
+  // Since `nil` doesn't have those properties, we are manually exposing them
+  String? get instanceRefId {
+    return map(
+      nill: (_) => null,
+      boolean: (a) => a.instanceRefId,
+      number: (a) => a.instanceRefId,
+      string: (a) => a.instanceRefId,
+      map: (a) => a.instanceRefId,
+      list: (a) => a.instanceRefId,
+      object: (a) => a.instanceRefId,
+      enumeration: (a) => a.instanceRefId,
+    );
+  }
+}
+
+/// The path to visit child elements of an [Instance] or providers from `provider`/`riverpod`.
+@freezed
+class InstancePath with _$InstancePath {
+  const InstancePath._();
+
+  const factory InstancePath.fromInstanceId(
+    String instanceId, {
+    @Default([]) List<PathToProperty> pathToProperty,
+  }) = _InstancePathFromInstanceId;
+
+  const factory InstancePath.fromProviderId(
+    String providerId, {
+    @Default([]) List<PathToProperty> pathToProperty,
+  }) = _InstancePathFromProviderId;
+
+  InstancePath get root => copyWith(pathToProperty: []);
+
+  InstancePath? get parent {
+    if (pathToProperty.isEmpty) return null;
+
+    return copyWith(
+      pathToProperty: [
+        for (var i = 0; i + 1 < pathToProperty.length; i++) pathToProperty[i],
+      ],
+    );
+  }
+
+  InstancePath pathForChild(PathToProperty property) {
+    return copyWith(
+      pathToProperty: [...pathToProperty, property],
+    );
+  }
+}
diff --git a/packages/provider_devtools_extension/lib/src/instance_viewer/instance_details.freezed.dart b/packages/provider_devtools_extension/lib/src/instance_viewer/instance_details.freezed.dart
new file mode 100644
index 00000000..e28024d1
--- /dev/null
+++ b/packages/provider_devtools_extension/lib/src/instance_viewer/instance_details.freezed.dart
@@ -0,0 +1,3868 @@
+// coverage:ignore-file
+// GENERATED CODE - DO NOT MODIFY BY HAND
+// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target
+
+part of 'instance_details.dart';
+
+// **************************************************************************
+// FreezedGenerator
+// **************************************************************************
+
+T _$identity<T>(T value) => value;
+
+final _privateConstructorUsedError = UnsupportedError(
+    'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more informations: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
+
+/// @nodoc
+class _$PathToPropertyTearOff {
+  const _$PathToPropertyTearOff();
+
+  ListIndexPath listIndex(int index) {
+    return ListIndexPath(
+      index,
+    );
+  }
+
+  MapKeyPath mapKey({required String? ref}) {
+    return MapKeyPath(
+      ref: ref,
+    );
+  }
+
+  PropertyPath objectProperty(
+      {required String name,
+      required String ownerUri,
+      required String ownerName}) {
+    return PropertyPath(
+      name: name,
+      ownerUri: ownerUri,
+      ownerName: ownerName,
+    );
+  }
+}
+
+/// @nodoc
+const $PathToProperty = _$PathToPropertyTearOff();
+
+/// @nodoc
+mixin _$PathToProperty {
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function(int index) listIndex,
+    required TResult Function(String? ref) mapKey,
+    required TResult Function(String name, String ownerUri, String ownerName)
+        objectProperty,
+  }) =>
+      throw _privateConstructorUsedError;
+  @optionalTypeArgs
+  TResult? whenOrNull<TResult extends Object?>({
+    TResult Function(int index)? listIndex,
+    TResult Function(String? ref)? mapKey,
+    TResult Function(String name, String ownerUri, String ownerName)?
+        objectProperty,
+  }) =>
+      throw _privateConstructorUsedError;
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function(int index)? listIndex,
+    TResult Function(String? ref)? mapKey,
+    TResult Function(String name, String ownerUri, String ownerName)?
+        objectProperty,
+    required TResult orElse(),
+  }) =>
+      throw _privateConstructorUsedError;
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(ListIndexPath value) listIndex,
+    required TResult Function(MapKeyPath value) mapKey,
+    required TResult Function(PropertyPath value) objectProperty,
+  }) =>
+      throw _privateConstructorUsedError;
+  @optionalTypeArgs
+  TResult? mapOrNull<TResult extends Object?>({
+    TResult Function(ListIndexPath value)? listIndex,
+    TResult Function(MapKeyPath value)? mapKey,
+    TResult Function(PropertyPath value)? objectProperty,
+  }) =>
+      throw _privateConstructorUsedError;
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(ListIndexPath value)? listIndex,
+    TResult Function(MapKeyPath value)? mapKey,
+    TResult Function(PropertyPath value)? objectProperty,
+    required TResult orElse(),
+  }) =>
+      throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class $PathToPropertyCopyWith<$Res> {
+  factory $PathToPropertyCopyWith(
+          PathToProperty value, $Res Function(PathToProperty) then) =
+      _$PathToPropertyCopyWithImpl<$Res>;
+}
+
+/// @nodoc
+class _$PathToPropertyCopyWithImpl<$Res>
+    implements $PathToPropertyCopyWith<$Res> {
+  _$PathToPropertyCopyWithImpl(this._value, this._then);
+
+  final PathToProperty _value;
+  // ignore: unused_field
+  final $Res Function(PathToProperty) _then;
+}
+
+/// @nodoc
+abstract class $ListIndexPathCopyWith<$Res> {
+  factory $ListIndexPathCopyWith(
+          ListIndexPath value, $Res Function(ListIndexPath) then) =
+      _$ListIndexPathCopyWithImpl<$Res>;
+  $Res call({int index});
+}
+
+/// @nodoc
+class _$ListIndexPathCopyWithImpl<$Res>
+    extends _$PathToPropertyCopyWithImpl<$Res>
+    implements $ListIndexPathCopyWith<$Res> {
+  _$ListIndexPathCopyWithImpl(
+      ListIndexPath _value, $Res Function(ListIndexPath) _then)
+      : super(_value, (v) => _then(v as ListIndexPath));
+
+  @override
+  ListIndexPath get _value => super._value as ListIndexPath;
+
+  @override
+  $Res call({
+    Object? index = freezed,
+  }) {
+    return _then(ListIndexPath(
+      index == freezed
+          ? _value.index
+          : index // ignore: cast_nullable_to_non_nullable
+              as int,
+    ));
+  }
+}
+
+/// @nodoc
+
+class _$ListIndexPath with DiagnosticableTreeMixin implements ListIndexPath {
+  const _$ListIndexPath(this.index);
+
+  @override
+  final int index;
+
+  @override
+  String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) {
+    return 'PathToProperty.listIndex(index: $index)';
+  }
+
+  @override
+  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
+    super.debugFillProperties(properties);
+    properties
+      ..add(DiagnosticsProperty('type', 'PathToProperty.listIndex'))
+      ..add(DiagnosticsProperty('index', index));
+  }
+
+  @override
+  bool operator ==(dynamic other) {
+    return identical(this, other) ||
+        (other.runtimeType == runtimeType &&
+            other is ListIndexPath &&
+            const DeepCollectionEquality().equals(other.index, index));
+  }
+
+  @override
+  int get hashCode =>
+      Object.hash(runtimeType, const DeepCollectionEquality().hash(index));
+
+  @JsonKey(ignore: true)
+  @override
+  $ListIndexPathCopyWith<ListIndexPath> get copyWith =>
+      _$ListIndexPathCopyWithImpl<ListIndexPath>(this, _$identity);
+
+  @override
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function(int index) listIndex,
+    required TResult Function(String? ref) mapKey,
+    required TResult Function(String name, String ownerUri, String ownerName)
+        objectProperty,
+  }) {
+    return listIndex(index);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult? whenOrNull<TResult extends Object?>({
+    TResult Function(int index)? listIndex,
+    TResult Function(String? ref)? mapKey,
+    TResult Function(String name, String ownerUri, String ownerName)?
+        objectProperty,
+  }) {
+    return listIndex?.call(index);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function(int index)? listIndex,
+    TResult Function(String? ref)? mapKey,
+    TResult Function(String name, String ownerUri, String ownerName)?
+        objectProperty,
+    required TResult orElse(),
+  }) {
+    if (listIndex != null) {
+      return listIndex(index);
+    }
+    return orElse();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(ListIndexPath value) listIndex,
+    required TResult Function(MapKeyPath value) mapKey,
+    required TResult Function(PropertyPath value) objectProperty,
+  }) {
+    return listIndex(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult? mapOrNull<TResult extends Object?>({
+    TResult Function(ListIndexPath value)? listIndex,
+    TResult Function(MapKeyPath value)? mapKey,
+    TResult Function(PropertyPath value)? objectProperty,
+  }) {
+    return listIndex?.call(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(ListIndexPath value)? listIndex,
+    TResult Function(MapKeyPath value)? mapKey,
+    TResult Function(PropertyPath value)? objectProperty,
+    required TResult orElse(),
+  }) {
+    if (listIndex != null) {
+      return listIndex(this);
+    }
+    return orElse();
+  }
+}
+
+abstract class ListIndexPath implements PathToProperty {
+  const factory ListIndexPath(int index) = _$ListIndexPath;
+
+  int get index;
+  @JsonKey(ignore: true)
+  $ListIndexPathCopyWith<ListIndexPath> get copyWith =>
+      throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class $MapKeyPathCopyWith<$Res> {
+  factory $MapKeyPathCopyWith(
+          MapKeyPath value, $Res Function(MapKeyPath) then) =
+      _$MapKeyPathCopyWithImpl<$Res>;
+  $Res call({String? ref});
+}
+
+/// @nodoc
+class _$MapKeyPathCopyWithImpl<$Res> extends _$PathToPropertyCopyWithImpl<$Res>
+    implements $MapKeyPathCopyWith<$Res> {
+  _$MapKeyPathCopyWithImpl(MapKeyPath _value, $Res Function(MapKeyPath) _then)
+      : super(_value, (v) => _then(v as MapKeyPath));
+
+  @override
+  MapKeyPath get _value => super._value as MapKeyPath;
+
+  @override
+  $Res call({
+    Object? ref = freezed,
+  }) {
+    return _then(MapKeyPath(
+      ref: ref == freezed
+          ? _value.ref
+          : ref // ignore: cast_nullable_to_non_nullable
+              as String?,
+    ));
+  }
+}
+
+/// @nodoc
+
+class _$MapKeyPath with DiagnosticableTreeMixin implements MapKeyPath {
+  const _$MapKeyPath({required this.ref});
+
+  @override
+  final String? ref;
+
+  @override
+  String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) {
+    return 'PathToProperty.mapKey(ref: $ref)';
+  }
+
+  @override
+  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
+    super.debugFillProperties(properties);
+    properties
+      ..add(DiagnosticsProperty('type', 'PathToProperty.mapKey'))
+      ..add(DiagnosticsProperty('ref', ref));
+  }
+
+  @override
+  bool operator ==(dynamic other) {
+    return identical(this, other) ||
+        (other.runtimeType == runtimeType &&
+            other is MapKeyPath &&
+            const DeepCollectionEquality().equals(other.ref, ref));
+  }
+
+  @override
+  int get hashCode =>
+      Object.hash(runtimeType, const DeepCollectionEquality().hash(ref));
+
+  @JsonKey(ignore: true)
+  @override
+  $MapKeyPathCopyWith<MapKeyPath> get copyWith =>
+      _$MapKeyPathCopyWithImpl<MapKeyPath>(this, _$identity);
+
+  @override
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function(int index) listIndex,
+    required TResult Function(String? ref) mapKey,
+    required TResult Function(String name, String ownerUri, String ownerName)
+        objectProperty,
+  }) {
+    return mapKey(ref);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult? whenOrNull<TResult extends Object?>({
+    TResult Function(int index)? listIndex,
+    TResult Function(String? ref)? mapKey,
+    TResult Function(String name, String ownerUri, String ownerName)?
+        objectProperty,
+  }) {
+    return mapKey?.call(ref);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function(int index)? listIndex,
+    TResult Function(String? ref)? mapKey,
+    TResult Function(String name, String ownerUri, String ownerName)?
+        objectProperty,
+    required TResult orElse(),
+  }) {
+    if (mapKey != null) {
+      return mapKey(ref);
+    }
+    return orElse();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(ListIndexPath value) listIndex,
+    required TResult Function(MapKeyPath value) mapKey,
+    required TResult Function(PropertyPath value) objectProperty,
+  }) {
+    return mapKey(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult? mapOrNull<TResult extends Object?>({
+    TResult Function(ListIndexPath value)? listIndex,
+    TResult Function(MapKeyPath value)? mapKey,
+    TResult Function(PropertyPath value)? objectProperty,
+  }) {
+    return mapKey?.call(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(ListIndexPath value)? listIndex,
+    TResult Function(MapKeyPath value)? mapKey,
+    TResult Function(PropertyPath value)? objectProperty,
+    required TResult orElse(),
+  }) {
+    if (mapKey != null) {
+      return mapKey(this);
+    }
+    return orElse();
+  }
+}
+
+abstract class MapKeyPath implements PathToProperty {
+  const factory MapKeyPath({required String? ref}) = _$MapKeyPath;
+
+  String? get ref;
+  @JsonKey(ignore: true)
+  $MapKeyPathCopyWith<MapKeyPath> get copyWith =>
+      throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class $PropertyPathCopyWith<$Res> {
+  factory $PropertyPathCopyWith(
+          PropertyPath value, $Res Function(PropertyPath) then) =
+      _$PropertyPathCopyWithImpl<$Res>;
+  $Res call({String name, String ownerUri, String ownerName});
+}
+
+/// @nodoc
+class _$PropertyPathCopyWithImpl<$Res>
+    extends _$PathToPropertyCopyWithImpl<$Res>
+    implements $PropertyPathCopyWith<$Res> {
+  _$PropertyPathCopyWithImpl(
+      PropertyPath _value, $Res Function(PropertyPath) _then)
+      : super(_value, (v) => _then(v as PropertyPath));
+
+  @override
+  PropertyPath get _value => super._value as PropertyPath;
+
+  @override
+  $Res call({
+    Object? name = freezed,
+    Object? ownerUri = freezed,
+    Object? ownerName = freezed,
+  }) {
+    return _then(PropertyPath(
+      name: name == freezed
+          ? _value.name
+          : name // ignore: cast_nullable_to_non_nullable
+              as String,
+      ownerUri: ownerUri == freezed
+          ? _value.ownerUri
+          : ownerUri // ignore: cast_nullable_to_non_nullable
+              as String,
+      ownerName: ownerName == freezed
+          ? _value.ownerName
+          : ownerName // ignore: cast_nullable_to_non_nullable
+              as String,
+    ));
+  }
+}
+
+/// @nodoc
+
+class _$PropertyPath with DiagnosticableTreeMixin implements PropertyPath {
+  const _$PropertyPath(
+      {required this.name, required this.ownerUri, required this.ownerName});
+
+  @override
+  final String name;
+  @override
+
+  /// Path to the class/mixin that defined this property
+  final String ownerUri;
+  @override
+
+  /// Name of the class/mixin that defined this property
+  final String ownerName;
+
+  @override
+  String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) {
+    return 'PathToProperty.objectProperty(name: $name, ownerUri: $ownerUri, ownerName: $ownerName)';
+  }
+
+  @override
+  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
+    super.debugFillProperties(properties);
+    properties
+      ..add(DiagnosticsProperty('type', 'PathToProperty.objectProperty'))
+      ..add(DiagnosticsProperty('name', name))
+      ..add(DiagnosticsProperty('ownerUri', ownerUri))
+      ..add(DiagnosticsProperty('ownerName', ownerName));
+  }
+
+  @override
+  bool operator ==(dynamic other) {
+    return identical(this, other) ||
+        (other.runtimeType == runtimeType &&
+            other is PropertyPath &&
+            const DeepCollectionEquality().equals(other.name, name) &&
+            const DeepCollectionEquality().equals(other.ownerUri, ownerUri) &&
+            const DeepCollectionEquality().equals(other.ownerName, ownerName));
+  }
+
+  @override
+  int get hashCode => Object.hash(
+      runtimeType,
+      const DeepCollectionEquality().hash(name),
+      const DeepCollectionEquality().hash(ownerUri),
+      const DeepCollectionEquality().hash(ownerName));
+
+  @JsonKey(ignore: true)
+  @override
+  $PropertyPathCopyWith<PropertyPath> get copyWith =>
+      _$PropertyPathCopyWithImpl<PropertyPath>(this, _$identity);
+
+  @override
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function(int index) listIndex,
+    required TResult Function(String? ref) mapKey,
+    required TResult Function(String name, String ownerUri, String ownerName)
+        objectProperty,
+  }) {
+    return objectProperty(name, ownerUri, ownerName);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult? whenOrNull<TResult extends Object?>({
+    TResult Function(int index)? listIndex,
+    TResult Function(String? ref)? mapKey,
+    TResult Function(String name, String ownerUri, String ownerName)?
+        objectProperty,
+  }) {
+    return objectProperty?.call(name, ownerUri, ownerName);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function(int index)? listIndex,
+    TResult Function(String? ref)? mapKey,
+    TResult Function(String name, String ownerUri, String ownerName)?
+        objectProperty,
+    required TResult orElse(),
+  }) {
+    if (objectProperty != null) {
+      return objectProperty(name, ownerUri, ownerName);
+    }
+    return orElse();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(ListIndexPath value) listIndex,
+    required TResult Function(MapKeyPath value) mapKey,
+    required TResult Function(PropertyPath value) objectProperty,
+  }) {
+    return objectProperty(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult? mapOrNull<TResult extends Object?>({
+    TResult Function(ListIndexPath value)? listIndex,
+    TResult Function(MapKeyPath value)? mapKey,
+    TResult Function(PropertyPath value)? objectProperty,
+  }) {
+    return objectProperty?.call(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(ListIndexPath value)? listIndex,
+    TResult Function(MapKeyPath value)? mapKey,
+    TResult Function(PropertyPath value)? objectProperty,
+    required TResult orElse(),
+  }) {
+    if (objectProperty != null) {
+      return objectProperty(this);
+    }
+    return orElse();
+  }
+}
+
+abstract class PropertyPath implements PathToProperty {
+  const factory PropertyPath(
+      {required String name,
+      required String ownerUri,
+      required String ownerName}) = _$PropertyPath;
+
+  String get name;
+
+  /// Path to the class/mixin that defined this property
+  String get ownerUri;
+
+  /// Name of the class/mixin that defined this property
+  String get ownerName;
+  @JsonKey(ignore: true)
+  $PropertyPathCopyWith<PropertyPath> get copyWith =>
+      throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+class _$ObjectFieldTearOff {
+  const _$ObjectFieldTearOff();
+
+  _ObjectField call(
+      {required String name,
+      required bool isFinal,
+      required String ownerName,
+      required String ownerUri,
+      required Result<InstanceRef> ref,
+      required EvalOnDartLibrary eval,
+      required bool isDefinedByDependency}) {
+    return _ObjectField(
+      name: name,
+      isFinal: isFinal,
+      ownerName: ownerName,
+      ownerUri: ownerUri,
+      ref: ref,
+      eval: eval,
+      isDefinedByDependency: isDefinedByDependency,
+    );
+  }
+}
+
+/// @nodoc
+const $ObjectField = _$ObjectFieldTearOff();
+
+/// @nodoc
+mixin _$ObjectField {
+  String get name => throw _privateConstructorUsedError;
+  bool get isFinal => throw _privateConstructorUsedError;
+  String get ownerName => throw _privateConstructorUsedError;
+  String get ownerUri => throw _privateConstructorUsedError;
+  Result<InstanceRef> get ref => throw _privateConstructorUsedError;
+
+  /// An [EvalOnDartLibrary] that can access this field from the owner object
+  EvalOnDartLibrary get eval => throw _privateConstructorUsedError;
+
+  /// Whether this field was defined by the inspected app or by one of its dependencies
+  ///
+  /// This is used by the UI to hide variables that are not useful for the user.
+  bool get isDefinedByDependency => throw _privateConstructorUsedError;
+
+  @JsonKey(ignore: true)
+  $ObjectFieldCopyWith<ObjectField> get copyWith =>
+      throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class $ObjectFieldCopyWith<$Res> {
+  factory $ObjectFieldCopyWith(
+          ObjectField value, $Res Function(ObjectField) then) =
+      _$ObjectFieldCopyWithImpl<$Res>;
+  $Res call(
+      {String name,
+      bool isFinal,
+      String ownerName,
+      String ownerUri,
+      Result<InstanceRef> ref,
+      EvalOnDartLibrary eval,
+      bool isDefinedByDependency});
+
+  $ResultCopyWith<InstanceRef, $Res> get ref;
+}
+
+/// @nodoc
+class _$ObjectFieldCopyWithImpl<$Res> implements $ObjectFieldCopyWith<$Res> {
+  _$ObjectFieldCopyWithImpl(this._value, this._then);
+
+  final ObjectField _value;
+  // ignore: unused_field
+  final $Res Function(ObjectField) _then;
+
+  @override
+  $Res call({
+    Object? name = freezed,
+    Object? isFinal = freezed,
+    Object? ownerName = freezed,
+    Object? ownerUri = freezed,
+    Object? ref = freezed,
+    Object? eval = freezed,
+    Object? isDefinedByDependency = freezed,
+  }) {
+    return _then(_value.copyWith(
+      name: name == freezed
+          ? _value.name
+          : name // ignore: cast_nullable_to_non_nullable
+              as String,
+      isFinal: isFinal == freezed
+          ? _value.isFinal
+          : isFinal // ignore: cast_nullable_to_non_nullable
+              as bool,
+      ownerName: ownerName == freezed
+          ? _value.ownerName
+          : ownerName // ignore: cast_nullable_to_non_nullable
+              as String,
+      ownerUri: ownerUri == freezed
+          ? _value.ownerUri
+          : ownerUri // ignore: cast_nullable_to_non_nullable
+              as String,
+      ref: ref == freezed
+          ? _value.ref
+          : ref // ignore: cast_nullable_to_non_nullable
+              as Result<InstanceRef>,
+      eval: eval == freezed
+          ? _value.eval
+          : eval // ignore: cast_nullable_to_non_nullable
+              as EvalOnDartLibrary,
+      isDefinedByDependency: isDefinedByDependency == freezed
+          ? _value.isDefinedByDependency
+          : isDefinedByDependency // ignore: cast_nullable_to_non_nullable
+              as bool,
+    ));
+  }
+
+  @override
+  $ResultCopyWith<InstanceRef, $Res> get ref {
+    return $ResultCopyWith<InstanceRef, $Res>(_value.ref, (value) {
+      return _then(_value.copyWith(ref: value));
+    });
+  }
+}
+
+/// @nodoc
+abstract class _$ObjectFieldCopyWith<$Res>
+    implements $ObjectFieldCopyWith<$Res> {
+  factory _$ObjectFieldCopyWith(
+          _ObjectField value, $Res Function(_ObjectField) then) =
+      __$ObjectFieldCopyWithImpl<$Res>;
+  @override
+  $Res call(
+      {String name,
+      bool isFinal,
+      String ownerName,
+      String ownerUri,
+      Result<InstanceRef> ref,
+      EvalOnDartLibrary eval,
+      bool isDefinedByDependency});
+
+  @override
+  $ResultCopyWith<InstanceRef, $Res> get ref;
+}
+
+/// @nodoc
+class __$ObjectFieldCopyWithImpl<$Res> extends _$ObjectFieldCopyWithImpl<$Res>
+    implements _$ObjectFieldCopyWith<$Res> {
+  __$ObjectFieldCopyWithImpl(
+      _ObjectField _value, $Res Function(_ObjectField) _then)
+      : super(_value, (v) => _then(v as _ObjectField));
+
+  @override
+  _ObjectField get _value => super._value as _ObjectField;
+
+  @override
+  $Res call({
+    Object? name = freezed,
+    Object? isFinal = freezed,
+    Object? ownerName = freezed,
+    Object? ownerUri = freezed,
+    Object? ref = freezed,
+    Object? eval = freezed,
+    Object? isDefinedByDependency = freezed,
+  }) {
+    return _then(_ObjectField(
+      name: name == freezed
+          ? _value.name
+          : name // ignore: cast_nullable_to_non_nullable
+              as String,
+      isFinal: isFinal == freezed
+          ? _value.isFinal
+          : isFinal // ignore: cast_nullable_to_non_nullable
+              as bool,
+      ownerName: ownerName == freezed
+          ? _value.ownerName
+          : ownerName // ignore: cast_nullable_to_non_nullable
+              as String,
+      ownerUri: ownerUri == freezed
+          ? _value.ownerUri
+          : ownerUri // ignore: cast_nullable_to_non_nullable
+              as String,
+      ref: ref == freezed
+          ? _value.ref
+          : ref // ignore: cast_nullable_to_non_nullable
+              as Result<InstanceRef>,
+      eval: eval == freezed
+          ? _value.eval
+          : eval // ignore: cast_nullable_to_non_nullable
+              as EvalOnDartLibrary,
+      isDefinedByDependency: isDefinedByDependency == freezed
+          ? _value.isDefinedByDependency
+          : isDefinedByDependency // ignore: cast_nullable_to_non_nullable
+              as bool,
+    ));
+  }
+}
+
+/// @nodoc
+
+class _$_ObjectField extends _ObjectField with DiagnosticableTreeMixin {
+  _$_ObjectField(
+      {required this.name,
+      required this.isFinal,
+      required this.ownerName,
+      required this.ownerUri,
+      required this.ref,
+      required this.eval,
+      required this.isDefinedByDependency})
+      : super._();
+
+  @override
+  final String name;
+  @override
+  final bool isFinal;
+  @override
+  final String ownerName;
+  @override
+  final String ownerUri;
+  @override
+  final Result<InstanceRef> ref;
+  @override
+
+  /// An [EvalOnDartLibrary] that can access this field from the owner object
+  final EvalOnDartLibrary eval;
+  @override
+
+  /// Whether this field was defined by the inspected app or by one of its dependencies
+  ///
+  /// This is used by the UI to hide variables that are not useful for the user.
+  final bool isDefinedByDependency;
+
+  @override
+  String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) {
+    return 'ObjectField(name: $name, isFinal: $isFinal, ownerName: $ownerName, ownerUri: $ownerUri, ref: $ref, eval: $eval, isDefinedByDependency: $isDefinedByDependency)';
+  }
+
+  @override
+  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
+    super.debugFillProperties(properties);
+    properties
+      ..add(DiagnosticsProperty('type', 'ObjectField'))
+      ..add(DiagnosticsProperty('name', name))
+      ..add(DiagnosticsProperty('isFinal', isFinal))
+      ..add(DiagnosticsProperty('ownerName', ownerName))
+      ..add(DiagnosticsProperty('ownerUri', ownerUri))
+      ..add(DiagnosticsProperty('ref', ref))
+      ..add(DiagnosticsProperty('eval', eval))
+      ..add(
+          DiagnosticsProperty('isDefinedByDependency', isDefinedByDependency));
+  }
+
+  @override
+  bool operator ==(dynamic other) {
+    return identical(this, other) ||
+        (other.runtimeType == runtimeType &&
+            other is _ObjectField &&
+            const DeepCollectionEquality().equals(other.name, name) &&
+            const DeepCollectionEquality().equals(other.isFinal, isFinal) &&
+            const DeepCollectionEquality().equals(other.ownerName, ownerName) &&
+            const DeepCollectionEquality().equals(other.ownerUri, ownerUri) &&
+            const DeepCollectionEquality().equals(other.ref, ref) &&
+            const DeepCollectionEquality().equals(other.eval, eval) &&
+            const DeepCollectionEquality()
+                .equals(other.isDefinedByDependency, isDefinedByDependency));
+  }
+
+  @override
+  int get hashCode => Object.hash(
+      runtimeType,
+      const DeepCollectionEquality().hash(name),
+      const DeepCollectionEquality().hash(isFinal),
+      const DeepCollectionEquality().hash(ownerName),
+      const DeepCollectionEquality().hash(ownerUri),
+      const DeepCollectionEquality().hash(ref),
+      const DeepCollectionEquality().hash(eval),
+      const DeepCollectionEquality().hash(isDefinedByDependency));
+
+  @JsonKey(ignore: true)
+  @override
+  _$ObjectFieldCopyWith<_ObjectField> get copyWith =>
+      __$ObjectFieldCopyWithImpl<_ObjectField>(this, _$identity);
+}
+
+abstract class _ObjectField extends ObjectField {
+  factory _ObjectField(
+      {required String name,
+      required bool isFinal,
+      required String ownerName,
+      required String ownerUri,
+      required Result<InstanceRef> ref,
+      required EvalOnDartLibrary eval,
+      required bool isDefinedByDependency}) = _$_ObjectField;
+  _ObjectField._() : super._();
+
+  @override
+  String get name;
+  @override
+  bool get isFinal;
+  @override
+  String get ownerName;
+  @override
+  String get ownerUri;
+  @override
+  Result<InstanceRef> get ref;
+  @override
+
+  /// An [EvalOnDartLibrary] that can access this field from the owner object
+  EvalOnDartLibrary get eval;
+  @override
+
+  /// Whether this field was defined by the inspected app or by one of its dependencies
+  ///
+  /// This is used by the UI to hide variables that are not useful for the user.
+  bool get isDefinedByDependency;
+  @override
+  @JsonKey(ignore: true)
+  _$ObjectFieldCopyWith<_ObjectField> get copyWith =>
+      throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+class _$InstanceDetailsTearOff {
+  const _$InstanceDetailsTearOff();
+
+  NullInstance nill({required Setter? setter}) {
+    return NullInstance(
+      setter: setter,
+    );
+  }
+
+  BoolInstance boolean(String displayString,
+      {required String instanceRefId, required Setter? setter}) {
+    return BoolInstance(
+      displayString,
+      instanceRefId: instanceRefId,
+      setter: setter,
+    );
+  }
+
+  NumInstance number(String displayString,
+      {required String instanceRefId, required Setter? setter}) {
+    return NumInstance(
+      displayString,
+      instanceRefId: instanceRefId,
+      setter: setter,
+    );
+  }
+
+  StringInstance string(String displayString,
+      {required String instanceRefId, required Setter? setter}) {
+    return StringInstance(
+      displayString,
+      instanceRefId: instanceRefId,
+      setter: setter,
+    );
+  }
+
+  MapInstance map(List<InstanceDetails> keys,
+      {required int hash,
+      required String instanceRefId,
+      required Setter? setter}) {
+    return MapInstance(
+      keys,
+      hash: hash,
+      instanceRefId: instanceRefId,
+      setter: setter,
+    );
+  }
+
+  ListInstance list(
+      {required int length,
+      required int hash,
+      required String instanceRefId,
+      required Setter? setter}) {
+    return ListInstance(
+      length: length,
+      hash: hash,
+      instanceRefId: instanceRefId,
+      setter: setter,
+    );
+  }
+
+  ObjectInstance object(List<ObjectField> fields,
+      {required String type,
+      required int hash,
+      required String instanceRefId,
+      required Setter? setter,
+      required EvalOnDartLibrary evalForInstance}) {
+    return ObjectInstance(
+      fields,
+      type: type,
+      hash: hash,
+      instanceRefId: instanceRefId,
+      setter: setter,
+      evalForInstance: evalForInstance,
+    );
+  }
+
+  EnumInstance enumeration(
+      {required String type,
+      required String value,
+      required Setter? setter,
+      required String instanceRefId}) {
+    return EnumInstance(
+      type: type,
+      value: value,
+      setter: setter,
+      instanceRefId: instanceRefId,
+    );
+  }
+}
+
+/// @nodoc
+const $InstanceDetails = _$InstanceDetailsTearOff();
+
+/// @nodoc
+mixin _$InstanceDetails {
+  Setter? get setter => throw _privateConstructorUsedError;
+
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function(Setter? setter) nill,
+    required TResult Function(
+            String displayString, String instanceRefId, Setter? setter)
+        boolean,
+    required TResult Function(
+            String displayString, String instanceRefId, Setter? setter)
+        number,
+    required TResult Function(
+            String displayString, String instanceRefId, Setter? setter)
+        string,
+    required TResult Function(List<InstanceDetails> keys, int hash,
+            String instanceRefId, Setter? setter)
+        map,
+    required TResult Function(
+            int length, int hash, String instanceRefId, Setter? setter)
+        list,
+    required TResult Function(
+            List<ObjectField> fields,
+            String type,
+            int hash,
+            String instanceRefId,
+            Setter? setter,
+            EvalOnDartLibrary evalForInstance)
+        object,
+    required TResult Function(
+            String type, String value, Setter? setter, String instanceRefId)
+        enumeration,
+  }) =>
+      throw _privateConstructorUsedError;
+  @optionalTypeArgs
+  TResult? whenOrNull<TResult extends Object?>({
+    TResult Function(Setter? setter)? nill,
+    TResult Function(
+            String displayString, String instanceRefId, Setter? setter)?
+        boolean,
+    TResult Function(
+            String displayString, String instanceRefId, Setter? setter)?
+        number,
+    TResult Function(
+            String displayString, String instanceRefId, Setter? setter)?
+        string,
+    TResult Function(List<InstanceDetails> keys, int hash, String instanceRefId,
+            Setter? setter)?
+        map,
+    TResult Function(
+            int length, int hash, String instanceRefId, Setter? setter)?
+        list,
+    TResult Function(
+            List<ObjectField> fields,
+            String type,
+            int hash,
+            String instanceRefId,
+            Setter? setter,
+            EvalOnDartLibrary evalForInstance)?
+        object,
+    TResult Function(
+            String type, String value, Setter? setter, String instanceRefId)?
+        enumeration,
+  }) =>
+      throw _privateConstructorUsedError;
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function(Setter? setter)? nill,
+    TResult Function(
+            String displayString, String instanceRefId, Setter? setter)?
+        boolean,
+    TResult Function(
+            String displayString, String instanceRefId, Setter? setter)?
+        number,
+    TResult Function(
+            String displayString, String instanceRefId, Setter? setter)?
+        string,
+    TResult Function(List<InstanceDetails> keys, int hash, String instanceRefId,
+            Setter? setter)?
+        map,
+    TResult Function(
+            int length, int hash, String instanceRefId, Setter? setter)?
+        list,
+    TResult Function(
+            List<ObjectField> fields,
+            String type,
+            int hash,
+            String instanceRefId,
+            Setter? setter,
+            EvalOnDartLibrary evalForInstance)?
+        object,
+    TResult Function(
+            String type, String value, Setter? setter, String instanceRefId)?
+        enumeration,
+    required TResult orElse(),
+  }) =>
+      throw _privateConstructorUsedError;
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(NullInstance value) nill,
+    required TResult Function(BoolInstance value) boolean,
+    required TResult Function(NumInstance value) number,
+    required TResult Function(StringInstance value) string,
+    required TResult Function(MapInstance value) map,
+    required TResult Function(ListInstance value) list,
+    required TResult Function(ObjectInstance value) object,
+    required TResult Function(EnumInstance value) enumeration,
+  }) =>
+      throw _privateConstructorUsedError;
+  @optionalTypeArgs
+  TResult? mapOrNull<TResult extends Object?>({
+    TResult Function(NullInstance value)? nill,
+    TResult Function(BoolInstance value)? boolean,
+    TResult Function(NumInstance value)? number,
+    TResult Function(StringInstance value)? string,
+    TResult Function(MapInstance value)? map,
+    TResult Function(ListInstance value)? list,
+    TResult Function(ObjectInstance value)? object,
+    TResult Function(EnumInstance value)? enumeration,
+  }) =>
+      throw _privateConstructorUsedError;
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(NullInstance value)? nill,
+    TResult Function(BoolInstance value)? boolean,
+    TResult Function(NumInstance value)? number,
+    TResult Function(StringInstance value)? string,
+    TResult Function(MapInstance value)? map,
+    TResult Function(ListInstance value)? list,
+    TResult Function(ObjectInstance value)? object,
+    TResult Function(EnumInstance value)? enumeration,
+    required TResult orElse(),
+  }) =>
+      throw _privateConstructorUsedError;
+
+  @JsonKey(ignore: true)
+  $InstanceDetailsCopyWith<InstanceDetails> get copyWith =>
+      throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class $InstanceDetailsCopyWith<$Res> {
+  factory $InstanceDetailsCopyWith(
+          InstanceDetails value, $Res Function(InstanceDetails) then) =
+      _$InstanceDetailsCopyWithImpl<$Res>;
+  $Res call({Setter? setter});
+}
+
+/// @nodoc
+class _$InstanceDetailsCopyWithImpl<$Res>
+    implements $InstanceDetailsCopyWith<$Res> {
+  _$InstanceDetailsCopyWithImpl(this._value, this._then);
+
+  final InstanceDetails _value;
+  // ignore: unused_field
+  final $Res Function(InstanceDetails) _then;
+
+  @override
+  $Res call({
+    Object? setter = freezed,
+  }) {
+    return _then(_value.copyWith(
+      setter: setter == freezed
+          ? _value.setter
+          : setter // ignore: cast_nullable_to_non_nullable
+              as Setter?,
+    ));
+  }
+}
+
+/// @nodoc
+abstract class $NullInstanceCopyWith<$Res>
+    implements $InstanceDetailsCopyWith<$Res> {
+  factory $NullInstanceCopyWith(
+          NullInstance value, $Res Function(NullInstance) then) =
+      _$NullInstanceCopyWithImpl<$Res>;
+  @override
+  $Res call({Setter? setter});
+}
+
+/// @nodoc
+class _$NullInstanceCopyWithImpl<$Res>
+    extends _$InstanceDetailsCopyWithImpl<$Res>
+    implements $NullInstanceCopyWith<$Res> {
+  _$NullInstanceCopyWithImpl(
+      NullInstance _value, $Res Function(NullInstance) _then)
+      : super(_value, (v) => _then(v as NullInstance));
+
+  @override
+  NullInstance get _value => super._value as NullInstance;
+
+  @override
+  $Res call({
+    Object? setter = freezed,
+  }) {
+    return _then(NullInstance(
+      setter: setter == freezed
+          ? _value.setter
+          : setter // ignore: cast_nullable_to_non_nullable
+              as Setter?,
+    ));
+  }
+}
+
+/// @nodoc
+
+class _$NullInstance extends NullInstance with DiagnosticableTreeMixin {
+  _$NullInstance({required this.setter}) : super._();
+
+  @override
+  final Setter? setter;
+
+  @override
+  String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) {
+    return 'InstanceDetails.nill(setter: $setter)';
+  }
+
+  @override
+  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
+    super.debugFillProperties(properties);
+    properties
+      ..add(DiagnosticsProperty('type', 'InstanceDetails.nill'))
+      ..add(DiagnosticsProperty('setter', setter));
+  }
+
+  @override
+  bool operator ==(dynamic other) {
+    return identical(this, other) ||
+        (other.runtimeType == runtimeType &&
+            other is NullInstance &&
+            (identical(other.setter, setter) || other.setter == setter));
+  }
+
+  @override
+  int get hashCode => Object.hash(runtimeType, setter);
+
+  @JsonKey(ignore: true)
+  @override
+  $NullInstanceCopyWith<NullInstance> get copyWith =>
+      _$NullInstanceCopyWithImpl<NullInstance>(this, _$identity);
+
+  @override
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function(Setter? setter) nill,
+    required TResult Function(
+            String displayString, String instanceRefId, Setter? setter)
+        boolean,
+    required TResult Function(
+            String displayString, String instanceRefId, Setter? setter)
+        number,
+    required TResult Function(
+            String displayString, String instanceRefId, Setter? setter)
+        string,
+    required TResult Function(List<InstanceDetails> keys, int hash,
+            String instanceRefId, Setter? setter)
+        map,
+    required TResult Function(
+            int length, int hash, String instanceRefId, Setter? setter)
+        list,
+    required TResult Function(
+            List<ObjectField> fields,
+            String type,
+            int hash,
+            String instanceRefId,
+            Setter? setter,
+            EvalOnDartLibrary evalForInstance)
+        object,
+    required TResult Function(
+            String type, String value, Setter? setter, String instanceRefId)
+        enumeration,
+  }) {
+    return nill(setter);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult? whenOrNull<TResult extends Object?>({
+    TResult Function(Setter? setter)? nill,
+    TResult Function(
+            String displayString, String instanceRefId, Setter? setter)?
+        boolean,
+    TResult Function(
+            String displayString, String instanceRefId, Setter? setter)?
+        number,
+    TResult Function(
+            String displayString, String instanceRefId, Setter? setter)?
+        string,
+    TResult Function(List<InstanceDetails> keys, int hash, String instanceRefId,
+            Setter? setter)?
+        map,
+    TResult Function(
+            int length, int hash, String instanceRefId, Setter? setter)?
+        list,
+    TResult Function(
+            List<ObjectField> fields,
+            String type,
+            int hash,
+            String instanceRefId,
+            Setter? setter,
+            EvalOnDartLibrary evalForInstance)?
+        object,
+    TResult Function(
+            String type, String value, Setter? setter, String instanceRefId)?
+        enumeration,
+  }) {
+    return nill?.call(setter);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function(Setter? setter)? nill,
+    TResult Function(
+            String displayString, String instanceRefId, Setter? setter)?
+        boolean,
+    TResult Function(
+            String displayString, String instanceRefId, Setter? setter)?
+        number,
+    TResult Function(
+            String displayString, String instanceRefId, Setter? setter)?
+        string,
+    TResult Function(List<InstanceDetails> keys, int hash, String instanceRefId,
+            Setter? setter)?
+        map,
+    TResult Function(
+            int length, int hash, String instanceRefId, Setter? setter)?
+        list,
+    TResult Function(
+            List<ObjectField> fields,
+            String type,
+            int hash,
+            String instanceRefId,
+            Setter? setter,
+            EvalOnDartLibrary evalForInstance)?
+        object,
+    TResult Function(
+            String type, String value, Setter? setter, String instanceRefId)?
+        enumeration,
+    required TResult orElse(),
+  }) {
+    if (nill != null) {
+      return nill(setter);
+    }
+    return orElse();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(NullInstance value) nill,
+    required TResult Function(BoolInstance value) boolean,
+    required TResult Function(NumInstance value) number,
+    required TResult Function(StringInstance value) string,
+    required TResult Function(MapInstance value) map,
+    required TResult Function(ListInstance value) list,
+    required TResult Function(ObjectInstance value) object,
+    required TResult Function(EnumInstance value) enumeration,
+  }) {
+    return nill(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult? mapOrNull<TResult extends Object?>({
+    TResult Function(NullInstance value)? nill,
+    TResult Function(BoolInstance value)? boolean,
+    TResult Function(NumInstance value)? number,
+    TResult Function(StringInstance value)? string,
+    TResult Function(MapInstance value)? map,
+    TResult Function(ListInstance value)? list,
+    TResult Function(ObjectInstance value)? object,
+    TResult Function(EnumInstance value)? enumeration,
+  }) {
+    return nill?.call(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(NullInstance value)? nill,
+    TResult Function(BoolInstance value)? boolean,
+    TResult Function(NumInstance value)? number,
+    TResult Function(StringInstance value)? string,
+    TResult Function(MapInstance value)? map,
+    TResult Function(ListInstance value)? list,
+    TResult Function(ObjectInstance value)? object,
+    TResult Function(EnumInstance value)? enumeration,
+    required TResult orElse(),
+  }) {
+    if (nill != null) {
+      return nill(this);
+    }
+    return orElse();
+  }
+}
+
+abstract class NullInstance extends InstanceDetails {
+  factory NullInstance({required Setter? setter}) = _$NullInstance;
+  NullInstance._() : super._();
+
+  @override
+  Setter? get setter;
+  @override
+  @JsonKey(ignore: true)
+  $NullInstanceCopyWith<NullInstance> get copyWith =>
+      throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class $BoolInstanceCopyWith<$Res>
+    implements $InstanceDetailsCopyWith<$Res> {
+  factory $BoolInstanceCopyWith(
+          BoolInstance value, $Res Function(BoolInstance) then) =
+      _$BoolInstanceCopyWithImpl<$Res>;
+  @override
+  $Res call({String displayString, String instanceRefId, Setter? setter});
+}
+
+/// @nodoc
+class _$BoolInstanceCopyWithImpl<$Res>
+    extends _$InstanceDetailsCopyWithImpl<$Res>
+    implements $BoolInstanceCopyWith<$Res> {
+  _$BoolInstanceCopyWithImpl(
+      BoolInstance _value, $Res Function(BoolInstance) _then)
+      : super(_value, (v) => _then(v as BoolInstance));
+
+  @override
+  BoolInstance get _value => super._value as BoolInstance;
+
+  @override
+  $Res call({
+    Object? displayString = freezed,
+    Object? instanceRefId = freezed,
+    Object? setter = freezed,
+  }) {
+    return _then(BoolInstance(
+      displayString == freezed
+          ? _value.displayString
+          : displayString // ignore: cast_nullable_to_non_nullable
+              as String,
+      instanceRefId: instanceRefId == freezed
+          ? _value.instanceRefId
+          : instanceRefId // ignore: cast_nullable_to_non_nullable
+              as String,
+      setter: setter == freezed
+          ? _value.setter
+          : setter // ignore: cast_nullable_to_non_nullable
+              as Setter?,
+    ));
+  }
+}
+
+/// @nodoc
+
+class _$BoolInstance extends BoolInstance with DiagnosticableTreeMixin {
+  _$BoolInstance(this.displayString,
+      {required this.instanceRefId, required this.setter})
+      : super._();
+
+  @override
+  final String displayString;
+  @override
+  final String instanceRefId;
+  @override
+  final Setter? setter;
+
+  @override
+  String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) {
+    return 'InstanceDetails.boolean(displayString: $displayString, instanceRefId: $instanceRefId, setter: $setter)';
+  }
+
+  @override
+  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
+    super.debugFillProperties(properties);
+    properties
+      ..add(DiagnosticsProperty('type', 'InstanceDetails.boolean'))
+      ..add(DiagnosticsProperty('displayString', displayString))
+      ..add(DiagnosticsProperty('instanceRefId', instanceRefId))
+      ..add(DiagnosticsProperty('setter', setter));
+  }
+
+  @override
+  bool operator ==(dynamic other) {
+    return identical(this, other) ||
+        (other.runtimeType == runtimeType &&
+            other is BoolInstance &&
+            const DeepCollectionEquality()
+                .equals(other.displayString, displayString) &&
+            const DeepCollectionEquality()
+                .equals(other.instanceRefId, instanceRefId) &&
+            (identical(other.setter, setter) || other.setter == setter));
+  }
+
+  @override
+  int get hashCode => Object.hash(
+      runtimeType,
+      const DeepCollectionEquality().hash(displayString),
+      const DeepCollectionEquality().hash(instanceRefId),
+      setter);
+
+  @JsonKey(ignore: true)
+  @override
+  $BoolInstanceCopyWith<BoolInstance> get copyWith =>
+      _$BoolInstanceCopyWithImpl<BoolInstance>(this, _$identity);
+
+  @override
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function(Setter? setter) nill,
+    required TResult Function(
+            String displayString, String instanceRefId, Setter? setter)
+        boolean,
+    required TResult Function(
+            String displayString, String instanceRefId, Setter? setter)
+        number,
+    required TResult Function(
+            String displayString, String instanceRefId, Setter? setter)
+        string,
+    required TResult Function(List<InstanceDetails> keys, int hash,
+            String instanceRefId, Setter? setter)
+        map,
+    required TResult Function(
+            int length, int hash, String instanceRefId, Setter? setter)
+        list,
+    required TResult Function(
+            List<ObjectField> fields,
+            String type,
+            int hash,
+            String instanceRefId,
+            Setter? setter,
+            EvalOnDartLibrary evalForInstance)
+        object,
+    required TResult Function(
+            String type, String value, Setter? setter, String instanceRefId)
+        enumeration,
+  }) {
+    return boolean(displayString, instanceRefId, setter);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult? whenOrNull<TResult extends Object?>({
+    TResult Function(Setter? setter)? nill,
+    TResult Function(
+            String displayString, String instanceRefId, Setter? setter)?
+        boolean,
+    TResult Function(
+            String displayString, String instanceRefId, Setter? setter)?
+        number,
+    TResult Function(
+            String displayString, String instanceRefId, Setter? setter)?
+        string,
+    TResult Function(List<InstanceDetails> keys, int hash, String instanceRefId,
+            Setter? setter)?
+        map,
+    TResult Function(
+            int length, int hash, String instanceRefId, Setter? setter)?
+        list,
+    TResult Function(
+            List<ObjectField> fields,
+            String type,
+            int hash,
+            String instanceRefId,
+            Setter? setter,
+            EvalOnDartLibrary evalForInstance)?
+        object,
+    TResult Function(
+            String type, String value, Setter? setter, String instanceRefId)?
+        enumeration,
+  }) {
+    return boolean?.call(displayString, instanceRefId, setter);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function(Setter? setter)? nill,
+    TResult Function(
+            String displayString, String instanceRefId, Setter? setter)?
+        boolean,
+    TResult Function(
+            String displayString, String instanceRefId, Setter? setter)?
+        number,
+    TResult Function(
+            String displayString, String instanceRefId, Setter? setter)?
+        string,
+    TResult Function(List<InstanceDetails> keys, int hash, String instanceRefId,
+            Setter? setter)?
+        map,
+    TResult Function(
+            int length, int hash, String instanceRefId, Setter? setter)?
+        list,
+    TResult Function(
+            List<ObjectField> fields,
+            String type,
+            int hash,
+            String instanceRefId,
+            Setter? setter,
+            EvalOnDartLibrary evalForInstance)?
+        object,
+    TResult Function(
+            String type, String value, Setter? setter, String instanceRefId)?
+        enumeration,
+    required TResult orElse(),
+  }) {
+    if (boolean != null) {
+      return boolean(displayString, instanceRefId, setter);
+    }
+    return orElse();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(NullInstance value) nill,
+    required TResult Function(BoolInstance value) boolean,
+    required TResult Function(NumInstance value) number,
+    required TResult Function(StringInstance value) string,
+    required TResult Function(MapInstance value) map,
+    required TResult Function(ListInstance value) list,
+    required TResult Function(ObjectInstance value) object,
+    required TResult Function(EnumInstance value) enumeration,
+  }) {
+    return boolean(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult? mapOrNull<TResult extends Object?>({
+    TResult Function(NullInstance value)? nill,
+    TResult Function(BoolInstance value)? boolean,
+    TResult Function(NumInstance value)? number,
+    TResult Function(StringInstance value)? string,
+    TResult Function(MapInstance value)? map,
+    TResult Function(ListInstance value)? list,
+    TResult Function(ObjectInstance value)? object,
+    TResult Function(EnumInstance value)? enumeration,
+  }) {
+    return boolean?.call(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(NullInstance value)? nill,
+    TResult Function(BoolInstance value)? boolean,
+    TResult Function(NumInstance value)? number,
+    TResult Function(StringInstance value)? string,
+    TResult Function(MapInstance value)? map,
+    TResult Function(ListInstance value)? list,
+    TResult Function(ObjectInstance value)? object,
+    TResult Function(EnumInstance value)? enumeration,
+    required TResult orElse(),
+  }) {
+    if (boolean != null) {
+      return boolean(this);
+    }
+    return orElse();
+  }
+}
+
+abstract class BoolInstance extends InstanceDetails {
+  factory BoolInstance(String displayString,
+      {required String instanceRefId,
+      required Setter? setter}) = _$BoolInstance;
+  BoolInstance._() : super._();
+
+  String get displayString;
+  String get instanceRefId;
+  @override
+  Setter? get setter;
+  @override
+  @JsonKey(ignore: true)
+  $BoolInstanceCopyWith<BoolInstance> get copyWith =>
+      throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class $NumInstanceCopyWith<$Res>
+    implements $InstanceDetailsCopyWith<$Res> {
+  factory $NumInstanceCopyWith(
+          NumInstance value, $Res Function(NumInstance) then) =
+      _$NumInstanceCopyWithImpl<$Res>;
+  @override
+  $Res call({String displayString, String instanceRefId, Setter? setter});
+}
+
+/// @nodoc
+class _$NumInstanceCopyWithImpl<$Res>
+    extends _$InstanceDetailsCopyWithImpl<$Res>
+    implements $NumInstanceCopyWith<$Res> {
+  _$NumInstanceCopyWithImpl(
+      NumInstance _value, $Res Function(NumInstance) _then)
+      : super(_value, (v) => _then(v as NumInstance));
+
+  @override
+  NumInstance get _value => super._value as NumInstance;
+
+  @override
+  $Res call({
+    Object? displayString = freezed,
+    Object? instanceRefId = freezed,
+    Object? setter = freezed,
+  }) {
+    return _then(NumInstance(
+      displayString == freezed
+          ? _value.displayString
+          : displayString // ignore: cast_nullable_to_non_nullable
+              as String,
+      instanceRefId: instanceRefId == freezed
+          ? _value.instanceRefId
+          : instanceRefId // ignore: cast_nullable_to_non_nullable
+              as String,
+      setter: setter == freezed
+          ? _value.setter
+          : setter // ignore: cast_nullable_to_non_nullable
+              as Setter?,
+    ));
+  }
+}
+
+/// @nodoc
+
+class _$NumInstance extends NumInstance with DiagnosticableTreeMixin {
+  _$NumInstance(this.displayString,
+      {required this.instanceRefId, required this.setter})
+      : super._();
+
+  @override
+  final String displayString;
+  @override
+  final String instanceRefId;
+  @override
+  final Setter? setter;
+
+  @override
+  String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) {
+    return 'InstanceDetails.number(displayString: $displayString, instanceRefId: $instanceRefId, setter: $setter)';
+  }
+
+  @override
+  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
+    super.debugFillProperties(properties);
+    properties
+      ..add(DiagnosticsProperty('type', 'InstanceDetails.number'))
+      ..add(DiagnosticsProperty('displayString', displayString))
+      ..add(DiagnosticsProperty('instanceRefId', instanceRefId))
+      ..add(DiagnosticsProperty('setter', setter));
+  }
+
+  @override
+  bool operator ==(dynamic other) {
+    return identical(this, other) ||
+        (other.runtimeType == runtimeType &&
+            other is NumInstance &&
+            const DeepCollectionEquality()
+                .equals(other.displayString, displayString) &&
+            const DeepCollectionEquality()
+                .equals(other.instanceRefId, instanceRefId) &&
+            (identical(other.setter, setter) || other.setter == setter));
+  }
+
+  @override
+  int get hashCode => Object.hash(
+      runtimeType,
+      const DeepCollectionEquality().hash(displayString),
+      const DeepCollectionEquality().hash(instanceRefId),
+      setter);
+
+  @JsonKey(ignore: true)
+  @override
+  $NumInstanceCopyWith<NumInstance> get copyWith =>
+      _$NumInstanceCopyWithImpl<NumInstance>(this, _$identity);
+
+  @override
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function(Setter? setter) nill,
+    required TResult Function(
+            String displayString, String instanceRefId, Setter? setter)
+        boolean,
+    required TResult Function(
+            String displayString, String instanceRefId, Setter? setter)
+        number,
+    required TResult Function(
+            String displayString, String instanceRefId, Setter? setter)
+        string,
+    required TResult Function(List<InstanceDetails> keys, int hash,
+            String instanceRefId, Setter? setter)
+        map,
+    required TResult Function(
+            int length, int hash, String instanceRefId, Setter? setter)
+        list,
+    required TResult Function(
+            List<ObjectField> fields,
+            String type,
+            int hash,
+            String instanceRefId,
+            Setter? setter,
+            EvalOnDartLibrary evalForInstance)
+        object,
+    required TResult Function(
+            String type, String value, Setter? setter, String instanceRefId)
+        enumeration,
+  }) {
+    return number(displayString, instanceRefId, setter);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult? whenOrNull<TResult extends Object?>({
+    TResult Function(Setter? setter)? nill,
+    TResult Function(
+            String displayString, String instanceRefId, Setter? setter)?
+        boolean,
+    TResult Function(
+            String displayString, String instanceRefId, Setter? setter)?
+        number,
+    TResult Function(
+            String displayString, String instanceRefId, Setter? setter)?
+        string,
+    TResult Function(List<InstanceDetails> keys, int hash, String instanceRefId,
+            Setter? setter)?
+        map,
+    TResult Function(
+            int length, int hash, String instanceRefId, Setter? setter)?
+        list,
+    TResult Function(
+            List<ObjectField> fields,
+            String type,
+            int hash,
+            String instanceRefId,
+            Setter? setter,
+            EvalOnDartLibrary evalForInstance)?
+        object,
+    TResult Function(
+            String type, String value, Setter? setter, String instanceRefId)?
+        enumeration,
+  }) {
+    return number?.call(displayString, instanceRefId, setter);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function(Setter? setter)? nill,
+    TResult Function(
+            String displayString, String instanceRefId, Setter? setter)?
+        boolean,
+    TResult Function(
+            String displayString, String instanceRefId, Setter? setter)?
+        number,
+    TResult Function(
+            String displayString, String instanceRefId, Setter? setter)?
+        string,
+    TResult Function(List<InstanceDetails> keys, int hash, String instanceRefId,
+            Setter? setter)?
+        map,
+    TResult Function(
+            int length, int hash, String instanceRefId, Setter? setter)?
+        list,
+    TResult Function(
+            List<ObjectField> fields,
+            String type,
+            int hash,
+            String instanceRefId,
+            Setter? setter,
+            EvalOnDartLibrary evalForInstance)?
+        object,
+    TResult Function(
+            String type, String value, Setter? setter, String instanceRefId)?
+        enumeration,
+    required TResult orElse(),
+  }) {
+    if (number != null) {
+      return number(displayString, instanceRefId, setter);
+    }
+    return orElse();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(NullInstance value) nill,
+    required TResult Function(BoolInstance value) boolean,
+    required TResult Function(NumInstance value) number,
+    required TResult Function(StringInstance value) string,
+    required TResult Function(MapInstance value) map,
+    required TResult Function(ListInstance value) list,
+    required TResult Function(ObjectInstance value) object,
+    required TResult Function(EnumInstance value) enumeration,
+  }) {
+    return number(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult? mapOrNull<TResult extends Object?>({
+    TResult Function(NullInstance value)? nill,
+    TResult Function(BoolInstance value)? boolean,
+    TResult Function(NumInstance value)? number,
+    TResult Function(StringInstance value)? string,
+    TResult Function(MapInstance value)? map,
+    TResult Function(ListInstance value)? list,
+    TResult Function(ObjectInstance value)? object,
+    TResult Function(EnumInstance value)? enumeration,
+  }) {
+    return number?.call(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(NullInstance value)? nill,
+    TResult Function(BoolInstance value)? boolean,
+    TResult Function(NumInstance value)? number,
+    TResult Function(StringInstance value)? string,
+    TResult Function(MapInstance value)? map,
+    TResult Function(ListInstance value)? list,
+    TResult Function(ObjectInstance value)? object,
+    TResult Function(EnumInstance value)? enumeration,
+    required TResult orElse(),
+  }) {
+    if (number != null) {
+      return number(this);
+    }
+    return orElse();
+  }
+}
+
+abstract class NumInstance extends InstanceDetails {
+  factory NumInstance(String displayString,
+      {required String instanceRefId, required Setter? setter}) = _$NumInstance;
+  NumInstance._() : super._();
+
+  String get displayString;
+  String get instanceRefId;
+  @override
+  Setter? get setter;
+  @override
+  @JsonKey(ignore: true)
+  $NumInstanceCopyWith<NumInstance> get copyWith =>
+      throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class $StringInstanceCopyWith<$Res>
+    implements $InstanceDetailsCopyWith<$Res> {
+  factory $StringInstanceCopyWith(
+          StringInstance value, $Res Function(StringInstance) then) =
+      _$StringInstanceCopyWithImpl<$Res>;
+  @override
+  $Res call({String displayString, String instanceRefId, Setter? setter});
+}
+
+/// @nodoc
+class _$StringInstanceCopyWithImpl<$Res>
+    extends _$InstanceDetailsCopyWithImpl<$Res>
+    implements $StringInstanceCopyWith<$Res> {
+  _$StringInstanceCopyWithImpl(
+      StringInstance _value, $Res Function(StringInstance) _then)
+      : super(_value, (v) => _then(v as StringInstance));
+
+  @override
+  StringInstance get _value => super._value as StringInstance;
+
+  @override
+  $Res call({
+    Object? displayString = freezed,
+    Object? instanceRefId = freezed,
+    Object? setter = freezed,
+  }) {
+    return _then(StringInstance(
+      displayString == freezed
+          ? _value.displayString
+          : displayString // ignore: cast_nullable_to_non_nullable
+              as String,
+      instanceRefId: instanceRefId == freezed
+          ? _value.instanceRefId
+          : instanceRefId // ignore: cast_nullable_to_non_nullable
+              as String,
+      setter: setter == freezed
+          ? _value.setter
+          : setter // ignore: cast_nullable_to_non_nullable
+              as Setter?,
+    ));
+  }
+}
+
+/// @nodoc
+
+class _$StringInstance extends StringInstance with DiagnosticableTreeMixin {
+  _$StringInstance(this.displayString,
+      {required this.instanceRefId, required this.setter})
+      : super._();
+
+  @override
+  final String displayString;
+  @override
+  final String instanceRefId;
+  @override
+  final Setter? setter;
+
+  @override
+  String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) {
+    return 'InstanceDetails.string(displayString: $displayString, instanceRefId: $instanceRefId, setter: $setter)';
+  }
+
+  @override
+  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
+    super.debugFillProperties(properties);
+    properties
+      ..add(DiagnosticsProperty('type', 'InstanceDetails.string'))
+      ..add(DiagnosticsProperty('displayString', displayString))
+      ..add(DiagnosticsProperty('instanceRefId', instanceRefId))
+      ..add(DiagnosticsProperty('setter', setter));
+  }
+
+  @override
+  bool operator ==(dynamic other) {
+    return identical(this, other) ||
+        (other.runtimeType == runtimeType &&
+            other is StringInstance &&
+            const DeepCollectionEquality()
+                .equals(other.displayString, displayString) &&
+            const DeepCollectionEquality()
+                .equals(other.instanceRefId, instanceRefId) &&
+            (identical(other.setter, setter) || other.setter == setter));
+  }
+
+  @override
+  int get hashCode => Object.hash(
+      runtimeType,
+      const DeepCollectionEquality().hash(displayString),
+      const DeepCollectionEquality().hash(instanceRefId),
+      setter);
+
+  @JsonKey(ignore: true)
+  @override
+  $StringInstanceCopyWith<StringInstance> get copyWith =>
+      _$StringInstanceCopyWithImpl<StringInstance>(this, _$identity);
+
+  @override
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function(Setter? setter) nill,
+    required TResult Function(
+            String displayString, String instanceRefId, Setter? setter)
+        boolean,
+    required TResult Function(
+            String displayString, String instanceRefId, Setter? setter)
+        number,
+    required TResult Function(
+            String displayString, String instanceRefId, Setter? setter)
+        string,
+    required TResult Function(List<InstanceDetails> keys, int hash,
+            String instanceRefId, Setter? setter)
+        map,
+    required TResult Function(
+            int length, int hash, String instanceRefId, Setter? setter)
+        list,
+    required TResult Function(
+            List<ObjectField> fields,
+            String type,
+            int hash,
+            String instanceRefId,
+            Setter? setter,
+            EvalOnDartLibrary evalForInstance)
+        object,
+    required TResult Function(
+            String type, String value, Setter? setter, String instanceRefId)
+        enumeration,
+  }) {
+    return string(displayString, instanceRefId, setter);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult? whenOrNull<TResult extends Object?>({
+    TResult Function(Setter? setter)? nill,
+    TResult Function(
+            String displayString, String instanceRefId, Setter? setter)?
+        boolean,
+    TResult Function(
+            String displayString, String instanceRefId, Setter? setter)?
+        number,
+    TResult Function(
+            String displayString, String instanceRefId, Setter? setter)?
+        string,
+    TResult Function(List<InstanceDetails> keys, int hash, String instanceRefId,
+            Setter? setter)?
+        map,
+    TResult Function(
+            int length, int hash, String instanceRefId, Setter? setter)?
+        list,
+    TResult Function(
+            List<ObjectField> fields,
+            String type,
+            int hash,
+            String instanceRefId,
+            Setter? setter,
+            EvalOnDartLibrary evalForInstance)?
+        object,
+    TResult Function(
+            String type, String value, Setter? setter, String instanceRefId)?
+        enumeration,
+  }) {
+    return string?.call(displayString, instanceRefId, setter);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function(Setter? setter)? nill,
+    TResult Function(
+            String displayString, String instanceRefId, Setter? setter)?
+        boolean,
+    TResult Function(
+            String displayString, String instanceRefId, Setter? setter)?
+        number,
+    TResult Function(
+            String displayString, String instanceRefId, Setter? setter)?
+        string,
+    TResult Function(List<InstanceDetails> keys, int hash, String instanceRefId,
+            Setter? setter)?
+        map,
+    TResult Function(
+            int length, int hash, String instanceRefId, Setter? setter)?
+        list,
+    TResult Function(
+            List<ObjectField> fields,
+            String type,
+            int hash,
+            String instanceRefId,
+            Setter? setter,
+            EvalOnDartLibrary evalForInstance)?
+        object,
+    TResult Function(
+            String type, String value, Setter? setter, String instanceRefId)?
+        enumeration,
+    required TResult orElse(),
+  }) {
+    if (string != null) {
+      return string(displayString, instanceRefId, setter);
+    }
+    return orElse();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(NullInstance value) nill,
+    required TResult Function(BoolInstance value) boolean,
+    required TResult Function(NumInstance value) number,
+    required TResult Function(StringInstance value) string,
+    required TResult Function(MapInstance value) map,
+    required TResult Function(ListInstance value) list,
+    required TResult Function(ObjectInstance value) object,
+    required TResult Function(EnumInstance value) enumeration,
+  }) {
+    return string(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult? mapOrNull<TResult extends Object?>({
+    TResult Function(NullInstance value)? nill,
+    TResult Function(BoolInstance value)? boolean,
+    TResult Function(NumInstance value)? number,
+    TResult Function(StringInstance value)? string,
+    TResult Function(MapInstance value)? map,
+    TResult Function(ListInstance value)? list,
+    TResult Function(ObjectInstance value)? object,
+    TResult Function(EnumInstance value)? enumeration,
+  }) {
+    return string?.call(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(NullInstance value)? nill,
+    TResult Function(BoolInstance value)? boolean,
+    TResult Function(NumInstance value)? number,
+    TResult Function(StringInstance value)? string,
+    TResult Function(MapInstance value)? map,
+    TResult Function(ListInstance value)? list,
+    TResult Function(ObjectInstance value)? object,
+    TResult Function(EnumInstance value)? enumeration,
+    required TResult orElse(),
+  }) {
+    if (string != null) {
+      return string(this);
+    }
+    return orElse();
+  }
+}
+
+abstract class StringInstance extends InstanceDetails {
+  factory StringInstance(String displayString,
+      {required String instanceRefId,
+      required Setter? setter}) = _$StringInstance;
+  StringInstance._() : super._();
+
+  String get displayString;
+  String get instanceRefId;
+  @override
+  Setter? get setter;
+  @override
+  @JsonKey(ignore: true)
+  $StringInstanceCopyWith<StringInstance> get copyWith =>
+      throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class $MapInstanceCopyWith<$Res>
+    implements $InstanceDetailsCopyWith<$Res> {
+  factory $MapInstanceCopyWith(
+          MapInstance value, $Res Function(MapInstance) then) =
+      _$MapInstanceCopyWithImpl<$Res>;
+  @override
+  $Res call(
+      {List<InstanceDetails> keys,
+      int hash,
+      String instanceRefId,
+      Setter? setter});
+}
+
+/// @nodoc
+class _$MapInstanceCopyWithImpl<$Res>
+    extends _$InstanceDetailsCopyWithImpl<$Res>
+    implements $MapInstanceCopyWith<$Res> {
+  _$MapInstanceCopyWithImpl(
+      MapInstance _value, $Res Function(MapInstance) _then)
+      : super(_value, (v) => _then(v as MapInstance));
+
+  @override
+  MapInstance get _value => super._value as MapInstance;
+
+  @override
+  $Res call({
+    Object? keys = freezed,
+    Object? hash = freezed,
+    Object? instanceRefId = freezed,
+    Object? setter = freezed,
+  }) {
+    return _then(MapInstance(
+      keys == freezed
+          ? _value.keys
+          : keys // ignore: cast_nullable_to_non_nullable
+              as List<InstanceDetails>,
+      hash: hash == freezed
+          ? _value.hash
+          : hash // ignore: cast_nullable_to_non_nullable
+              as int,
+      instanceRefId: instanceRefId == freezed
+          ? _value.instanceRefId
+          : instanceRefId // ignore: cast_nullable_to_non_nullable
+              as String,
+      setter: setter == freezed
+          ? _value.setter
+          : setter // ignore: cast_nullable_to_non_nullable
+              as Setter?,
+    ));
+  }
+}
+
+/// @nodoc
+
+class _$MapInstance extends MapInstance with DiagnosticableTreeMixin {
+  _$MapInstance(this.keys,
+      {required this.hash, required this.instanceRefId, required this.setter})
+      : super._();
+
+  @override
+  final List<InstanceDetails> keys;
+  @override
+  final int hash;
+  @override
+  final String instanceRefId;
+  @override
+  final Setter? setter;
+
+  @override
+  String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) {
+    return 'InstanceDetails.map(keys: $keys, hash: $hash, instanceRefId: $instanceRefId, setter: $setter)';
+  }
+
+  @override
+  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
+    super.debugFillProperties(properties);
+    properties
+      ..add(DiagnosticsProperty('type', 'InstanceDetails.map'))
+      ..add(DiagnosticsProperty('keys', keys))
+      ..add(DiagnosticsProperty('hash', hash))
+      ..add(DiagnosticsProperty('instanceRefId', instanceRefId))
+      ..add(DiagnosticsProperty('setter', setter));
+  }
+
+  @override
+  bool operator ==(dynamic other) {
+    return identical(this, other) ||
+        (other.runtimeType == runtimeType &&
+            other is MapInstance &&
+            const DeepCollectionEquality().equals(other.keys, keys) &&
+            const DeepCollectionEquality().equals(other.hash, hash) &&
+            const DeepCollectionEquality()
+                .equals(other.instanceRefId, instanceRefId) &&
+            (identical(other.setter, setter) || other.setter == setter));
+  }
+
+  @override
+  int get hashCode => Object.hash(
+      runtimeType,
+      const DeepCollectionEquality().hash(keys),
+      const DeepCollectionEquality().hash(hash),
+      const DeepCollectionEquality().hash(instanceRefId),
+      setter);
+
+  @JsonKey(ignore: true)
+  @override
+  $MapInstanceCopyWith<MapInstance> get copyWith =>
+      _$MapInstanceCopyWithImpl<MapInstance>(this, _$identity);
+
+  @override
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function(Setter? setter) nill,
+    required TResult Function(
+            String displayString, String instanceRefId, Setter? setter)
+        boolean,
+    required TResult Function(
+            String displayString, String instanceRefId, Setter? setter)
+        number,
+    required TResult Function(
+            String displayString, String instanceRefId, Setter? setter)
+        string,
+    required TResult Function(List<InstanceDetails> keys, int hash,
+            String instanceRefId, Setter? setter)
+        map,
+    required TResult Function(
+            int length, int hash, String instanceRefId, Setter? setter)
+        list,
+    required TResult Function(
+            List<ObjectField> fields,
+            String type,
+            int hash,
+            String instanceRefId,
+            Setter? setter,
+            EvalOnDartLibrary evalForInstance)
+        object,
+    required TResult Function(
+            String type, String value, Setter? setter, String instanceRefId)
+        enumeration,
+  }) {
+    return map(keys, hash, instanceRefId, setter);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult? whenOrNull<TResult extends Object?>({
+    TResult Function(Setter? setter)? nill,
+    TResult Function(
+            String displayString, String instanceRefId, Setter? setter)?
+        boolean,
+    TResult Function(
+            String displayString, String instanceRefId, Setter? setter)?
+        number,
+    TResult Function(
+            String displayString, String instanceRefId, Setter? setter)?
+        string,
+    TResult Function(List<InstanceDetails> keys, int hash, String instanceRefId,
+            Setter? setter)?
+        map,
+    TResult Function(
+            int length, int hash, String instanceRefId, Setter? setter)?
+        list,
+    TResult Function(
+            List<ObjectField> fields,
+            String type,
+            int hash,
+            String instanceRefId,
+            Setter? setter,
+            EvalOnDartLibrary evalForInstance)?
+        object,
+    TResult Function(
+            String type, String value, Setter? setter, String instanceRefId)?
+        enumeration,
+  }) {
+    return map?.call(keys, hash, instanceRefId, setter);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function(Setter? setter)? nill,
+    TResult Function(
+            String displayString, String instanceRefId, Setter? setter)?
+        boolean,
+    TResult Function(
+            String displayString, String instanceRefId, Setter? setter)?
+        number,
+    TResult Function(
+            String displayString, String instanceRefId, Setter? setter)?
+        string,
+    TResult Function(List<InstanceDetails> keys, int hash, String instanceRefId,
+            Setter? setter)?
+        map,
+    TResult Function(
+            int length, int hash, String instanceRefId, Setter? setter)?
+        list,
+    TResult Function(
+            List<ObjectField> fields,
+            String type,
+            int hash,
+            String instanceRefId,
+            Setter? setter,
+            EvalOnDartLibrary evalForInstance)?
+        object,
+    TResult Function(
+            String type, String value, Setter? setter, String instanceRefId)?
+        enumeration,
+    required TResult orElse(),
+  }) {
+    if (map != null) {
+      return map(keys, hash, instanceRefId, setter);
+    }
+    return orElse();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(NullInstance value) nill,
+    required TResult Function(BoolInstance value) boolean,
+    required TResult Function(NumInstance value) number,
+    required TResult Function(StringInstance value) string,
+    required TResult Function(MapInstance value) map,
+    required TResult Function(ListInstance value) list,
+    required TResult Function(ObjectInstance value) object,
+    required TResult Function(EnumInstance value) enumeration,
+  }) {
+    return map(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult? mapOrNull<TResult extends Object?>({
+    TResult Function(NullInstance value)? nill,
+    TResult Function(BoolInstance value)? boolean,
+    TResult Function(NumInstance value)? number,
+    TResult Function(StringInstance value)? string,
+    TResult Function(MapInstance value)? map,
+    TResult Function(ListInstance value)? list,
+    TResult Function(ObjectInstance value)? object,
+    TResult Function(EnumInstance value)? enumeration,
+  }) {
+    return map?.call(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(NullInstance value)? nill,
+    TResult Function(BoolInstance value)? boolean,
+    TResult Function(NumInstance value)? number,
+    TResult Function(StringInstance value)? string,
+    TResult Function(MapInstance value)? map,
+    TResult Function(ListInstance value)? list,
+    TResult Function(ObjectInstance value)? object,
+    TResult Function(EnumInstance value)? enumeration,
+    required TResult orElse(),
+  }) {
+    if (map != null) {
+      return map(this);
+    }
+    return orElse();
+  }
+}
+
+abstract class MapInstance extends InstanceDetails {
+  factory MapInstance(List<InstanceDetails> keys,
+      {required int hash,
+      required String instanceRefId,
+      required Setter? setter}) = _$MapInstance;
+  MapInstance._() : super._();
+
+  List<InstanceDetails> get keys;
+  int get hash;
+  String get instanceRefId;
+  @override
+  Setter? get setter;
+  @override
+  @JsonKey(ignore: true)
+  $MapInstanceCopyWith<MapInstance> get copyWith =>
+      throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class $ListInstanceCopyWith<$Res>
+    implements $InstanceDetailsCopyWith<$Res> {
+  factory $ListInstanceCopyWith(
+          ListInstance value, $Res Function(ListInstance) then) =
+      _$ListInstanceCopyWithImpl<$Res>;
+  @override
+  $Res call({int length, int hash, String instanceRefId, Setter? setter});
+}
+
+/// @nodoc
+class _$ListInstanceCopyWithImpl<$Res>
+    extends _$InstanceDetailsCopyWithImpl<$Res>
+    implements $ListInstanceCopyWith<$Res> {
+  _$ListInstanceCopyWithImpl(
+      ListInstance _value, $Res Function(ListInstance) _then)
+      : super(_value, (v) => _then(v as ListInstance));
+
+  @override
+  ListInstance get _value => super._value as ListInstance;
+
+  @override
+  $Res call({
+    Object? length = freezed,
+    Object? hash = freezed,
+    Object? instanceRefId = freezed,
+    Object? setter = freezed,
+  }) {
+    return _then(ListInstance(
+      length: length == freezed
+          ? _value.length
+          : length // ignore: cast_nullable_to_non_nullable
+              as int,
+      hash: hash == freezed
+          ? _value.hash
+          : hash // ignore: cast_nullable_to_non_nullable
+              as int,
+      instanceRefId: instanceRefId == freezed
+          ? _value.instanceRefId
+          : instanceRefId // ignore: cast_nullable_to_non_nullable
+              as String,
+      setter: setter == freezed
+          ? _value.setter
+          : setter // ignore: cast_nullable_to_non_nullable
+              as Setter?,
+    ));
+  }
+}
+
+/// @nodoc
+
+class _$ListInstance extends ListInstance with DiagnosticableTreeMixin {
+  _$ListInstance(
+      {required this.length,
+      required this.hash,
+      required this.instanceRefId,
+      required this.setter})
+      : super._();
+
+  @override
+  final int length;
+  @override
+  final int hash;
+  @override
+  final String instanceRefId;
+  @override
+  final Setter? setter;
+
+  @override
+  String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) {
+    return 'InstanceDetails.list(length: $length, hash: $hash, instanceRefId: $instanceRefId, setter: $setter)';
+  }
+
+  @override
+  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
+    super.debugFillProperties(properties);
+    properties
+      ..add(DiagnosticsProperty('type', 'InstanceDetails.list'))
+      ..add(DiagnosticsProperty('length', length))
+      ..add(DiagnosticsProperty('hash', hash))
+      ..add(DiagnosticsProperty('instanceRefId', instanceRefId))
+      ..add(DiagnosticsProperty('setter', setter));
+  }
+
+  @override
+  bool operator ==(dynamic other) {
+    return identical(this, other) ||
+        (other.runtimeType == runtimeType &&
+            other is ListInstance &&
+            const DeepCollectionEquality().equals(other.length, length) &&
+            const DeepCollectionEquality().equals(other.hash, hash) &&
+            const DeepCollectionEquality()
+                .equals(other.instanceRefId, instanceRefId) &&
+            (identical(other.setter, setter) || other.setter == setter));
+  }
+
+  @override
+  int get hashCode => Object.hash(
+      runtimeType,
+      const DeepCollectionEquality().hash(length),
+      const DeepCollectionEquality().hash(hash),
+      const DeepCollectionEquality().hash(instanceRefId),
+      setter);
+
+  @JsonKey(ignore: true)
+  @override
+  $ListInstanceCopyWith<ListInstance> get copyWith =>
+      _$ListInstanceCopyWithImpl<ListInstance>(this, _$identity);
+
+  @override
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function(Setter? setter) nill,
+    required TResult Function(
+            String displayString, String instanceRefId, Setter? setter)
+        boolean,
+    required TResult Function(
+            String displayString, String instanceRefId, Setter? setter)
+        number,
+    required TResult Function(
+            String displayString, String instanceRefId, Setter? setter)
+        string,
+    required TResult Function(List<InstanceDetails> keys, int hash,
+            String instanceRefId, Setter? setter)
+        map,
+    required TResult Function(
+            int length, int hash, String instanceRefId, Setter? setter)
+        list,
+    required TResult Function(
+            List<ObjectField> fields,
+            String type,
+            int hash,
+            String instanceRefId,
+            Setter? setter,
+            EvalOnDartLibrary evalForInstance)
+        object,
+    required TResult Function(
+            String type, String value, Setter? setter, String instanceRefId)
+        enumeration,
+  }) {
+    return list(length, hash, instanceRefId, setter);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult? whenOrNull<TResult extends Object?>({
+    TResult Function(Setter? setter)? nill,
+    TResult Function(
+            String displayString, String instanceRefId, Setter? setter)?
+        boolean,
+    TResult Function(
+            String displayString, String instanceRefId, Setter? setter)?
+        number,
+    TResult Function(
+            String displayString, String instanceRefId, Setter? setter)?
+        string,
+    TResult Function(List<InstanceDetails> keys, int hash, String instanceRefId,
+            Setter? setter)?
+        map,
+    TResult Function(
+            int length, int hash, String instanceRefId, Setter? setter)?
+        list,
+    TResult Function(
+            List<ObjectField> fields,
+            String type,
+            int hash,
+            String instanceRefId,
+            Setter? setter,
+            EvalOnDartLibrary evalForInstance)?
+        object,
+    TResult Function(
+            String type, String value, Setter? setter, String instanceRefId)?
+        enumeration,
+  }) {
+    return list?.call(length, hash, instanceRefId, setter);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function(Setter? setter)? nill,
+    TResult Function(
+            String displayString, String instanceRefId, Setter? setter)?
+        boolean,
+    TResult Function(
+            String displayString, String instanceRefId, Setter? setter)?
+        number,
+    TResult Function(
+            String displayString, String instanceRefId, Setter? setter)?
+        string,
+    TResult Function(List<InstanceDetails> keys, int hash, String instanceRefId,
+            Setter? setter)?
+        map,
+    TResult Function(
+            int length, int hash, String instanceRefId, Setter? setter)?
+        list,
+    TResult Function(
+            List<ObjectField> fields,
+            String type,
+            int hash,
+            String instanceRefId,
+            Setter? setter,
+            EvalOnDartLibrary evalForInstance)?
+        object,
+    TResult Function(
+            String type, String value, Setter? setter, String instanceRefId)?
+        enumeration,
+    required TResult orElse(),
+  }) {
+    if (list != null) {
+      return list(length, hash, instanceRefId, setter);
+    }
+    return orElse();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(NullInstance value) nill,
+    required TResult Function(BoolInstance value) boolean,
+    required TResult Function(NumInstance value) number,
+    required TResult Function(StringInstance value) string,
+    required TResult Function(MapInstance value) map,
+    required TResult Function(ListInstance value) list,
+    required TResult Function(ObjectInstance value) object,
+    required TResult Function(EnumInstance value) enumeration,
+  }) {
+    return list(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult? mapOrNull<TResult extends Object?>({
+    TResult Function(NullInstance value)? nill,
+    TResult Function(BoolInstance value)? boolean,
+    TResult Function(NumInstance value)? number,
+    TResult Function(StringInstance value)? string,
+    TResult Function(MapInstance value)? map,
+    TResult Function(ListInstance value)? list,
+    TResult Function(ObjectInstance value)? object,
+    TResult Function(EnumInstance value)? enumeration,
+  }) {
+    return list?.call(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(NullInstance value)? nill,
+    TResult Function(BoolInstance value)? boolean,
+    TResult Function(NumInstance value)? number,
+    TResult Function(StringInstance value)? string,
+    TResult Function(MapInstance value)? map,
+    TResult Function(ListInstance value)? list,
+    TResult Function(ObjectInstance value)? object,
+    TResult Function(EnumInstance value)? enumeration,
+    required TResult orElse(),
+  }) {
+    if (list != null) {
+      return list(this);
+    }
+    return orElse();
+  }
+}
+
+abstract class ListInstance extends InstanceDetails {
+  factory ListInstance(
+      {required int length,
+      required int hash,
+      required String instanceRefId,
+      required Setter? setter}) = _$ListInstance;
+  ListInstance._() : super._();
+
+  int get length;
+  int get hash;
+  String get instanceRefId;
+  @override
+  Setter? get setter;
+  @override
+  @JsonKey(ignore: true)
+  $ListInstanceCopyWith<ListInstance> get copyWith =>
+      throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class $ObjectInstanceCopyWith<$Res>
+    implements $InstanceDetailsCopyWith<$Res> {
+  factory $ObjectInstanceCopyWith(
+          ObjectInstance value, $Res Function(ObjectInstance) then) =
+      _$ObjectInstanceCopyWithImpl<$Res>;
+  @override
+  $Res call(
+      {List<ObjectField> fields,
+      String type,
+      int hash,
+      String instanceRefId,
+      Setter? setter,
+      EvalOnDartLibrary evalForInstance});
+}
+
+/// @nodoc
+class _$ObjectInstanceCopyWithImpl<$Res>
+    extends _$InstanceDetailsCopyWithImpl<$Res>
+    implements $ObjectInstanceCopyWith<$Res> {
+  _$ObjectInstanceCopyWithImpl(
+      ObjectInstance _value, $Res Function(ObjectInstance) _then)
+      : super(_value, (v) => _then(v as ObjectInstance));
+
+  @override
+  ObjectInstance get _value => super._value as ObjectInstance;
+
+  @override
+  $Res call({
+    Object? fields = freezed,
+    Object? type = freezed,
+    Object? hash = freezed,
+    Object? instanceRefId = freezed,
+    Object? setter = freezed,
+    Object? evalForInstance = freezed,
+  }) {
+    return _then(ObjectInstance(
+      fields == freezed
+          ? _value.fields
+          : fields // ignore: cast_nullable_to_non_nullable
+              as List<ObjectField>,
+      type: type == freezed
+          ? _value.type
+          : type // ignore: cast_nullable_to_non_nullable
+              as String,
+      hash: hash == freezed
+          ? _value.hash
+          : hash // ignore: cast_nullable_to_non_nullable
+              as int,
+      instanceRefId: instanceRefId == freezed
+          ? _value.instanceRefId
+          : instanceRefId // ignore: cast_nullable_to_non_nullable
+              as String,
+      setter: setter == freezed
+          ? _value.setter
+          : setter // ignore: cast_nullable_to_non_nullable
+              as Setter?,
+      evalForInstance: evalForInstance == freezed
+          ? _value.evalForInstance
+          : evalForInstance // ignore: cast_nullable_to_non_nullable
+              as EvalOnDartLibrary,
+    ));
+  }
+}
+
+/// @nodoc
+
+class _$ObjectInstance extends ObjectInstance with DiagnosticableTreeMixin {
+  _$ObjectInstance(this.fields,
+      {required this.type,
+      required this.hash,
+      required this.instanceRefId,
+      required this.setter,
+      required this.evalForInstance})
+      : super._();
+
+  @override
+  final List<ObjectField> fields;
+  @override
+  final String type;
+  @override
+  final int hash;
+  @override
+  final String instanceRefId;
+  @override
+  final Setter? setter;
+  @override
+
+  /// An [EvalOnDartLibrary] associated with the library of this object
+  ///
+  /// This allows to edit private properties.
+  final EvalOnDartLibrary evalForInstance;
+
+  @override
+  String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) {
+    return 'InstanceDetails.object(fields: $fields, type: $type, hash: $hash, instanceRefId: $instanceRefId, setter: $setter, evalForInstance: $evalForInstance)';
+  }
+
+  @override
+  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
+    super.debugFillProperties(properties);
+    properties
+      ..add(DiagnosticsProperty('type', 'InstanceDetails.object'))
+      ..add(DiagnosticsProperty('fields', fields))
+      ..add(DiagnosticsProperty('type', type))
+      ..add(DiagnosticsProperty('hash', hash))
+      ..add(DiagnosticsProperty('instanceRefId', instanceRefId))
+      ..add(DiagnosticsProperty('setter', setter))
+      ..add(DiagnosticsProperty('evalForInstance', evalForInstance));
+  }
+
+  @override
+  bool operator ==(dynamic other) {
+    return identical(this, other) ||
+        (other.runtimeType == runtimeType &&
+            other is ObjectInstance &&
+            const DeepCollectionEquality().equals(other.fields, fields) &&
+            const DeepCollectionEquality().equals(other.type, type) &&
+            const DeepCollectionEquality().equals(other.hash, hash) &&
+            const DeepCollectionEquality()
+                .equals(other.instanceRefId, instanceRefId) &&
+            (identical(other.setter, setter) || other.setter == setter) &&
+            const DeepCollectionEquality()
+                .equals(other.evalForInstance, evalForInstance));
+  }
+
+  @override
+  int get hashCode => Object.hash(
+      runtimeType,
+      const DeepCollectionEquality().hash(fields),
+      const DeepCollectionEquality().hash(type),
+      const DeepCollectionEquality().hash(hash),
+      const DeepCollectionEquality().hash(instanceRefId),
+      setter,
+      const DeepCollectionEquality().hash(evalForInstance));
+
+  @JsonKey(ignore: true)
+  @override
+  $ObjectInstanceCopyWith<ObjectInstance> get copyWith =>
+      _$ObjectInstanceCopyWithImpl<ObjectInstance>(this, _$identity);
+
+  @override
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function(Setter? setter) nill,
+    required TResult Function(
+            String displayString, String instanceRefId, Setter? setter)
+        boolean,
+    required TResult Function(
+            String displayString, String instanceRefId, Setter? setter)
+        number,
+    required TResult Function(
+            String displayString, String instanceRefId, Setter? setter)
+        string,
+    required TResult Function(List<InstanceDetails> keys, int hash,
+            String instanceRefId, Setter? setter)
+        map,
+    required TResult Function(
+            int length, int hash, String instanceRefId, Setter? setter)
+        list,
+    required TResult Function(
+            List<ObjectField> fields,
+            String type,
+            int hash,
+            String instanceRefId,
+            Setter? setter,
+            EvalOnDartLibrary evalForInstance)
+        object,
+    required TResult Function(
+            String type, String value, Setter? setter, String instanceRefId)
+        enumeration,
+  }) {
+    return object(fields, type, hash, instanceRefId, setter, evalForInstance);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult? whenOrNull<TResult extends Object?>({
+    TResult Function(Setter? setter)? nill,
+    TResult Function(
+            String displayString, String instanceRefId, Setter? setter)?
+        boolean,
+    TResult Function(
+            String displayString, String instanceRefId, Setter? setter)?
+        number,
+    TResult Function(
+            String displayString, String instanceRefId, Setter? setter)?
+        string,
+    TResult Function(List<InstanceDetails> keys, int hash, String instanceRefId,
+            Setter? setter)?
+        map,
+    TResult Function(
+            int length, int hash, String instanceRefId, Setter? setter)?
+        list,
+    TResult Function(
+            List<ObjectField> fields,
+            String type,
+            int hash,
+            String instanceRefId,
+            Setter? setter,
+            EvalOnDartLibrary evalForInstance)?
+        object,
+    TResult Function(
+            String type, String value, Setter? setter, String instanceRefId)?
+        enumeration,
+  }) {
+    return object?.call(
+        fields, type, hash, instanceRefId, setter, evalForInstance);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function(Setter? setter)? nill,
+    TResult Function(
+            String displayString, String instanceRefId, Setter? setter)?
+        boolean,
+    TResult Function(
+            String displayString, String instanceRefId, Setter? setter)?
+        number,
+    TResult Function(
+            String displayString, String instanceRefId, Setter? setter)?
+        string,
+    TResult Function(List<InstanceDetails> keys, int hash, String instanceRefId,
+            Setter? setter)?
+        map,
+    TResult Function(
+            int length, int hash, String instanceRefId, Setter? setter)?
+        list,
+    TResult Function(
+            List<ObjectField> fields,
+            String type,
+            int hash,
+            String instanceRefId,
+            Setter? setter,
+            EvalOnDartLibrary evalForInstance)?
+        object,
+    TResult Function(
+            String type, String value, Setter? setter, String instanceRefId)?
+        enumeration,
+    required TResult orElse(),
+  }) {
+    if (object != null) {
+      return object(fields, type, hash, instanceRefId, setter, evalForInstance);
+    }
+    return orElse();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(NullInstance value) nill,
+    required TResult Function(BoolInstance value) boolean,
+    required TResult Function(NumInstance value) number,
+    required TResult Function(StringInstance value) string,
+    required TResult Function(MapInstance value) map,
+    required TResult Function(ListInstance value) list,
+    required TResult Function(ObjectInstance value) object,
+    required TResult Function(EnumInstance value) enumeration,
+  }) {
+    return object(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult? mapOrNull<TResult extends Object?>({
+    TResult Function(NullInstance value)? nill,
+    TResult Function(BoolInstance value)? boolean,
+    TResult Function(NumInstance value)? number,
+    TResult Function(StringInstance value)? string,
+    TResult Function(MapInstance value)? map,
+    TResult Function(ListInstance value)? list,
+    TResult Function(ObjectInstance value)? object,
+    TResult Function(EnumInstance value)? enumeration,
+  }) {
+    return object?.call(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(NullInstance value)? nill,
+    TResult Function(BoolInstance value)? boolean,
+    TResult Function(NumInstance value)? number,
+    TResult Function(StringInstance value)? string,
+    TResult Function(MapInstance value)? map,
+    TResult Function(ListInstance value)? list,
+    TResult Function(ObjectInstance value)? object,
+    TResult Function(EnumInstance value)? enumeration,
+    required TResult orElse(),
+  }) {
+    if (object != null) {
+      return object(this);
+    }
+    return orElse();
+  }
+}
+
+abstract class ObjectInstance extends InstanceDetails {
+  factory ObjectInstance(List<ObjectField> fields,
+      {required String type,
+      required int hash,
+      required String instanceRefId,
+      required Setter? setter,
+      required EvalOnDartLibrary evalForInstance}) = _$ObjectInstance;
+  ObjectInstance._() : super._();
+
+  List<ObjectField> get fields;
+  String get type;
+  int get hash;
+  String get instanceRefId;
+  @override
+  Setter? get setter;
+
+  /// An [EvalOnDartLibrary] associated with the library of this object
+  ///
+  /// This allows to edit private properties.
+  EvalOnDartLibrary get evalForInstance;
+  @override
+  @JsonKey(ignore: true)
+  $ObjectInstanceCopyWith<ObjectInstance> get copyWith =>
+      throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class $EnumInstanceCopyWith<$Res>
+    implements $InstanceDetailsCopyWith<$Res> {
+  factory $EnumInstanceCopyWith(
+          EnumInstance value, $Res Function(EnumInstance) then) =
+      _$EnumInstanceCopyWithImpl<$Res>;
+  @override
+  $Res call({String type, String value, Setter? setter, String instanceRefId});
+}
+
+/// @nodoc
+class _$EnumInstanceCopyWithImpl<$Res>
+    extends _$InstanceDetailsCopyWithImpl<$Res>
+    implements $EnumInstanceCopyWith<$Res> {
+  _$EnumInstanceCopyWithImpl(
+      EnumInstance _value, $Res Function(EnumInstance) _then)
+      : super(_value, (v) => _then(v as EnumInstance));
+
+  @override
+  EnumInstance get _value => super._value as EnumInstance;
+
+  @override
+  $Res call({
+    Object? type = freezed,
+    Object? value = freezed,
+    Object? setter = freezed,
+    Object? instanceRefId = freezed,
+  }) {
+    return _then(EnumInstance(
+      type: type == freezed
+          ? _value.type
+          : type // ignore: cast_nullable_to_non_nullable
+              as String,
+      value: value == freezed
+          ? _value.value
+          : value // ignore: cast_nullable_to_non_nullable
+              as String,
+      setter: setter == freezed
+          ? _value.setter
+          : setter // ignore: cast_nullable_to_non_nullable
+              as Setter?,
+      instanceRefId: instanceRefId == freezed
+          ? _value.instanceRefId
+          : instanceRefId // ignore: cast_nullable_to_non_nullable
+              as String,
+    ));
+  }
+}
+
+/// @nodoc
+
+class _$EnumInstance extends EnumInstance with DiagnosticableTreeMixin {
+  _$EnumInstance(
+      {required this.type,
+      required this.value,
+      required this.setter,
+      required this.instanceRefId})
+      : super._();
+
+  @override
+  final String type;
+  @override
+  final String value;
+  @override
+  final Setter? setter;
+  @override
+  final String instanceRefId;
+
+  @override
+  String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) {
+    return 'InstanceDetails.enumeration(type: $type, value: $value, setter: $setter, instanceRefId: $instanceRefId)';
+  }
+
+  @override
+  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
+    super.debugFillProperties(properties);
+    properties
+      ..add(DiagnosticsProperty('type', 'InstanceDetails.enumeration'))
+      ..add(DiagnosticsProperty('type', type))
+      ..add(DiagnosticsProperty('value', value))
+      ..add(DiagnosticsProperty('setter', setter))
+      ..add(DiagnosticsProperty('instanceRefId', instanceRefId));
+  }
+
+  @override
+  bool operator ==(dynamic other) {
+    return identical(this, other) ||
+        (other.runtimeType == runtimeType &&
+            other is EnumInstance &&
+            const DeepCollectionEquality().equals(other.type, type) &&
+            const DeepCollectionEquality().equals(other.value, value) &&
+            (identical(other.setter, setter) || other.setter == setter) &&
+            const DeepCollectionEquality()
+                .equals(other.instanceRefId, instanceRefId));
+  }
+
+  @override
+  int get hashCode => Object.hash(
+      runtimeType,
+      const DeepCollectionEquality().hash(type),
+      const DeepCollectionEquality().hash(value),
+      setter,
+      const DeepCollectionEquality().hash(instanceRefId));
+
+  @JsonKey(ignore: true)
+  @override
+  $EnumInstanceCopyWith<EnumInstance> get copyWith =>
+      _$EnumInstanceCopyWithImpl<EnumInstance>(this, _$identity);
+
+  @override
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function(Setter? setter) nill,
+    required TResult Function(
+            String displayString, String instanceRefId, Setter? setter)
+        boolean,
+    required TResult Function(
+            String displayString, String instanceRefId, Setter? setter)
+        number,
+    required TResult Function(
+            String displayString, String instanceRefId, Setter? setter)
+        string,
+    required TResult Function(List<InstanceDetails> keys, int hash,
+            String instanceRefId, Setter? setter)
+        map,
+    required TResult Function(
+            int length, int hash, String instanceRefId, Setter? setter)
+        list,
+    required TResult Function(
+            List<ObjectField> fields,
+            String type,
+            int hash,
+            String instanceRefId,
+            Setter? setter,
+            EvalOnDartLibrary evalForInstance)
+        object,
+    required TResult Function(
+            String type, String value, Setter? setter, String instanceRefId)
+        enumeration,
+  }) {
+    return enumeration(type, value, setter, instanceRefId);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult? whenOrNull<TResult extends Object?>({
+    TResult Function(Setter? setter)? nill,
+    TResult Function(
+            String displayString, String instanceRefId, Setter? setter)?
+        boolean,
+    TResult Function(
+            String displayString, String instanceRefId, Setter? setter)?
+        number,
+    TResult Function(
+            String displayString, String instanceRefId, Setter? setter)?
+        string,
+    TResult Function(List<InstanceDetails> keys, int hash, String instanceRefId,
+            Setter? setter)?
+        map,
+    TResult Function(
+            int length, int hash, String instanceRefId, Setter? setter)?
+        list,
+    TResult Function(
+            List<ObjectField> fields,
+            String type,
+            int hash,
+            String instanceRefId,
+            Setter? setter,
+            EvalOnDartLibrary evalForInstance)?
+        object,
+    TResult Function(
+            String type, String value, Setter? setter, String instanceRefId)?
+        enumeration,
+  }) {
+    return enumeration?.call(type, value, setter, instanceRefId);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function(Setter? setter)? nill,
+    TResult Function(
+            String displayString, String instanceRefId, Setter? setter)?
+        boolean,
+    TResult Function(
+            String displayString, String instanceRefId, Setter? setter)?
+        number,
+    TResult Function(
+            String displayString, String instanceRefId, Setter? setter)?
+        string,
+    TResult Function(List<InstanceDetails> keys, int hash, String instanceRefId,
+            Setter? setter)?
+        map,
+    TResult Function(
+            int length, int hash, String instanceRefId, Setter? setter)?
+        list,
+    TResult Function(
+            List<ObjectField> fields,
+            String type,
+            int hash,
+            String instanceRefId,
+            Setter? setter,
+            EvalOnDartLibrary evalForInstance)?
+        object,
+    TResult Function(
+            String type, String value, Setter? setter, String instanceRefId)?
+        enumeration,
+    required TResult orElse(),
+  }) {
+    if (enumeration != null) {
+      return enumeration(type, value, setter, instanceRefId);
+    }
+    return orElse();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(NullInstance value) nill,
+    required TResult Function(BoolInstance value) boolean,
+    required TResult Function(NumInstance value) number,
+    required TResult Function(StringInstance value) string,
+    required TResult Function(MapInstance value) map,
+    required TResult Function(ListInstance value) list,
+    required TResult Function(ObjectInstance value) object,
+    required TResult Function(EnumInstance value) enumeration,
+  }) {
+    return enumeration(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult? mapOrNull<TResult extends Object?>({
+    TResult Function(NullInstance value)? nill,
+    TResult Function(BoolInstance value)? boolean,
+    TResult Function(NumInstance value)? number,
+    TResult Function(StringInstance value)? string,
+    TResult Function(MapInstance value)? map,
+    TResult Function(ListInstance value)? list,
+    TResult Function(ObjectInstance value)? object,
+    TResult Function(EnumInstance value)? enumeration,
+  }) {
+    return enumeration?.call(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(NullInstance value)? nill,
+    TResult Function(BoolInstance value)? boolean,
+    TResult Function(NumInstance value)? number,
+    TResult Function(StringInstance value)? string,
+    TResult Function(MapInstance value)? map,
+    TResult Function(ListInstance value)? list,
+    TResult Function(ObjectInstance value)? object,
+    TResult Function(EnumInstance value)? enumeration,
+    required TResult orElse(),
+  }) {
+    if (enumeration != null) {
+      return enumeration(this);
+    }
+    return orElse();
+  }
+}
+
+abstract class EnumInstance extends InstanceDetails {
+  factory EnumInstance(
+      {required String type,
+      required String value,
+      required Setter? setter,
+      required String instanceRefId}) = _$EnumInstance;
+  EnumInstance._() : super._();
+
+  String get type;
+  String get value;
+  @override
+  Setter? get setter;
+  String get instanceRefId;
+  @override
+  @JsonKey(ignore: true)
+  $EnumInstanceCopyWith<EnumInstance> get copyWith =>
+      throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+class _$InstancePathTearOff {
+  const _$InstancePathTearOff();
+
+  _InstancePathFromInstanceId fromInstanceId(String instanceId,
+      {List<PathToProperty> pathToProperty = const []}) {
+    return _InstancePathFromInstanceId(
+      instanceId,
+      pathToProperty: pathToProperty,
+    );
+  }
+
+  _InstancePathFromProviderId fromProviderId(String providerId,
+      {List<PathToProperty> pathToProperty = const []}) {
+    return _InstancePathFromProviderId(
+      providerId,
+      pathToProperty: pathToProperty,
+    );
+  }
+}
+
+/// @nodoc
+const $InstancePath = _$InstancePathTearOff();
+
+/// @nodoc
+mixin _$InstancePath {
+  List<PathToProperty> get pathToProperty => throw _privateConstructorUsedError;
+
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function(
+            String instanceId, List<PathToProperty> pathToProperty)
+        fromInstanceId,
+    required TResult Function(
+            String providerId, List<PathToProperty> pathToProperty)
+        fromProviderId,
+  }) =>
+      throw _privateConstructorUsedError;
+  @optionalTypeArgs
+  TResult? whenOrNull<TResult extends Object?>({
+    TResult Function(String instanceId, List<PathToProperty> pathToProperty)?
+        fromInstanceId,
+    TResult Function(String providerId, List<PathToProperty> pathToProperty)?
+        fromProviderId,
+  }) =>
+      throw _privateConstructorUsedError;
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function(String instanceId, List<PathToProperty> pathToProperty)?
+        fromInstanceId,
+    TResult Function(String providerId, List<PathToProperty> pathToProperty)?
+        fromProviderId,
+    required TResult orElse(),
+  }) =>
+      throw _privateConstructorUsedError;
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(_InstancePathFromInstanceId value) fromInstanceId,
+    required TResult Function(_InstancePathFromProviderId value) fromProviderId,
+  }) =>
+      throw _privateConstructorUsedError;
+  @optionalTypeArgs
+  TResult? mapOrNull<TResult extends Object?>({
+    TResult Function(_InstancePathFromInstanceId value)? fromInstanceId,
+    TResult Function(_InstancePathFromProviderId value)? fromProviderId,
+  }) =>
+      throw _privateConstructorUsedError;
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(_InstancePathFromInstanceId value)? fromInstanceId,
+    TResult Function(_InstancePathFromProviderId value)? fromProviderId,
+    required TResult orElse(),
+  }) =>
+      throw _privateConstructorUsedError;
+
+  @JsonKey(ignore: true)
+  $InstancePathCopyWith<InstancePath> get copyWith =>
+      throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class $InstancePathCopyWith<$Res> {
+  factory $InstancePathCopyWith(
+          InstancePath value, $Res Function(InstancePath) then) =
+      _$InstancePathCopyWithImpl<$Res>;
+  $Res call({List<PathToProperty> pathToProperty});
+}
+
+/// @nodoc
+class _$InstancePathCopyWithImpl<$Res> implements $InstancePathCopyWith<$Res> {
+  _$InstancePathCopyWithImpl(this._value, this._then);
+
+  final InstancePath _value;
+  // ignore: unused_field
+  final $Res Function(InstancePath) _then;
+
+  @override
+  $Res call({
+    Object? pathToProperty = freezed,
+  }) {
+    return _then(_value.copyWith(
+      pathToProperty: pathToProperty == freezed
+          ? _value.pathToProperty
+          : pathToProperty // ignore: cast_nullable_to_non_nullable
+              as List<PathToProperty>,
+    ));
+  }
+}
+
+/// @nodoc
+abstract class _$InstancePathFromInstanceIdCopyWith<$Res>
+    implements $InstancePathCopyWith<$Res> {
+  factory _$InstancePathFromInstanceIdCopyWith(
+          _InstancePathFromInstanceId value,
+          $Res Function(_InstancePathFromInstanceId) then) =
+      __$InstancePathFromInstanceIdCopyWithImpl<$Res>;
+  @override
+  $Res call({String instanceId, List<PathToProperty> pathToProperty});
+}
+
+/// @nodoc
+class __$InstancePathFromInstanceIdCopyWithImpl<$Res>
+    extends _$InstancePathCopyWithImpl<$Res>
+    implements _$InstancePathFromInstanceIdCopyWith<$Res> {
+  __$InstancePathFromInstanceIdCopyWithImpl(_InstancePathFromInstanceId _value,
+      $Res Function(_InstancePathFromInstanceId) _then)
+      : super(_value, (v) => _then(v as _InstancePathFromInstanceId));
+
+  @override
+  _InstancePathFromInstanceId get _value =>
+      super._value as _InstancePathFromInstanceId;
+
+  @override
+  $Res call({
+    Object? instanceId = freezed,
+    Object? pathToProperty = freezed,
+  }) {
+    return _then(_InstancePathFromInstanceId(
+      instanceId == freezed
+          ? _value.instanceId
+          : instanceId // ignore: cast_nullable_to_non_nullable
+              as String,
+      pathToProperty: pathToProperty == freezed
+          ? _value.pathToProperty
+          : pathToProperty // ignore: cast_nullable_to_non_nullable
+              as List<PathToProperty>,
+    ));
+  }
+}
+
+/// @nodoc
+
+class _$_InstancePathFromInstanceId extends _InstancePathFromInstanceId
+    with DiagnosticableTreeMixin {
+  const _$_InstancePathFromInstanceId(this.instanceId,
+      {this.pathToProperty = const []})
+      : super._();
+
+  @override
+  final String instanceId;
+  @JsonKey()
+  @override
+  final List<PathToProperty> pathToProperty;
+
+  @override
+  String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) {
+    return 'InstancePath.fromInstanceId(instanceId: $instanceId, pathToProperty: $pathToProperty)';
+  }
+
+  @override
+  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
+    super.debugFillProperties(properties);
+    properties
+      ..add(DiagnosticsProperty('type', 'InstancePath.fromInstanceId'))
+      ..add(DiagnosticsProperty('instanceId', instanceId))
+      ..add(DiagnosticsProperty('pathToProperty', pathToProperty));
+  }
+
+  @override
+  bool operator ==(dynamic other) {
+    return identical(this, other) ||
+        (other.runtimeType == runtimeType &&
+            other is _InstancePathFromInstanceId &&
+            const DeepCollectionEquality()
+                .equals(other.instanceId, instanceId) &&
+            const DeepCollectionEquality()
+                .equals(other.pathToProperty, pathToProperty));
+  }
+
+  @override
+  int get hashCode => Object.hash(
+      runtimeType,
+      const DeepCollectionEquality().hash(instanceId),
+      const DeepCollectionEquality().hash(pathToProperty));
+
+  @JsonKey(ignore: true)
+  @override
+  _$InstancePathFromInstanceIdCopyWith<_InstancePathFromInstanceId>
+      get copyWith => __$InstancePathFromInstanceIdCopyWithImpl<
+          _InstancePathFromInstanceId>(this, _$identity);
+
+  @override
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function(
+            String instanceId, List<PathToProperty> pathToProperty)
+        fromInstanceId,
+    required TResult Function(
+            String providerId, List<PathToProperty> pathToProperty)
+        fromProviderId,
+  }) {
+    return fromInstanceId(instanceId, pathToProperty);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult? whenOrNull<TResult extends Object?>({
+    TResult Function(String instanceId, List<PathToProperty> pathToProperty)?
+        fromInstanceId,
+    TResult Function(String providerId, List<PathToProperty> pathToProperty)?
+        fromProviderId,
+  }) {
+    return fromInstanceId?.call(instanceId, pathToProperty);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function(String instanceId, List<PathToProperty> pathToProperty)?
+        fromInstanceId,
+    TResult Function(String providerId, List<PathToProperty> pathToProperty)?
+        fromProviderId,
+    required TResult orElse(),
+  }) {
+    if (fromInstanceId != null) {
+      return fromInstanceId(instanceId, pathToProperty);
+    }
+    return orElse();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(_InstancePathFromInstanceId value) fromInstanceId,
+    required TResult Function(_InstancePathFromProviderId value) fromProviderId,
+  }) {
+    return fromInstanceId(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult? mapOrNull<TResult extends Object?>({
+    TResult Function(_InstancePathFromInstanceId value)? fromInstanceId,
+    TResult Function(_InstancePathFromProviderId value)? fromProviderId,
+  }) {
+    return fromInstanceId?.call(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(_InstancePathFromInstanceId value)? fromInstanceId,
+    TResult Function(_InstancePathFromProviderId value)? fromProviderId,
+    required TResult orElse(),
+  }) {
+    if (fromInstanceId != null) {
+      return fromInstanceId(this);
+    }
+    return orElse();
+  }
+}
+
+abstract class _InstancePathFromInstanceId extends InstancePath {
+  const factory _InstancePathFromInstanceId(String instanceId,
+      {List<PathToProperty> pathToProperty}) = _$_InstancePathFromInstanceId;
+  const _InstancePathFromInstanceId._() : super._();
+
+  String get instanceId;
+  @override
+  List<PathToProperty> get pathToProperty;
+  @override
+  @JsonKey(ignore: true)
+  _$InstancePathFromInstanceIdCopyWith<_InstancePathFromInstanceId>
+      get copyWith => throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class _$InstancePathFromProviderIdCopyWith<$Res>
+    implements $InstancePathCopyWith<$Res> {
+  factory _$InstancePathFromProviderIdCopyWith(
+          _InstancePathFromProviderId value,
+          $Res Function(_InstancePathFromProviderId) then) =
+      __$InstancePathFromProviderIdCopyWithImpl<$Res>;
+  @override
+  $Res call({String providerId, List<PathToProperty> pathToProperty});
+}
+
+/// @nodoc
+class __$InstancePathFromProviderIdCopyWithImpl<$Res>
+    extends _$InstancePathCopyWithImpl<$Res>
+    implements _$InstancePathFromProviderIdCopyWith<$Res> {
+  __$InstancePathFromProviderIdCopyWithImpl(_InstancePathFromProviderId _value,
+      $Res Function(_InstancePathFromProviderId) _then)
+      : super(_value, (v) => _then(v as _InstancePathFromProviderId));
+
+  @override
+  _InstancePathFromProviderId get _value =>
+      super._value as _InstancePathFromProviderId;
+
+  @override
+  $Res call({
+    Object? providerId = freezed,
+    Object? pathToProperty = freezed,
+  }) {
+    return _then(_InstancePathFromProviderId(
+      providerId == freezed
+          ? _value.providerId
+          : providerId // ignore: cast_nullable_to_non_nullable
+              as String,
+      pathToProperty: pathToProperty == freezed
+          ? _value.pathToProperty
+          : pathToProperty // ignore: cast_nullable_to_non_nullable
+              as List<PathToProperty>,
+    ));
+  }
+}
+
+/// @nodoc
+
+class _$_InstancePathFromProviderId extends _InstancePathFromProviderId
+    with DiagnosticableTreeMixin {
+  const _$_InstancePathFromProviderId(this.providerId,
+      {this.pathToProperty = const []})
+      : super._();
+
+  @override
+  final String providerId;
+  @JsonKey()
+  @override
+  final List<PathToProperty> pathToProperty;
+
+  @override
+  String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) {
+    return 'InstancePath.fromProviderId(providerId: $providerId, pathToProperty: $pathToProperty)';
+  }
+
+  @override
+  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
+    super.debugFillProperties(properties);
+    properties
+      ..add(DiagnosticsProperty('type', 'InstancePath.fromProviderId'))
+      ..add(DiagnosticsProperty('providerId', providerId))
+      ..add(DiagnosticsProperty('pathToProperty', pathToProperty));
+  }
+
+  @override
+  bool operator ==(dynamic other) {
+    return identical(this, other) ||
+        (other.runtimeType == runtimeType &&
+            other is _InstancePathFromProviderId &&
+            const DeepCollectionEquality()
+                .equals(other.providerId, providerId) &&
+            const DeepCollectionEquality()
+                .equals(other.pathToProperty, pathToProperty));
+  }
+
+  @override
+  int get hashCode => Object.hash(
+      runtimeType,
+      const DeepCollectionEquality().hash(providerId),
+      const DeepCollectionEquality().hash(pathToProperty));
+
+  @JsonKey(ignore: true)
+  @override
+  _$InstancePathFromProviderIdCopyWith<_InstancePathFromProviderId>
+      get copyWith => __$InstancePathFromProviderIdCopyWithImpl<
+          _InstancePathFromProviderId>(this, _$identity);
+
+  @override
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function(
+            String instanceId, List<PathToProperty> pathToProperty)
+        fromInstanceId,
+    required TResult Function(
+            String providerId, List<PathToProperty> pathToProperty)
+        fromProviderId,
+  }) {
+    return fromProviderId(providerId, pathToProperty);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult? whenOrNull<TResult extends Object?>({
+    TResult Function(String instanceId, List<PathToProperty> pathToProperty)?
+        fromInstanceId,
+    TResult Function(String providerId, List<PathToProperty> pathToProperty)?
+        fromProviderId,
+  }) {
+    return fromProviderId?.call(providerId, pathToProperty);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function(String instanceId, List<PathToProperty> pathToProperty)?
+        fromInstanceId,
+    TResult Function(String providerId, List<PathToProperty> pathToProperty)?
+        fromProviderId,
+    required TResult orElse(),
+  }) {
+    if (fromProviderId != null) {
+      return fromProviderId(providerId, pathToProperty);
+    }
+    return orElse();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(_InstancePathFromInstanceId value) fromInstanceId,
+    required TResult Function(_InstancePathFromProviderId value) fromProviderId,
+  }) {
+    return fromProviderId(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult? mapOrNull<TResult extends Object?>({
+    TResult Function(_InstancePathFromInstanceId value)? fromInstanceId,
+    TResult Function(_InstancePathFromProviderId value)? fromProviderId,
+  }) {
+    return fromProviderId?.call(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(_InstancePathFromInstanceId value)? fromInstanceId,
+    TResult Function(_InstancePathFromProviderId value)? fromProviderId,
+    required TResult orElse(),
+  }) {
+    if (fromProviderId != null) {
+      return fromProviderId(this);
+    }
+    return orElse();
+  }
+}
+
+abstract class _InstancePathFromProviderId extends InstancePath {
+  const factory _InstancePathFromProviderId(String providerId,
+      {List<PathToProperty> pathToProperty}) = _$_InstancePathFromProviderId;
+  const _InstancePathFromProviderId._() : super._();
+
+  String get providerId;
+  @override
+  List<PathToProperty> get pathToProperty;
+  @override
+  @JsonKey(ignore: true)
+  _$InstancePathFromProviderIdCopyWith<_InstancePathFromProviderId>
+      get copyWith => throw _privateConstructorUsedError;
+}
diff --git a/packages/provider_devtools_extension/lib/src/instance_viewer/instance_providers.dart b/packages/provider_devtools_extension/lib/src/instance_viewer/instance_providers.dart
new file mode 100644
index 00000000..8a730d8b
--- /dev/null
+++ b/packages/provider_devtools_extension/lib/src/instance_viewer/instance_providers.dart
@@ -0,0 +1,435 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:collection/collection.dart';
+import 'package:devtools_app_shared/service.dart';
+import 'package:devtools_app_shared/utils.dart';
+import 'package:devtools_extensions/devtools_extensions.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:vm_service/vm_service.dart' hide SentinelException;
+
+import 'eval.dart';
+import 'instance_details.dart';
+import 'result.dart';
+
+Future<InstanceRef> _resolveInstanceRefForPath(
+  InstancePath path, {
+  required AutoDisposeRef ref,
+  required Disposable isAlive,
+  required InstanceDetails? parent,
+}) async {
+  if (parent == null) {
+    // root of the provider tree
+
+    return path.map(
+      fromProviderId: (path) async {
+        final eval = await ref.watch(providerEvalProvider.future);
+        // cause the instances to be re-evaluated when the devtool is notified
+        // that a provider changed
+        ref.watch(_providerChanged(path.providerId));
+
+        return eval.safeEval(
+          'ProviderBinding.debugInstance.providerDetails["${path.providerId}"]?.value',
+          isAlive: isAlive,
+        );
+      },
+      fromInstanceId: (path) async {
+        final eval = await ref.watch(evalProvider.future);
+        return eval.safeEval(
+          'value',
+          isAlive: isAlive,
+          scope: {'value': path.instanceId},
+        );
+      },
+    );
+  }
+
+  final eval = await ref.watch(evalProvider.future);
+
+  return parent.maybeMap(
+    // TODO: support sets
+    // TODO: iterables should use iterators / next() for iterable to navigate, to avoid recomputing the content
+
+    map: (parent) {
+      final keyPath = path.pathToProperty.last as MapKeyPath;
+      final key = keyPath.ref == null ? 'null' : 'key';
+      final keyPathRef = keyPath.ref;
+
+      return eval.safeEval(
+        'parent[$key]',
+        isAlive: isAlive,
+        scope: {
+          'parent': parent.instanceRefId,
+          if (keyPathRef != null) 'key': keyPathRef,
+        },
+      );
+    },
+    list: (parent) {
+      final indexPath = path.pathToProperty.last as ListIndexPath;
+
+      return eval.safeEval(
+        'parent[${indexPath.index}]',
+        isAlive: isAlive,
+        scope: {'parent': parent.instanceRefId},
+      );
+    },
+    object: (parent) {
+      final propertyPath = path.pathToProperty.last as PropertyPath;
+
+      // compare by both name and ref ID because an object may have multiple
+      // fields with the same name
+      final field = parent.fields.firstWhere(
+        (element) =>
+            element.name == propertyPath.name &&
+            element.ownerName == propertyPath.ownerName &&
+            element.ownerUri == propertyPath.ownerUri,
+      );
+
+      final ref = field.ref.dataOrThrow;
+
+      // we cannot do `eval('parent.propertyName')` because it is possible for
+      // objects to have multiple properties with the same name
+      return eval.safeGetInstance(ref, isAlive);
+    },
+    orElse: () => throw Exception('Unexpected instance type.'),
+  );
+}
+
+/// Update a variable using the `=` operator.
+///
+/// In rare cases, it is possible for this function to mutate the wrong property.
+/// This can happen when an object contains multiple fields with the same name
+/// (such as private properties or overridden properties), where the conflicting
+/// fields are both defined in the same library.
+Future<void> _mutate(
+  String newValueExpression, {
+  required InstancePath path,
+  required AutoDisposeRef ref,
+  required Disposable isAlive,
+  required InstanceDetails parent,
+}) async {
+  await parent.maybeMap(
+    list: (parent) async {
+      final eval = await ref.watch(evalProvider.future);
+      final indexPath = path.pathToProperty.last as ListIndexPath;
+
+      return eval.safeEval(
+        'parent[${indexPath.index}] = $newValueExpression',
+        isAlive: isAlive,
+        scope: {
+          'parent': parent.instanceRefId,
+        },
+      );
+    },
+    map: (parent) async {
+      final eval = await ref.watch(evalProvider.future);
+      final keyPath = path.pathToProperty.last as MapKeyPath;
+      final keyRefVar = keyPath.ref == null ? 'null' : 'key';
+      final keyPathRef = keyPath.ref;
+
+      return eval.safeEval(
+        'parent[$keyRefVar] = $newValueExpression',
+        isAlive: isAlive,
+        scope: {
+          'parent': parent.instanceRefId,
+          if (keyPathRef != null) 'key': keyPathRef,
+        },
+      );
+    },
+    // TODO test can mutate properties of a mixin placed in a different library that the class that uses it
+    object: (parent) {
+      final propertyPath = path.pathToProperty.last as PropertyPath;
+
+      final field = parent.fields.firstWhere(
+        (f) =>
+            f.name == propertyPath.name &&
+            f.ownerName == propertyPath.ownerName,
+      );
+
+      return field.eval.safeEval(
+        '(parent as ${propertyPath.ownerName}).${propertyPath.name} = $newValueExpression',
+        isAlive: isAlive,
+        scope: {
+          'parent': parent.instanceRefId,
+        },
+      );
+    },
+    orElse: () => throw StateError('Can only mutate lists/maps/objects'),
+  );
+
+  // TODO(rrousselGit): call notifyListeners/setState/notifyClients based on the modified object
+
+  // Since the same object can be used in multiple locations at once, we need
+  // to refresh the entire tree instead of just the node that was modified.
+  ref.refresh(instanceProvider(path.root));
+
+  // Forces the UI to rebuild after the state change
+  await serviceManager.performHotReload();
+}
+
+Future<InstanceDetails?> _resolveParent(
+  AutoDisposeRef ref,
+  InstancePath path,
+) async {
+  return path.pathToProperty.isNotEmpty
+      ? await ref.watch(instanceProvider(path.parent!).future)
+      : null;
+}
+
+Future<EnumInstance?> _tryParseEnum(
+  Instance instance, {
+  required EvalOnDartLibrary eval,
+  required Disposable isAlive,
+  required String instanceRefId,
+  required Setter? setter,
+}) async {
+  if (instance.kind != InstanceKind.kPlainInstance ||
+      instance.fields?.length != 2) return null;
+
+  InstanceRef? findPropertyWithName(String name) {
+    return instance.fields
+        ?.firstWhereOrNull((element) => element.decl?.name == name)
+        ?.value;
+  }
+
+  final nameRef = findPropertyWithName('_name');
+  final indexRef = findPropertyWithName('index');
+  if (nameRef == null || indexRef == null) return null;
+
+  final nameInstanceFuture = eval.safeGetInstance(nameRef, isAlive);
+  final indexInstanceFuture = eval.safeGetInstance(indexRef, isAlive);
+
+  final index = await indexInstanceFuture;
+  if (index.kind != InstanceKind.kInt) return null;
+
+  final name = await nameInstanceFuture;
+  if (name.kind != InstanceKind.kString) return null;
+
+  // Some Dart versions have for name "EnumType.valueName", others only have "valueName".
+  // So we have to strip the type manually
+  final nameSplit = name.valueAsString!.split('.');
+
+  return EnumInstance(
+    type: instance.classRef!.name!,
+    value: nameSplit.last,
+    instanceRefId: instanceRefId,
+    setter: setter,
+  );
+}
+
+Setter? _parseSetter({
+  required InstancePath path,
+  required AutoDisposeRef ref,
+  required Disposable isAlive,
+  required InstanceDetails? parent,
+}) {
+  if (parent == null) return null;
+
+  Future<void> mutate(String expression) {
+    return _mutate(
+      expression,
+      path: path,
+      ref: ref,
+      isAlive: isAlive,
+      parent: parent,
+    );
+  }
+
+  return parent.maybeMap(
+    // TODO const collections should have no setter
+    map: (parent) => mutate,
+    list: (parent) => mutate,
+    object: (parent) {
+      final keyPath = path.pathToProperty.last as PropertyPath;
+
+      // Mutate properties by name as we can't mutate them from a reference.
+      // This may edit the wrong property when an object has two properties with
+      // with the same name.
+      // TODO use ownerUri
+      final field = parent.fields.firstWhere(
+        (field) =>
+            field.name == keyPath.name && field.ownerName == keyPath.ownerName,
+      );
+
+      if (field.isFinal) return null;
+      return mutate;
+    },
+    orElse: () => throw Exception('Unexpected instance type.'),
+  );
+}
+
+/// Fetches informations related to an instance/provider at a given path
+///
+/// The UI should not be used directly. Instead, use [instanceProvider].
+final AutoDisposeFutureProviderFamily<InstanceDetails, InstancePath>
+    instanceProvider =
+    AutoDisposeFutureProviderFamily<InstanceDetails, InstancePath>(
+  (ref, path) async {
+    ref.watch(hotRestartEventProvider);
+
+    final eval = await ref.watch(evalProvider.future);
+
+    final isAlive = Disposable();
+    ref.onDispose(isAlive.dispose);
+
+    final parent = await _resolveParent(ref, path);
+
+    final instanceRef = await _resolveInstanceRefForPath(
+      path,
+      ref: ref,
+      parent: parent,
+      isAlive: isAlive,
+    );
+
+    final setter = _parseSetter(
+      path: path,
+      isAlive: isAlive,
+      ref: ref,
+      parent: parent,
+    );
+
+    final instance = await eval.safeGetInstance(instanceRef, isAlive);
+
+    switch (instance.kind) {
+      case InstanceKind.kNull:
+        return InstanceDetails.nill(setter: setter);
+      case InstanceKind.kBool:
+        return InstanceDetails.boolean(
+          instance.valueAsString!,
+          instanceRefId: instanceRef.id!,
+          setter: setter,
+        );
+      case InstanceKind.kInt:
+      case InstanceKind.kDouble:
+        return InstanceDetails.number(
+          instance.valueAsString!,
+          instanceRefId: instanceRef.id!,
+          setter: setter,
+        );
+      case InstanceKind.kString:
+        return InstanceDetails.string(
+          instance.valueAsString!,
+          instanceRefId: instanceRef.id!,
+          setter: setter,
+        );
+
+      case InstanceKind.kMap:
+
+        // voluntarily throw if a key failed to load
+        final keysRef = instance.associations!.map((e) => e.key as InstanceRef);
+
+        final keysFuture = Future.wait<InstanceDetails>([
+          for (final keyRef in keysRef)
+            ref.watch(
+              instanceProvider(InstancePath.fromInstanceId(keyRef.id!)).future,
+            ),
+        ]);
+
+        return InstanceDetails.map(
+          await keysFuture,
+          hash: await eval.getHashCode(instance, isAlive: isAlive),
+          instanceRefId: instanceRef.id!,
+          setter: setter,
+        );
+
+      // TODO(rrousselGit): support sets
+      // TODO(rrousselGit): support custom lists
+      // TODO(rrousselGit): support Type
+      case InstanceKind.kList:
+        return InstanceDetails.list(
+          length: instance.length!,
+          hash: await eval.getHashCode(instance, isAlive: isAlive),
+          instanceRefId: instanceRef.id!,
+          setter: setter,
+        );
+
+      case InstanceKind.kPlainInstance:
+      default:
+        final enumDetails = await _tryParseEnum(
+          instance,
+          eval: eval,
+          isAlive: isAlive,
+          instanceRefId: instanceRef.id!,
+          setter: setter,
+        );
+
+        if (enumDetails != null) return enumDetails;
+
+        final classInstance =
+            await eval.safeGetClass(instance.classRef!, isAlive);
+        final evalForInstance =
+            // TODO(rrousselGit) when can `library` be null?
+            ref.watch(libraryEvalProvider(classInstance.library!.uri!).future);
+
+        final appName = tryParsePackageName(eval.isolate!.rootLib!.uri!);
+
+        final fields = await _parseFields(
+          ref,
+          eval,
+          instance,
+          isAlive: isAlive,
+          appName: appName,
+        );
+
+        return InstanceDetails.object(
+          fields.sorted((a, b) => sortFieldsByName(a.name, b.name)),
+          hash: await eval.getHashCode(instance, isAlive: isAlive),
+          type: classInstance.name!,
+          instanceRefId: instanceRef.id!,
+          evalForInstance: await evalForInstance,
+          setter: setter,
+        );
+    }
+  },
+);
+
+final _packageNameExp = RegExp(
+  r'package:(.+?)/',
+);
+
+String? tryParsePackageName(String uri) {
+  return _packageNameExp.firstMatch(uri)?.group(1);
+}
+
+Future<List<ObjectField>> _parseFields(
+  AutoDisposeRef ref,
+  EvalOnDartLibrary eval,
+  Instance instance, {
+  required Disposable isAlive,
+  required String? appName,
+}) {
+  final fields = instance.fields!.map((field) async {
+    final fieldDeclaration = field.decl!;
+    final owner =
+        await eval.safeGetClass(fieldDeclaration.owner! as ClassRef, isAlive);
+
+    final ownerUri = fieldDeclaration.location!.script!.uri!;
+    final ownerName = owner.mixin?.name ?? owner.name!;
+    final ownerPackageName = tryParsePackageName(ownerUri);
+
+    return ObjectField(
+      name: fieldDeclaration.name!,
+      isFinal: fieldDeclaration.isFinal!,
+      ref: parseSentinel<InstanceRef>(field.value),
+      ownerName: ownerName,
+      ownerUri: ownerUri,
+      eval: await ref.watch(libraryEvalProvider(ownerUri).future),
+      isDefinedByDependency: ownerPackageName != appName,
+    );
+  }).toList();
+
+  return Future.wait(fields);
+}
+
+final _providerChanged =
+    AutoDisposeStreamProviderFamily<void, String>((ref, id) async* {
+  final service = await ref.watch(serviceProvider.future);
+
+  yield* service.onExtensionEvent.where((event) {
+    return event.extensionKind == 'provider:provider_changed' &&
+        event.extensionData?.data['id'] == id;
+  });
+});
diff --git a/packages/provider_devtools_extension/lib/src/instance_viewer/instance_viewer.dart b/packages/provider_devtools_extension/lib/src/instance_viewer/instance_viewer.dart
new file mode 100644
index 00000000..e7092179
--- /dev/null
+++ b/packages/provider_devtools_extension/lib/src/instance_viewer/instance_viewer.dart
@@ -0,0 +1,638 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// TODO(rrousselGit) merge this code with the debugger view
+
+import 'dart:async';
+import 'dart:math' as math;
+
+import 'package:devtools_app_shared/service.dart';
+import 'package:devtools_app_shared/ui.dart';
+import 'package:devtools_app_shared/utils.dart';
+import 'package:flutter/foundation.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+
+import 'instance_details.dart';
+import 'instance_providers.dart';
+
+import '../utils/sliver_iterable_child_delegate.dart';
+
+const typeColor = Color.fromARGB(255, 78, 201, 176);
+const boolColor = Color.fromARGB(255, 86, 156, 214);
+const nullColor = boolColor;
+const numColor = Color.fromARGB(255, 181, 206, 168);
+const stringColor = Color.fromARGB(255, 206, 145, 120);
+
+const double rowHeight = 20.0;
+
+final isExpandedProvider = StateProviderFamily<bool, InstancePath>((ref, path) {
+  // expands the root by default, but not children
+  return path.pathToProperty.isEmpty;
+});
+
+final estimatedChildCountProvider =
+    AutoDisposeProviderFamily<int, InstancePath>((ref, rootPath) {
+  int estimatedChildCount(InstancePath path) {
+    int one(InstanceDetails _) => 1;
+
+    int expandableEstimatedChildCount(Iterable<PathToProperty> keys) {
+      if (!ref.watch(isExpandedProvider(path))) {
+        return 1;
+      }
+      return keys.fold(1, (acc, element) {
+        return acc +
+            estimatedChildCount(
+              path.pathForChild(element),
+            );
+      });
+    }
+
+    return ref.watch(instanceProvider(path)).when(
+          loading: () => 1,
+          error: (err, stack) => 1,
+          data: (instance) {
+            return instance.map(
+              nill: one,
+              boolean: one,
+              number: one,
+              string: one,
+              enumeration: one,
+              map: (instance) {
+                return expandableEstimatedChildCount(
+                  instance.keys.map(
+                    (key) => PathToProperty.mapKey(ref: key.instanceRefId),
+                  ),
+                );
+              },
+              list: (instance) {
+                return expandableEstimatedChildCount(
+                  List.generate(instance.length, $PathToProperty.listIndex),
+                );
+              },
+              object: (instance) {
+                return expandableEstimatedChildCount(
+                  instance.fields.map(
+                    (field) => PathToProperty.fromObjectField(field),
+                  ),
+                );
+              },
+            );
+          },
+        );
+  }
+
+  return estimatedChildCount(rootPath);
+});
+
+void showErrorSnackBar(BuildContext context, Object error) {
+  ScaffoldMessenger.of(context).showSnackBar(
+    SnackBar(content: Text('Error: $error')),
+  );
+}
+
+class InstanceViewer extends ConsumerStatefulWidget {
+  const InstanceViewer({
+    Key? key,
+    required this.rootPath,
+    required this.showInternalProperties,
+  }) : super(key: key);
+
+  final InstancePath rootPath;
+  final bool showInternalProperties;
+
+  @override
+  ConsumerState<ConsumerStatefulWidget> createState() => _InstanceViewerState();
+}
+
+class _InstanceViewerState extends ConsumerState<InstanceViewer> {
+  final scrollController = ScrollController();
+
+  @override
+  void dispose() {
+    scrollController.dispose();
+    super.dispose();
+  }
+
+  Iterable<Widget> _buildError(
+    Object error,
+    StackTrace? _,
+    InstancePath __,
+  ) {
+    if (error is SentinelException) {
+      final valueAsString = error.sentinel.valueAsString;
+      if (valueAsString != null) return [Text(valueAsString)];
+    }
+
+    return const [Text('<unknown error>')];
+  }
+
+  Iterable<Widget?> _buildListViewItems(
+    BuildContext context,
+    WidgetRef ref, {
+    required InstancePath path,
+    bool disableExpand = false,
+  }) {
+    return ref.watch(instanceProvider(path)).when(
+          loading: () => const [Text('loading...')],
+          error: (err, stack) => _buildError(err, stack, path),
+          data: (instance) sync* {
+            final isExpanded = ref.watch(isExpandedProvider(path).state);
+            yield _buildHeader(
+              instance,
+              path: path,
+              isExpanded: isExpanded,
+              disableExpand: disableExpand,
+            );
+
+            if (isExpanded.state) {
+              yield* instance.maybeMap(
+                object: (instance) => _buildObjectItem(
+                  context,
+                  ref,
+                  instance,
+                  path: path,
+                ),
+                list: (list) => _buildListItem(
+                  context,
+                  ref,
+                  list,
+                  path: path,
+                ),
+                map: (map) => _buildMapItem(
+                  context,
+                  ref,
+                  map,
+                  path: path,
+                ),
+                // Reaches when the root of the instance tree is a string/numbers/bool/....
+                orElse: () => const [],
+              );
+            }
+          },
+        );
+  }
+
+  Widget _buildHeader(
+    InstanceDetails instance, {
+    required InstancePath path,
+    StateController<bool>? isExpanded,
+    bool disableExpand = false,
+  }) {
+    return _Expandable(
+      key: ValueKey(path),
+      isExpandable: !disableExpand && instance.isExpandable,
+      isExpanded: isExpanded,
+      title: instance.map(
+        enumeration: (instance) => _EditableField(
+          setter: instance.setter,
+          initialEditString: '${instance.type}.${instance.value}',
+          child: Text.rich(
+            TextSpan(
+              children: [
+                TextSpan(
+                  text: instance.type,
+                  style: const TextStyle(color: typeColor),
+                ),
+                TextSpan(text: '.${instance.value}'),
+              ],
+            ),
+          ),
+        ),
+        nill: (instance) => _EditableField(
+          setter: instance.setter,
+          initialEditString: 'null',
+          child: const Text('null', style: TextStyle(color: nullColor)),
+        ),
+        string: (instance) => _EditableField(
+          setter: instance.setter,
+          initialEditString: '"${instance.displayString}"',
+          child: Text.rich(
+            TextSpan(
+              children: [
+                const TextSpan(text: '"'),
+                TextSpan(
+                  text: instance.displayString,
+                  style: const TextStyle(color: stringColor),
+                ),
+                const TextSpan(text: '"'),
+              ],
+            ),
+          ),
+        ),
+        number: (instance) => _EditableField(
+          setter: instance.setter,
+          initialEditString: instance.displayString,
+          child: Text(
+            instance.displayString,
+            style: const TextStyle(color: numColor),
+          ),
+        ),
+        boolean: (instance) => _EditableField(
+          setter: instance.setter,
+          initialEditString: instance.displayString,
+          child: Text(
+            instance.displayString,
+            style: const TextStyle(color: boolColor),
+          ),
+        ),
+        map: (instance) => _ObjectHeader(
+          startToken: '{',
+          endToken: '}',
+          hash: instance.hash,
+          meta: instance.keys.isEmpty
+              ? null
+              : '${instance.keys.length} ${pluralize('element', instance.keys.length)}',
+        ),
+        list: (instance) => _ObjectHeader(
+          startToken: '[',
+          endToken: ']',
+          hash: instance.hash,
+          meta: instance.length == 0
+              ? null
+              : '${instance.length} ${pluralize('element', instance.length)}',
+        ),
+        object: (instance) => _ObjectHeader(
+          type: instance.type,
+          hash: instance.hash,
+          startToken: '',
+          endToken: '',
+          // Never show the number of elements when using custom objects
+          meta: null,
+        ),
+      ),
+    );
+  }
+
+  Iterable<Widget> _buildMapItem(
+    BuildContext context,
+    WidgetRef ref,
+    MapInstance instance, {
+    required InstancePath path,
+  }) sync* {
+    for (final key in instance.keys) {
+      final value = _buildListViewItems(
+        context,
+        ref,
+        path: path.pathForChild(PathToProperty.mapKey(ref: key.instanceRefId)),
+      );
+
+      final keyHeader = _buildHeader(key, disableExpand: true, path: path);
+
+      var isFirstItem = true;
+      for (final child in value) {
+        yield child != null
+            ? Padding(
+                padding: const EdgeInsets.only(left: defaultSpacing),
+                child: isFirstItem
+                    ? Row(
+                        children: [
+                          keyHeader,
+                          const Text(': '),
+                          Expanded(child: child),
+                        ],
+                      )
+                    : child,
+              )
+            : const SizedBox();
+
+        isFirstItem = false;
+      }
+
+      assert(
+        !isFirstItem,
+        'Bad state: the value of $key did not render any widget',
+      );
+    }
+  }
+
+  Iterable<Widget> _buildListItem(
+    BuildContext context,
+    WidgetRef ref,
+    ListInstance instance, {
+    required InstancePath path,
+  }) sync* {
+    for (var index = 0; index < instance.length; index++) {
+      final children = _buildListViewItems(
+        context,
+        ref,
+        path: path.pathForChild(PathToProperty.listIndex(index)),
+      );
+
+      bool isFirst = true;
+
+      for (final child in children) {
+        Widget? rowItem = child;
+
+        // Add the item index only on the first line of the element
+        if (isFirst && rowItem != null) {
+          isFirst = false;
+          rowItem = Row(
+            children: [
+              Text('[$index]: '),
+              Expanded(child: rowItem),
+            ],
+          );
+        }
+
+        yield rowItem != null
+            ? Padding(
+                padding: const EdgeInsets.only(left: defaultSpacing),
+                child: rowItem,
+              )
+            : const SizedBox();
+      }
+    }
+  }
+
+  Iterable<Widget> _buildObjectItem(
+    BuildContext context,
+    WidgetRef ref,
+    ObjectInstance instance, {
+    required InstancePath path,
+  }) sync* {
+    for (final field in instance.fields) {
+      if (!widget.showInternalProperties &&
+          field.isDefinedByDependency &&
+          field.isPrivate) {
+        // Hide private properties from classes defined by dependencies
+        continue;
+      }
+
+      final children = _buildListViewItems(
+        context,
+        ref,
+        path: path.pathForChild(PathToProperty.fromObjectField(field)),
+      );
+
+      bool isFirst = true;
+
+      for (final child in children) {
+        Widget? rowItem = child;
+        if (isFirst && rowItem != null) {
+          isFirst = false;
+          rowItem = Row(
+            children: [
+              if (field.isFinal)
+                Text(
+                  'final ',
+                  style: Theme.of(context).subtleTextStyle,
+                ),
+              Text('${field.name}: '),
+              Expanded(child: rowItem),
+            ],
+          );
+        }
+
+        yield rowItem != null
+            ? Padding(
+                padding: const EdgeInsets.only(left: defaultSpacing),
+                child: rowItem,
+              )
+            : const SizedBox();
+      }
+    }
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Scrollbar(
+      thumbVisibility: true,
+      controller: scrollController,
+      child: ListView.custom(
+        controller: scrollController,
+        // TODO: item height should be based on font size
+        itemExtent: rowHeight,
+        padding: const EdgeInsets.symmetric(
+          vertical: denseSpacing,
+          horizontal: defaultSpacing,
+        ),
+        childrenDelegate: SliverIterableChildDelegate(
+          _buildListViewItems(
+            context,
+            ref,
+            path: widget.rootPath,
+            disableExpand: true,
+          ).cast<Widget?>(), // This cast is necessary to avoid Null type errors
+          estimatedChildCount:
+              ref.watch(estimatedChildCountProvider(widget.rootPath)),
+        ),
+      ),
+    );
+  }
+}
+
+class _ObjectHeader extends StatelessWidget {
+  const _ObjectHeader({
+    Key? key,
+    this.type,
+    required this.hash,
+    required this.meta,
+    required this.startToken,
+    required this.endToken,
+  }) : super(key: key);
+
+  final String? type;
+  final int hash;
+  final String? meta;
+  final String startToken;
+  final String endToken;
+
+  @override
+  Widget build(BuildContext context) {
+    final theme = Theme.of(context);
+
+    return Text.rich(
+      TextSpan(
+        children: [
+          if (type != null)
+            TextSpan(
+              text: type,
+              style: const TextStyle(color: typeColor),
+            ),
+          TextSpan(
+            text: '#${shortHash(hash)}',
+            style: theme.subtleTextStyle,
+          ),
+          TextSpan(text: startToken),
+          if (meta != null) TextSpan(text: meta),
+          TextSpan(text: endToken),
+        ],
+      ),
+    );
+  }
+}
+
+class _EditableField extends StatefulWidget {
+  const _EditableField({
+    Key? key,
+    required this.setter,
+    required this.child,
+    required this.initialEditString,
+  }) : super(key: key);
+
+  final Widget child;
+  final String initialEditString;
+  final Future<void> Function(String)? setter;
+
+  @override
+  _EditableFieldState createState() => _EditableFieldState();
+}
+
+class _EditableFieldState extends State<_EditableField> {
+  final controller = TextEditingController();
+  final focusNode = FocusNode(debugLabel: 'editable-field');
+  final textFieldFocusNode = FocusNode(debugLabel: 'text-field');
+  var isHovering = false;
+
+  final _isAlive = Disposable();
+
+  @override
+  void dispose() {
+    _isAlive.dispose();
+    controller.dispose();
+    focusNode.dispose();
+    textFieldFocusNode.dispose();
+    super.dispose();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    if (widget.setter == null) return widget.child;
+
+    final colorScheme = Theme.of(context).colorScheme;
+
+    final editingChild = TextField(
+      autofocus: true,
+      controller: controller,
+      focusNode: textFieldFocusNode,
+      onSubmitted: (value) async {
+        try {
+          final setter = widget.setter;
+          if (setter != null) await setter(value);
+        } catch (err) {
+          if (!context.mounted) return;
+          showErrorSnackBar(context, err);
+        }
+      },
+      decoration: InputDecoration(
+        contentPadding: const EdgeInsets.all(densePadding),
+        isDense: true,
+        border: OutlineInputBorder(
+          borderRadius: const BorderRadius.all(Radius.circular(5)),
+          borderSide: BorderSide(color: colorScheme.surface),
+        ),
+      ),
+    );
+
+    final displayChild = Stack(
+      clipBehavior: Clip.none,
+      children: [
+        if (isHovering)
+          Positioned(
+            bottom: -5,
+            left: -5,
+            top: -5,
+            right: -5,
+            child: DecoratedBox(
+              decoration: BoxDecoration(
+                borderRadius: defaultBorderRadius,
+                border: Border.all(color: colorScheme.surface),
+              ),
+            ),
+          ),
+        GestureDetector(
+          behavior: HitTestBehavior.opaque,
+          onTap: () {
+            focusNode.requestFocus();
+            textFieldFocusNode.requestFocus();
+            controller.text = widget.initialEditString;
+            controller.selection = TextSelection(
+              baseOffset: 0,
+              extentOffset: widget.initialEditString.length,
+            );
+          },
+          child: Align(
+            alignment: Alignment.centerLeft,
+            heightFactor: 1,
+            child: widget.child,
+          ),
+        ),
+      ],
+    );
+
+    return AnimatedBuilder(
+      animation: focusNode,
+      builder: (context, _) {
+        final isEditing = focusNode.hasFocus;
+
+        return Focus(
+          focusNode: focusNode,
+          onKeyEvent: (node, key) {
+            if (key.physicalKey == PhysicalKeyboardKey.escape) {
+              focusNode.unfocus();
+              return KeyEventResult.handled;
+            }
+            return KeyEventResult.ignored;
+          },
+          child: MouseRegion(
+            onEnter: (_) => setState(() => isHovering = true),
+            onExit: (_) => setState(() => isHovering = false),
+            // use a Stack to show the borders, to avoid the UI "moving" when hovering
+            child: isEditing ? editingChild : displayChild,
+          ),
+        );
+      },
+    );
+  }
+}
+
+class _Expandable extends StatelessWidget {
+  const _Expandable({
+    Key? key,
+    required this.isExpanded,
+    required this.isExpandable,
+    required this.title,
+  }) : super(key: key);
+
+  final StateController<bool>? isExpanded;
+  final bool isExpandable;
+  final Widget title;
+
+  @override
+  Widget build(BuildContext context) {
+    if (!isExpandable) {
+      return Align(
+        alignment: Alignment.centerLeft,
+        child: title,
+      );
+    }
+
+    final isExpanded = this.isExpanded!;
+
+    return GestureDetector(
+      onTap: () => isExpanded.state = !isExpanded.state,
+      behavior: HitTestBehavior.opaque,
+      child: Row(
+        children: [
+          TweenAnimationBuilder<double>(
+            tween: Tween(end: isExpanded.state ? 0 : -math.pi / 2),
+            duration: defaultDuration,
+            builder: (context, angle, _) {
+              return Transform.rotate(
+                angle: angle,
+                child: Icon(
+                  Icons.expand_more,
+                  size: defaultIconSize,
+                ),
+              );
+            },
+          ),
+          title,
+        ],
+      ),
+    );
+  }
+}
diff --git a/packages/provider_devtools_extension/lib/src/instance_viewer/result.dart b/packages/provider_devtools_extension/lib/src/instance_viewer/result.dart
new file mode 100644
index 00000000..746129b8
--- /dev/null
+++ b/packages/provider_devtools_extension/lib/src/instance_viewer/result.dart
@@ -0,0 +1,82 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:collection/collection.dart';
+import 'package:devtools_app_shared/service.dart';
+import 'package:flutter/foundation.dart';
+import 'package:vm_service/vm_service.dart' hide SentinelException, Error;
+
+import 'fake_freezed_annotation.dart';
+
+// This part is generated using `package:freezed`, but without the devtool depending
+// on the package.
+// To update the generated files, temporarily add freezed/freezed_annotation/build_runner
+// as dependencies; replace the `fake_freezed_annotation.dart` import with the
+// real annotation package, then execute `pub run build_runner build`.
+part 'result.freezed.dart';
+
+@freezed
+class Result<T> with _$Result<T> {
+  Result._();
+  factory Result.data(T value) = _ResultData<T>;
+  factory Result.error(Object error, StackTrace stackTrace) = _ResultError<T>;
+
+  factory Result.guard(T Function() cb) {
+    try {
+      return Result.data(cb());
+    } catch (err, stack) {
+      return Result.error(err, stack);
+    }
+  }
+
+  static Future<Result<T>> guardFuture<T>(Future<T> Function() cb) async {
+    try {
+      return Result.data(await cb());
+    } catch (err, stack) {
+      return Result.error(err, stack);
+    }
+  }
+
+  Result<Res> chain<Res>(Res Function(T value) cb) {
+    return when(
+      data: (value) {
+        try {
+          return Result.data(cb(value));
+        } catch (err, stack) {
+          return Result.error(err, stack);
+        }
+      },
+      error: (err, stack) => Result.error(err, stack),
+    );
+  }
+
+  T get dataOrThrow {
+    return when(
+      data: (value) => value,
+      error: Error.throwWithStackTrace,
+    );
+  }
+}
+
+Result<T> parseSentinel<T>(Object? value) {
+  if (value is T) return Result.data(value);
+
+  if (value == null) {
+    return Result.error(
+      ArgumentError(
+        'Expected $value to be an instance of $T but received `null`',
+      ),
+      StackTrace.current,
+    );
+  }
+
+  if (value is Sentinel) {
+    return Result.error(
+      SentinelException(value),
+      StackTrace.current,
+    );
+  }
+
+  return Result.error(value, StackTrace.current);
+}
diff --git a/packages/provider_devtools_extension/lib/src/instance_viewer/result.freezed.dart b/packages/provider_devtools_extension/lib/src/instance_viewer/result.freezed.dart
new file mode 100644
index 00000000..54635970
--- /dev/null
+++ b/packages/provider_devtools_extension/lib/src/instance_viewer/result.freezed.dart
@@ -0,0 +1,389 @@
+// coverage:ignore-file
+// GENERATED CODE - DO NOT MODIFY BY HAND
+// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target
+
+part of 'result.dart';
+
+// **************************************************************************
+// FreezedGenerator
+// **************************************************************************
+
+T _$identity<T>(T value) => value;
+
+final _privateConstructorUsedError = UnsupportedError(
+    'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more informations: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
+
+/// @nodoc
+class _$ResultTearOff {
+  const _$ResultTearOff();
+
+  _ResultData<T> data<T>(T value) {
+    return _ResultData<T>(
+      value,
+    );
+  }
+
+  _ResultError<T> error<T>(Object error, StackTrace stackTrace) {
+    return _ResultError<T>(
+      error,
+      stackTrace,
+    );
+  }
+}
+
+/// @nodoc
+const $Result = _$ResultTearOff();
+
+/// @nodoc
+mixin _$Result<T> {
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function(T value) data,
+    required TResult Function(Object error, StackTrace stackTrace) error,
+  }) =>
+      throw _privateConstructorUsedError;
+  @optionalTypeArgs
+  TResult? whenOrNull<TResult extends Object?>({
+    TResult Function(T value)? data,
+    TResult Function(Object error, StackTrace stackTrace)? error,
+  }) =>
+      throw _privateConstructorUsedError;
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function(T value)? data,
+    TResult Function(Object error, StackTrace stackTrace)? error,
+    required TResult orElse(),
+  }) =>
+      throw _privateConstructorUsedError;
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(_ResultData<T> value) data,
+    required TResult Function(_ResultError<T> value) error,
+  }) =>
+      throw _privateConstructorUsedError;
+  @optionalTypeArgs
+  TResult? mapOrNull<TResult extends Object?>({
+    TResult Function(_ResultData<T> value)? data,
+    TResult Function(_ResultError<T> value)? error,
+  }) =>
+      throw _privateConstructorUsedError;
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(_ResultData<T> value)? data,
+    TResult Function(_ResultError<T> value)? error,
+    required TResult orElse(),
+  }) =>
+      throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class $ResultCopyWith<T, $Res> {
+  factory $ResultCopyWith(Result<T> value, $Res Function(Result<T>) then) =
+      _$ResultCopyWithImpl<T, $Res>;
+}
+
+/// @nodoc
+class _$ResultCopyWithImpl<T, $Res> implements $ResultCopyWith<T, $Res> {
+  _$ResultCopyWithImpl(this._value, this._then);
+
+  final Result<T> _value;
+  // ignore: unused_field
+  final $Res Function(Result<T>) _then;
+}
+
+/// @nodoc
+abstract class _$ResultDataCopyWith<T, $Res> {
+  factory _$ResultDataCopyWith(
+          _ResultData<T> value, $Res Function(_ResultData<T>) then) =
+      __$ResultDataCopyWithImpl<T, $Res>;
+  $Res call({T value});
+}
+
+/// @nodoc
+class __$ResultDataCopyWithImpl<T, $Res> extends _$ResultCopyWithImpl<T, $Res>
+    implements _$ResultDataCopyWith<T, $Res> {
+  __$ResultDataCopyWithImpl(
+      _ResultData<T> _value, $Res Function(_ResultData<T>) _then)
+      : super(_value, (v) => _then(v as _ResultData<T>));
+
+  @override
+  _ResultData<T> get _value => super._value as _ResultData<T>;
+
+  @override
+  $Res call({
+    Object? value = freezed,
+  }) {
+    return _then(_ResultData<T>(
+      value == freezed
+          ? _value.value
+          : value // ignore: cast_nullable_to_non_nullable
+              as T,
+    ));
+  }
+}
+
+/// @nodoc
+
+class _$_ResultData<T> extends _ResultData<T> with DiagnosticableTreeMixin {
+  _$_ResultData(this.value) : super._();
+
+  @override
+  final T value;
+
+  @override
+  String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) {
+    return 'Result<$T>.data(value: $value)';
+  }
+
+  @override
+  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
+    super.debugFillProperties(properties);
+    properties
+      ..add(DiagnosticsProperty('type', 'Result<$T>.data'))
+      ..add(DiagnosticsProperty('value', value));
+  }
+
+  @override
+  bool operator ==(dynamic other) {
+    return identical(this, other) ||
+        (other.runtimeType == runtimeType &&
+            other is _ResultData<T> &&
+            const DeepCollectionEquality().equals(other.value, value));
+  }
+
+  @override
+  int get hashCode =>
+      Object.hash(runtimeType, const DeepCollectionEquality().hash(value));
+
+  @JsonKey(ignore: true)
+  @override
+  _$ResultDataCopyWith<T, _ResultData<T>> get copyWith =>
+      __$ResultDataCopyWithImpl<T, _ResultData<T>>(this, _$identity);
+
+  @override
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function(T value) data,
+    required TResult Function(Object error, StackTrace stackTrace) error,
+  }) {
+    return data(value);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult? whenOrNull<TResult extends Object?>({
+    TResult Function(T value)? data,
+    TResult Function(Object error, StackTrace stackTrace)? error,
+  }) {
+    return data?.call(value);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function(T value)? data,
+    TResult Function(Object error, StackTrace stackTrace)? error,
+    required TResult orElse(),
+  }) {
+    if (data != null) {
+      return data(value);
+    }
+    return orElse();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(_ResultData<T> value) data,
+    required TResult Function(_ResultError<T> value) error,
+  }) {
+    return data(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult? mapOrNull<TResult extends Object?>({
+    TResult Function(_ResultData<T> value)? data,
+    TResult Function(_ResultError<T> value)? error,
+  }) {
+    return data?.call(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(_ResultData<T> value)? data,
+    TResult Function(_ResultError<T> value)? error,
+    required TResult orElse(),
+  }) {
+    if (data != null) {
+      return data(this);
+    }
+    return orElse();
+  }
+}
+
+abstract class _ResultData<T> extends Result<T> {
+  factory _ResultData(T value) = _$_ResultData<T>;
+  _ResultData._() : super._();
+
+  T get value;
+  @JsonKey(ignore: true)
+  _$ResultDataCopyWith<T, _ResultData<T>> get copyWith =>
+      throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class _$ResultErrorCopyWith<T, $Res> {
+  factory _$ResultErrorCopyWith(
+          _ResultError<T> value, $Res Function(_ResultError<T>) then) =
+      __$ResultErrorCopyWithImpl<T, $Res>;
+  $Res call({Object error, StackTrace stackTrace});
+}
+
+/// @nodoc
+class __$ResultErrorCopyWithImpl<T, $Res> extends _$ResultCopyWithImpl<T, $Res>
+    implements _$ResultErrorCopyWith<T, $Res> {
+  __$ResultErrorCopyWithImpl(
+      _ResultError<T> _value, $Res Function(_ResultError<T>) _then)
+      : super(_value, (v) => _then(v as _ResultError<T>));
+
+  @override
+  _ResultError<T> get _value => super._value as _ResultError<T>;
+
+  @override
+  $Res call({
+    Object? error = freezed,
+    Object? stackTrace = freezed,
+  }) {
+    return _then(_ResultError<T>(
+      error == freezed
+          ? _value.error
+          : error // ignore: cast_nullable_to_non_nullable
+              as Object,
+      stackTrace == freezed
+          ? _value.stackTrace
+          : stackTrace // ignore: cast_nullable_to_non_nullable
+              as StackTrace,
+    ));
+  }
+}
+
+/// @nodoc
+
+class _$_ResultError<T> extends _ResultError<T> with DiagnosticableTreeMixin {
+  _$_ResultError(this.error, this.stackTrace) : super._();
+
+  @override
+  final Object error;
+  @override
+  final StackTrace stackTrace;
+
+  @override
+  String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) {
+    return 'Result<$T>.error(error: $error, stackTrace: $stackTrace)';
+  }
+
+  @override
+  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
+    super.debugFillProperties(properties);
+    properties
+      ..add(DiagnosticsProperty('type', 'Result<$T>.error'))
+      ..add(DiagnosticsProperty('error', error))
+      ..add(DiagnosticsProperty('stackTrace', stackTrace));
+  }
+
+  @override
+  bool operator ==(dynamic other) {
+    return identical(this, other) ||
+        (other.runtimeType == runtimeType &&
+            other is _ResultError<T> &&
+            const DeepCollectionEquality().equals(other.error, error) &&
+            const DeepCollectionEquality()
+                .equals(other.stackTrace, stackTrace));
+  }
+
+  @override
+  int get hashCode => Object.hash(
+      runtimeType,
+      const DeepCollectionEquality().hash(error),
+      const DeepCollectionEquality().hash(stackTrace));
+
+  @JsonKey(ignore: true)
+  @override
+  _$ResultErrorCopyWith<T, _ResultError<T>> get copyWith =>
+      __$ResultErrorCopyWithImpl<T, _ResultError<T>>(this, _$identity);
+
+  @override
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function(T value) data,
+    required TResult Function(Object error, StackTrace stackTrace) error,
+  }) {
+    return error(this.error, stackTrace);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult? whenOrNull<TResult extends Object?>({
+    TResult Function(T value)? data,
+    TResult Function(Object error, StackTrace stackTrace)? error,
+  }) {
+    return error?.call(this.error, stackTrace);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function(T value)? data,
+    TResult Function(Object error, StackTrace stackTrace)? error,
+    required TResult orElse(),
+  }) {
+    if (error != null) {
+      return error(this.error, stackTrace);
+    }
+    return orElse();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(_ResultData<T> value) data,
+    required TResult Function(_ResultError<T> value) error,
+  }) {
+    return error(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult? mapOrNull<TResult extends Object?>({
+    TResult Function(_ResultData<T> value)? data,
+    TResult Function(_ResultError<T> value)? error,
+  }) {
+    return error?.call(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(_ResultData<T> value)? data,
+    TResult Function(_ResultError<T> value)? error,
+    required TResult orElse(),
+  }) {
+    if (error != null) {
+      return error(this);
+    }
+    return orElse();
+  }
+}
+
+abstract class _ResultError<T> extends Result<T> {
+  factory _ResultError(Object error, StackTrace stackTrace) = _$_ResultError<T>;
+  _ResultError._() : super._();
+
+  Object get error;
+  StackTrace get stackTrace;
+  @JsonKey(ignore: true)
+  _$ResultErrorCopyWith<T, _ResultError<T>> get copyWith =>
+      throw _privateConstructorUsedError;
+}
diff --git a/packages/provider_devtools_extension/lib/src/provider_list.dart b/packages/provider_devtools_extension/lib/src/provider_list.dart
new file mode 100644
index 00000000..d564df44
--- /dev/null
+++ b/packages/provider_devtools_extension/lib/src/provider_list.dart
@@ -0,0 +1,128 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:devtools_app_shared/ui.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+
+import 'provider_nodes.dart';
+
+const _tilePadding = EdgeInsets.only(
+  left: defaultSpacing,
+  right: densePadding,
+  top: densePadding,
+  bottom: densePadding,
+);
+
+final AutoDisposeStateNotifierProvider<StateController<String?>, String?>
+    selectedProviderIdProvider =
+    AutoDisposeStateNotifierProvider<StateController<String?>, String?>(
+  (ref) {
+    final controller = StateController<String?>(null);
+
+    ref.listen<AsyncValue<List<ProviderNode>>>(
+      sortedProviderNodesProvider,
+      (prev, value) {
+        final nodes = value.asData?.value;
+        if (nodes == null) return;
+
+        if (controller.state == null) {
+          if (nodes.isNotEmpty) controller.state = nodes.first.id;
+          return;
+        }
+
+        if (nodes.isEmpty) {
+          controller.state = null;
+        }
+
+        /// The previously selected provider was unmounted
+        else if (!nodes.any((node) => node.id == controller.state)) {
+          controller.state = nodes.first.id;
+        }
+      },
+      fireImmediately: true,
+    );
+
+    return controller;
+  },
+  name: 'selectedProviderIdProvider',
+);
+
+class ProviderList extends ConsumerStatefulWidget {
+  const ProviderList({Key? key}) : super(key: key);
+
+  @override
+  ConsumerState<ConsumerStatefulWidget> createState() => _ProviderListState();
+}
+
+class _ProviderListState extends ConsumerState<ProviderList> {
+  final scrollController = ScrollController();
+
+  @override
+  void dispose() {
+    scrollController.dispose();
+    super.dispose();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    final nodes = ref.watch(sortedProviderNodesProvider);
+
+    return nodes.when(
+      loading: () => const Center(child: CircularProgressIndicator()),
+      error: (err, stack) => Padding(
+        padding: _tilePadding,
+        child: Text('<unknown error>\n$stack'),
+      ),
+      data: (nodes) {
+        return Scrollbar(
+          controller: scrollController,
+          thumbVisibility: true,
+          child: ListView.builder(
+            primary: false,
+            controller: scrollController,
+            itemCount: nodes.length,
+            itemBuilder: (context, index) {
+              final node = nodes[index];
+              return ProviderNodeItem(
+                key: Key('provider-${node.id}'),
+                node: node,
+              );
+            },
+          ),
+        );
+      },
+    );
+  }
+}
+
+class ProviderNodeItem extends ConsumerWidget {
+  const ProviderNodeItem({
+    Key? key,
+    required this.node,
+  }) : super(key: key);
+
+  final ProviderNode node;
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final isSelected = ref.watch(selectedProviderIdProvider) == node.id;
+
+    final colorScheme = Theme.of(context).colorScheme;
+    final backgroundColor =
+        isSelected ? colorScheme.selectedRowBackgroundColor : null;
+
+    return GestureDetector(
+      behavior: HitTestBehavior.opaque,
+      onTap: () {
+        ref.read(selectedProviderIdProvider.notifier).state = node.id;
+      },
+      child: Container(
+        color: backgroundColor,
+        padding: _tilePadding,
+        child: Text('${node.type}()'),
+      ),
+    );
+  }
+}
diff --git a/packages/provider_devtools_extension/lib/src/provider_nodes.dart b/packages/provider_devtools_extension/lib/src/provider_nodes.dart
new file mode 100644
index 00000000..11012680
--- /dev/null
+++ b/packages/provider_devtools_extension/lib/src/provider_nodes.dart
@@ -0,0 +1,108 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:devtools_app_shared/service.dart';
+import 'package:flutter/foundation.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:vm_service/vm_service.dart';
+
+import 'instance_viewer/eval.dart';
+
+@immutable
+class ProviderNode {
+  const ProviderNode({
+    required this.id,
+    required this.type,
+  });
+
+  final String id;
+  final String type;
+}
+
+final _providerListChanged = AutoDisposeStreamProvider<void>((ref) async* {
+  final service = await ref.watch(serviceProvider.future);
+
+  yield* service.onExtensionEvent.where((event) {
+    return event.extensionKind == 'provider:provider_list_changed';
+  });
+});
+
+final _rawProviderIdsProvider = AutoDisposeFutureProvider<List<String>>(
+  (ref) async {
+    // recompute the list of providers on hot-restart
+    ref.watch(hotRestartEventProvider);
+    // cause the list of providers to be re-evaluated when notified of a change
+    ref.watch(_providerListChanged);
+
+    final isAlive = Disposable();
+    ref.onDispose(isAlive.dispose);
+
+    final eval = await ref.watch(providerEvalProvider.future);
+
+    final providerIdRefs = await eval.evalInstance(
+      'ProviderBinding.debugInstance.providerDetails.keys.toList()',
+      isAlive: isAlive,
+    );
+
+    final providerIdInstances = await Future.wait([
+      for (final idRef in providerIdRefs.elements!.cast<InstanceRef>())
+        eval.safeGetInstance(idRef, isAlive),
+    ]);
+
+    return [
+      for (final idInstance in providerIdInstances) idInstance.valueAsString!,
+    ];
+  },
+  name: '_rawProviderIdsProvider',
+);
+
+final _rawProviderNodeProvider =
+    AutoDisposeFutureProviderFamily<ProviderNode, String>(
+  (ref, id) async {
+    // recompute the providers informations on hot-restart
+    ref.watch(hotRestartEventProvider);
+
+    final isAlive = Disposable();
+    ref.onDispose(isAlive.dispose);
+
+    final eval = await ref.watch(providerEvalProvider.future);
+
+    final providerNodeInstance = await eval.evalInstance(
+      "ProviderBinding.debugInstance.providerDetails['$id']",
+      isAlive: isAlive,
+    );
+
+    Future<Instance> getFieldWithName(String name) {
+      return eval.safeGetInstance(
+        providerNodeInstance.fields!
+            .firstWhere((e) => e.decl?.name == name)
+            .value as InstanceRef,
+        isAlive,
+      );
+    }
+
+    final type = await getFieldWithName('type');
+
+    return ProviderNode(
+      id: id,
+      type: type.valueAsString!,
+    );
+  },
+  name: '_rawProviderNodeProvider',
+);
+
+/// Combines [providerIdsProvider] with [providerNodeProvider] to obtain all
+/// the [ProviderNode]s at once, sorted alphabetically.
+final sortedProviderNodesProvider =
+    AutoDisposeFutureProvider<List<ProviderNode>>((ref) async {
+  final ids = await ref.watch(_rawProviderIdsProvider.future);
+
+  final nodes = await Future.wait<ProviderNode>(
+    ids.map((id) => ref.watch(_rawProviderNodeProvider(id).future)),
+  );
+
+  return nodes.toList()..sort((a, b) => a.type.compareTo(b.type));
+});
diff --git a/packages/provider_devtools_extension/lib/src/provider_screen.dart b/packages/provider_devtools_extension/lib/src/provider_screen.dart
new file mode 100644
index 00000000..db725cde
--- /dev/null
+++ b/packages/provider_devtools_extension/lib/src/provider_screen.dart
@@ -0,0 +1,160 @@
+import 'dart:async';
+
+import 'package:collection/collection.dart';
+import 'package:devtools_app_shared/ui.dart';
+import 'package:devtools_extensions/devtools_extensions.dart';
+import 'package:flutter/material.dart';
+
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+
+import 'instance_viewer/instance_details.dart';
+import 'instance_viewer/instance_providers.dart';
+import 'instance_viewer/instance_viewer.dart';
+import 'provider_list.dart';
+import 'provider_nodes.dart';
+
+final _hasErrorProvider = Provider.autoDispose<bool>((ref) {
+  if (ref.watch(sortedProviderNodesProvider) is AsyncError) return true;
+
+  final selectedProviderId = ref.watch(selectedProviderIdProvider);
+
+  if (selectedProviderId == null) return false;
+
+  final instance = ref.watch(
+    instanceProvider(InstancePath.fromProviderId(selectedProviderId)),
+  );
+
+  return instance is AsyncError;
+});
+
+final _selectedProviderNode = AutoDisposeProvider<ProviderNode?>((ref) {
+  final selectedId = ref.watch(selectedProviderIdProvider);
+
+  return ref.watch(sortedProviderNodesProvider).asData?.value.firstWhereOrNull(
+        (node) => node.id == selectedId,
+      );
+});
+
+final _showInternals = StateProvider<bool>((ref) => false);
+
+class ProviderScreenBody extends ConsumerWidget {
+  const ProviderScreenBody({Key? key}) : super(key: key);
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final splitAxis = Split.axisFor(context, 0.85);
+
+    // A provider will automatically be selected as soon as one is detected
+    final selectedProviderId = ref.watch(selectedProviderIdProvider);
+    final detailsTitleText = selectedProviderId != null
+        ? ref.watch(_selectedProviderNode)?.type ?? ''
+        : '[No provider selected]';
+
+    ref.listen<bool>(_hasErrorProvider, (_, hasError) {
+      if (hasError) showProviderErrorBanner();
+    });
+
+    return Split(
+      axis: splitAxis,
+      initialFractions: const [0.33, 0.67],
+      children: [
+        const RoundedOutlinedBorder(
+          clip: true,
+          child: Column(
+            children: [
+              AreaPaneHeader(
+                roundedTopBorder: false,
+                includeTopBorder: false,
+                title: Text('Providers'),
+              ),
+              Expanded(
+                child: ProviderList(),
+              ),
+            ],
+          ),
+        ),
+        RoundedOutlinedBorder(
+          clip: true,
+          child: Column(
+            children: [
+              AreaPaneHeader(
+                roundedTopBorder: false,
+                includeTopBorder: false,
+                title: Text(detailsTitleText),
+                actions: [
+                  IconButton(
+                    icon: const Icon(Icons.settings),
+                    onPressed: () {
+                      unawaited(
+                        showDialog(
+                          context: context,
+                          builder: (_) => _StateInspectorSettingsDialog(),
+                        ),
+                      );
+                    },
+                  ),
+                ],
+              ),
+              if (selectedProviderId != null)
+                Expanded(
+                  child: InstanceViewer(
+                    rootPath: InstancePath.fromProviderId(selectedProviderId),
+                    showInternalProperties: ref.watch(_showInternals),
+                  ),
+                ),
+            ],
+          ),
+        ),
+      ],
+    );
+  }
+}
+
+void showProviderErrorBanner() {
+  extensionManager.showBannerMessage(
+    key: 'provider_unknown_error',
+    type: 'error',
+    message: '''
+DevTools failed to connect with package:provider.
+
+This could be caused by an older version of package:provider; please make sure that you are using version >=5.0.0.''',
+    extensionName: 'provider',
+  );
+}
+
+class _StateInspectorSettingsDialog extends ConsumerWidget {
+  static const title = 'State inspector configurations';
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    return DevToolsDialog(
+      title: const DialogTitleText(title),
+      content: Column(
+        mainAxisSize: MainAxisSize.min,
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: [
+          InkWell(
+            onTap: () =>
+                ref.read(_showInternals.notifier).update((state) => !state),
+            child: Row(
+              children: [
+                Checkbox(
+                  value: ref.watch(_showInternals),
+                  onChanged: (_) => ref
+                      .read(_showInternals.notifier)
+                      .update((state) => !state),
+                ),
+                const Text(
+                  'Show private properties inherited from SDKs/packages',
+                ),
+              ],
+            ),
+          ),
+        ],
+      ),
+      actions: const [
+        DialogCloseButton(),
+      ],
+    );
+  }
+}
diff --git a/packages/provider_devtools_extension/lib/src/utils/riverpod_error_logger_observer.dart b/packages/provider_devtools_extension/lib/src/utils/riverpod_error_logger_observer.dart
new file mode 100644
index 00000000..01343997
--- /dev/null
+++ b/packages/provider_devtools_extension/lib/src/utils/riverpod_error_logger_observer.dart
@@ -0,0 +1,44 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:devtools_app_shared/service.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:logging/logging.dart';
+
+final _log = Logger('riverpod_error_logger_observer');
+
+class ErrorLoggerObserver extends ProviderObserver {
+  const ErrorLoggerObserver();
+
+  @override
+  void didAddProvider(
+    ProviderBase provider,
+    Object? value,
+    ProviderContainer container,
+  ) {
+    _maybeLogError(provider, value);
+  }
+
+  @override
+  void didUpdateProvider(
+    ProviderBase provider,
+    Object? previousValue,
+    Object? newValue,
+    ProviderContainer container,
+  ) {
+    _maybeLogError(provider, newValue);
+  }
+
+  void _maybeLogError(ProviderBase provider, Object? value) {
+    if (value is AsyncError) {
+      if (value.error is SentinelException) return;
+      _log.shout('Provider $provider failed with "${value.error}"');
+
+      final stackTrace = value.stackTrace;
+      if (stackTrace != null) {
+        _log.info(stackTrace);
+      }
+    }
+  }
+}
diff --git a/packages/provider_devtools_extension/lib/src/utils/sliver_iterable_child_delegate.dart b/packages/provider_devtools_extension/lib/src/utils/sliver_iterable_child_delegate.dart
new file mode 100644
index 00000000..da32e4c6
--- /dev/null
+++ b/packages/provider_devtools_extension/lib/src/utils/sliver_iterable_child_delegate.dart
@@ -0,0 +1,43 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/widgets.dart';
+
+/// A delegate that allows using ListView with an undetermined list length
+/// while preserve the "build only what is visible" behaviour.
+class SliverIterableChildDelegate extends SliverChildDelegate {
+  SliverIterableChildDelegate(
+    this.children, {
+    this.estimatedChildCount,
+  });
+
+  final Iterable<Widget?> children;
+  int? _lastAccessedIndex;
+  late Iterator<Widget?> _lastAccessedIterator;
+
+  @override
+  Widget? build(BuildContext context, int index) {
+    if (_lastAccessedIndex == null || _lastAccessedIndex! > index) {
+      _lastAccessedIndex = -1;
+      _lastAccessedIterator = children.iterator;
+    }
+
+    while (_lastAccessedIndex! < index) {
+      _lastAccessedIterator.moveNext();
+      _lastAccessedIndex = _lastAccessedIndex! + 1;
+    }
+
+    return _lastAccessedIterator.current;
+  }
+
+  @override
+  final int? estimatedChildCount;
+
+  @override
+  bool shouldRebuild(SliverIterableChildDelegate oldDelegate) {
+    return children != oldDelegate.children ||
+        _lastAccessedIndex != oldDelegate._lastAccessedIndex ||
+        _lastAccessedIterator != oldDelegate._lastAccessedIterator;
+  }
+}
diff --git a/packages/provider_devtools_extension/pubspec.yaml b/packages/provider_devtools_extension/pubspec.yaml
new file mode 100644
index 00000000..1011afff
--- /dev/null
+++ b/packages/provider_devtools_extension/pubspec.yaml
@@ -0,0 +1,32 @@
+name: provider_devtools_extension
+description: "The Flutter web app for the package:provider DevTools extension."
+publish_to: 'none'
+
+version: 0.0.1
+
+environment:
+  sdk: '>=3.0.0 <4.0.0'
+
+dependencies:
+  flutter:
+    sdk: flutter
+  cupertino_icons: ^1.0.2
+  collection: ^1.15.0
+  devtools_extensions: ^0.0.6
+  devtools_app_shared: ^0.0.4
+  # TODO: https://github.com/flutter/devtools/issues/4569 - unpin this version
+  flutter_riverpod: 2.0.0-dev.9
+  logging: ^1.1.1
+  vm_service: ">=11.9.0 <14.0.0"
+
+dev_dependencies:
+  flutter_driver:
+    sdk: flutter
+  flutter_test:
+    sdk: flutter
+  flutter_lints: ^2.0.0
+  integration_test:
+    sdk: flutter
+
+flutter:
+  uses-material-design: true
diff --git a/packages/provider_devtools_extension/test/provider_screen_test.dart b/packages/provider_devtools_extension/test/provider_screen_test.dart
new file mode 100644
index 00000000..dcd87bc5
--- /dev/null
+++ b/packages/provider_devtools_extension/test/provider_screen_test.dart
@@ -0,0 +1,408 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+@TestOn('vm')
+import 'package:provider_devtools_extension/src/provider_screen.dart';
+import 'package:provider_devtools_extension/src/instance_viewer/instance_details.dart';
+import 'package:provider_devtools_extension/src/instance_viewer/instance_providers.dart';
+import 'package:provider_devtools_extension/src/provider_list.dart';
+import 'package:provider_devtools_extension/src/provider_nodes.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+import 'test_utils.dart';
+
+void main() {
+  // Set a wide enough screen width that we do not run into overflow.
+  const windowSize = Size(2225.0, 1000.0);
+
+  late Widget providerScreen;
+
+  setUpAll(() async => await loadFonts());
+
+  setUp(() {
+    // setGlobal(IdeTheme, getIdeTheme());
+  });
+
+  setUp(() {
+    providerScreen = Container(
+      color: Colors.grey,
+      child: Directionality(
+        textDirection: TextDirection.ltr,
+        child: wrap(
+          const ProviderScreenBody(),
+        ),
+      ),
+    );
+  });
+
+  // TODO: add a test that verifies the add banner message request was posted
+  // to devtools via the extension manager.
+  // group('ProviderScreen', () {
+  //   testWidgetsWithWindowSize(
+  //     'shows ProviderUnknownErrorBanner if the devtool failed to fetch the list of providers',
+  //     windowSize,
+  //     (tester) async {
+  //       await tester.pumpWidget(
+  //         ProviderScope(
+  //           overrides: [
+  //             sortedProviderNodesProvider.overrideWithValue(
+  //               const AsyncValue.loading(),
+  //             ),
+  //           ],
+  //           child: providerScreen,
+  //         ),
+  //       );
+
+  //       await tester.pumpWidget(
+  //         ProviderScope(
+  //           overrides: [
+  //             sortedProviderNodesProvider.overrideWithValue(
+  //               AsyncValue.error(StateError('')),
+  //             ),
+  //           ],
+  //           child: providerScreen,
+  //         ),
+  //       );
+
+  //       // wait for the Banner to appear as it is mounted asynchronously
+  //       await tester.pump();
+
+  //       await expectLater(
+  //         find.byType(ProviderScreenBody),
+  //         matchesGoldenFile(
+  //           'goldens/provider_screen/list_error_banner.png',
+  //         ),
+  //       );
+  //     },
+  //   );
+  // });
+
+  group('selectedProviderIdProvider', () {
+    test('selects the first provider available', () async {
+      final container = ProviderContainer(
+        overrides: [
+          sortedProviderNodesProvider.overrideWithValue(
+            const AsyncValue.loading(),
+          ),
+        ],
+      );
+      addTearDown(container.dispose);
+
+      final sub = container.listen<String?>(
+        selectedProviderIdProvider,
+        (prev, next) {},
+      );
+
+      expect(sub.read(), isNull);
+
+      container.updateOverrides([
+        sortedProviderNodesProvider.overrideWithValue(
+          const AsyncValue.data([
+            ProviderNode(id: '0', type: 'Provider<A>'),
+            ProviderNode(id: '1', type: 'Provider<B>'),
+          ]),
+        ),
+      ]);
+
+      await container.pump();
+
+      expect(sub.read(), '0');
+    });
+
+    test('selects the first provider available after an error', () async {
+      final container = ProviderContainer(
+        overrides: [
+          sortedProviderNodesProvider.overrideWithValue(
+            AsyncValue.error(Error()),
+          ),
+        ],
+      );
+      addTearDown(container.dispose);
+
+      final sub = container.listen<String?>(
+        selectedProviderIdProvider,
+        (prev, next) {},
+      );
+
+      // wait for the error to be handled
+      await container.pump();
+
+      expect(sub.read(), isNull);
+
+      container.updateOverrides([
+        sortedProviderNodesProvider.overrideWithValue(
+          const AsyncValue.data([
+            ProviderNode(id: '0', type: 'Provider<A>'),
+            ProviderNode(id: '1', type: 'Provider<B>'),
+          ]),
+        ),
+      ]);
+
+      // wait for the ids update to be handled
+      await container.pump();
+
+      expect(sub.read(), '0');
+    });
+
+    test(
+      'When the currently selected provider is removed, selects the next first provider',
+      () {
+        final container = ProviderContainer(
+          overrides: [
+            sortedProviderNodesProvider.overrideWithValue(
+              const AsyncValue.data([
+                ProviderNode(id: '0', type: 'Provider<A>'),
+              ]),
+            ),
+          ],
+        );
+        addTearDown(container.dispose);
+
+        final sub = container.listen<String?>(
+          selectedProviderIdProvider,
+          (prev, next) {},
+        );
+
+        expect(sub.read(), '0');
+
+        container.updateOverrides([
+          sortedProviderNodesProvider.overrideWithValue(
+            const AsyncValue.data([
+              ProviderNode(id: '1', type: 'Provider<B>'),
+            ]),
+          ),
+        ]);
+
+        expect(sub.read(), '1');
+      },
+    );
+
+    test('Once a provider is selected, further updates are no-op', () async {
+      final container = ProviderContainer(
+        overrides: [
+          sortedProviderNodesProvider.overrideWithValue(
+            const AsyncValue.data([
+              ProviderNode(id: '0', type: 'Provider<A>'),
+            ]),
+          ),
+        ],
+      );
+      addTearDown(container.dispose);
+
+      final sub = container.listen<String?>(
+        selectedProviderIdProvider,
+        (prev, next) {},
+      );
+
+      await container.pump();
+
+      expect(sub.read(), '0');
+
+      container.updateOverrides([
+        sortedProviderNodesProvider.overrideWithValue(
+          // '0' is no-longer the first provider on purpose
+          const AsyncValue.data([
+            ProviderNode(id: '1', type: 'Provider<B>'),
+            ProviderNode(id: '0', type: 'Provider<A>'),
+          ]),
+        ),
+      ]);
+
+      await container.pump();
+
+      expect(sub.read(), '0');
+    });
+
+    test(
+      'when the list of providers becomes empty, the current provider is unselected '
+      ', then, the first provider will be selected when the list becomes non-empty again.',
+      () async {
+        final container = ProviderContainer(
+          overrides: [
+            sortedProviderNodesProvider.overrideWithValue(
+              const AsyncValue.data([
+                ProviderNode(id: '0', type: 'Provider<A>'),
+              ]),
+            ),
+          ],
+        );
+        addTearDown(container.dispose);
+
+        final sub = container.listen<String?>(
+          selectedProviderIdProvider,
+          (prev, next) {},
+        );
+
+        await container.pump();
+
+        expect(sub.read(), '0');
+
+        container.updateOverrides([
+          sortedProviderNodesProvider.overrideWithValue(
+            const AsyncValue.data([]),
+          ),
+        ]);
+
+        await container.pump();
+
+        expect(sub.read(), isNull);
+
+        container.updateOverrides([
+          sortedProviderNodesProvider.overrideWithValue(
+            const AsyncValue.data([
+              ProviderNode(id: '1', type: 'Provider<B>'),
+            ]),
+          ),
+        ]);
+
+        await container.pump();
+
+        expect(sub.read(), '1');
+      },
+    );
+  });
+
+  group('ProviderList', () {
+    List<Override> getOverrides() {
+      return [
+        instanceProvider(const InstancePath.fromProviderId('0'))
+            .overrideWithValue(
+          AsyncValue.data(
+            InstanceDetails.string(
+              'Value0',
+              instanceRefId: 'string/0',
+              setter: null,
+            ),
+          ),
+        ),
+      ];
+    }
+
+    testWidgetsWithWindowSize(
+      'selects the first provider the first time a provider is received',
+      windowSize,
+      (tester) async {
+        final container = ProviderContainer(
+          overrides: [
+            sortedProviderNodesProvider
+                .overrideWithValue(const AsyncValue.loading()),
+            ...getOverrides(),
+          ],
+        );
+
+        await tester.pumpWidget(
+          UncontrolledProviderScope(
+            container: container,
+            child: providerScreen,
+          ),
+        );
+
+        expect(container.read(selectedProviderIdProvider), isNull);
+        expect(find.byType(ProviderNodeItem), findsNothing);
+
+        await expectLater(
+          find.byType(ProviderScreenBody),
+          matchesGoldenFile(
+            'goldens/provider_screen/no_selected_provider.png',
+          ),
+        );
+
+        container.updateOverrides([
+          sortedProviderNodesProvider.overrideWithValue(
+            const AsyncValue.data([
+              ProviderNode(id: '0', type: 'Provider<A>'),
+              ProviderNode(id: '1', type: 'Provider<B>'),
+            ]),
+          ),
+          ...getOverrides(),
+        ]);
+
+        await tester.pump();
+
+        expect(container.read(selectedProviderIdProvider), '0');
+        expect(find.byType(ProviderNodeItem), findsNWidgets(2));
+        expect(
+          find.descendant(
+            of: find.byKey(const Key('provider-0')),
+            matching: find.text('Provider<A>()'),
+          ),
+          findsOneWidget,
+        );
+        expect(
+          find.descendant(
+            of: find.byKey(const Key('provider-1')),
+            matching: find.text('Provider<B>()'),
+          ),
+          findsOneWidget,
+        );
+
+        await expectLater(
+          find.byType(ProviderScreenBody),
+          matchesGoldenFile(
+            'goldens/provider_screen/selected_provider.png',
+          ),
+        );
+      },
+    );
+
+    // TODO: add a test that verifies the add banner message request was posted
+    // to devtools via the extension manager.
+    // testWidgetsWithWindowSize(
+    //   'shows ProviderUnknownErrorBanner if the devtool failed to fetch the selected provider',
+    //   windowSize,
+    //   (tester) async {
+    //     final overrides = [
+    //       sortedProviderNodesProvider.overrideWithValue(
+    //         const AsyncValue.data([
+    //           ProviderNode(id: '0', type: 'Provider<A>'),
+    //           ProviderNode(id: '1', type: 'Provider<B>'),
+    //         ]),
+    //       ),
+    //       ...getOverrides(),
+    //     ];
+
+    //     await tester.pumpWidget(
+    //       ProviderScope(
+    //         overrides: [
+    //           ...overrides,
+    //           instanceProvider(const InstancePath.fromProviderId('0'))
+    //               .overrideWithValue(const AsyncValue.loading()),
+    //         ],
+    //         child: providerScreen,
+    //       ),
+    //     );
+
+    //     await tester.pumpWidget(
+    //       ProviderScope(
+    //         overrides: [
+    //           ...overrides,
+    //           instanceProvider(const InstancePath.fromProviderId('0'))
+    //               .overrideWithValue(AsyncValue.error(Error())),
+    //         ],
+    //         child: providerScreen,
+    //       ),
+    //     );
+
+    //     // await for the modal to be mounted as it is rendered asynchronously
+    //     await tester.pump();
+
+    //     expect(
+    //       find.byKey(
+    //         Key('ProviderUnknownErrorBanner - ${ProviderScreen.id}'),
+    //       ),
+    //       findsOneWidget,
+    //     );
+
+    //     await expectLater(
+    //       find.byType(ProviderScreenBody),
+    //       matchesGoldenFile(
+    //         'goldens/provider_screen/selected_provider_error_banner.png',
+    //       ),
+    //     );
+    //   },
+    // );
+  });
+}
diff --git a/packages/provider_devtools_extension/test/test_utils.dart b/packages/provider_devtools_extension/test/test_utils.dart
new file mode 100644
index 00000000..316c87c6
--- /dev/null
+++ b/packages/provider_devtools_extension/test/test_utils.dart
@@ -0,0 +1,106 @@
+import 'dart:io';
+
+import 'package:devtools_app_shared/ui.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+Future<void> loadFonts() async {
+  // source: https://medium.com/swlh/test-your-flutter-widgets-using-golden-files-b533ac0de469
+
+  //https://github.com/flutter/flutter/issues/20907
+  if (Directory.current.path.endsWith('/test')) {
+    Directory.current = Directory.current.parent;
+  }
+
+  const fonts = {
+    'Roboto': [
+      'fonts/Roboto/Roboto-Thin.ttf',
+      'fonts/Roboto/Roboto-Light.ttf',
+      'fonts/Roboto/Roboto-Regular.ttf',
+      'fonts/Roboto/Roboto-Medium.ttf',
+      'fonts/Roboto/Roboto-Bold.ttf',
+      'fonts/Roboto/Roboto-Black.ttf',
+    ],
+    'RobotoMono': [
+      'fonts/Roboto_Mono/RobotoMono-Thin.ttf',
+      'fonts/Roboto_Mono/RobotoMono-Light.ttf',
+      'fonts/Roboto_Mono/RobotoMono-Regular.ttf',
+      'fonts/Roboto_Mono/RobotoMono-Medium.ttf',
+      'fonts/Roboto_Mono/RobotoMono-Bold.ttf',
+    ],
+    'Octicons': ['fonts/Octicons.ttf'],
+    // 'Codicon': ['packages/codicon/font/codicon.ttf']
+  };
+
+  final loadFontsFuture = fonts.entries.map((entry) async {
+    final loader = FontLoader(entry.key);
+
+    for (final path in entry.value) {
+      final fontData = File(path).readAsBytes().then((bytes) {
+        return ByteData.view(Uint8List.fromList(bytes).buffer);
+      });
+
+      loader.addFont(fontData);
+    }
+
+    await loader.load();
+  });
+
+  await Future.wait(loadFontsFuture);
+}
+
+// NOTE: the below helpers are duplicated from
+// `flutter/devtools/packages/devtools_test`. We copied because `devtools_test`
+// is not published on pub.dev for us to import.
+
+/// Wraps [widget] with the build context it needs to load in a test.
+///
+/// This includes a [MaterialApp] to provide context like [Theme.of], a
+/// [Material] to support elements like [TextField] that draw ink effects, and a
+/// [Directionality] to support [RenderFlex] widgets like [Row] and [Column].
+Widget wrap(Widget widget) {
+  return MaterialApp(
+    theme: themeFor(
+      isDarkTheme: false,
+      ideTheme: IdeTheme(),
+      theme: ThemeData(
+        useMaterial3: true,
+        colorScheme: lightColorScheme,
+      ),
+    ),
+    home: Directionality(
+      textDirection: TextDirection.ltr,
+      child: widget,
+    ),
+  );
+}
+
+/// Runs a test with the size of the app window under test to [windowSize].
+void testWidgetsWithWindowSize(
+  String name,
+  Size windowSize,
+  WidgetTesterCallback test, {
+  bool skip = false,
+}) {
+  testWidgets(
+    name,
+    (WidgetTester tester) async {
+      await _setWindowSize(tester, windowSize);
+      await test(tester);
+      await _resetWindowSize(tester);
+    },
+    skip: skip,
+  );
+}
+
+Future<void> _setWindowSize(WidgetTester tester, Size windowSize) async {
+  final binding = TestWidgetsFlutterBinding.ensureInitialized();
+  await binding.setSurfaceSize(windowSize);
+  tester.view.physicalSize = windowSize;
+  tester.view.devicePixelRatio = 1.0;
+}
+
+Future<void> _resetWindowSize(WidgetTester tester) async {
+  await _setWindowSize(tester, const Size(800.0, 600.0));
+}
diff --git a/packages/provider_devtools_extension/test_driver/integration_test.dart b/packages/provider_devtools_extension/test_driver/integration_test.dart
new file mode 100644
index 00000000..be070e2b
--- /dev/null
+++ b/packages/provider_devtools_extension/test_driver/integration_test.dart
@@ -0,0 +1,7 @@
+import 'package:flutter_driver/flutter_driver.dart';
+import 'package:integration_test/integration_test_driver_extended.dart';
+
+Future<void> main() async {
+  final driver = await FlutterDriver.connect();
+  await integrationDriver(driver: driver);
+}
diff --git a/packages/provider_devtools_extension/web/favicon.png b/packages/provider_devtools_extension/web/favicon.png
new file mode 100644
index 00000000..8aaa46ac
Binary files /dev/null and b/packages/provider_devtools_extension/web/favicon.png differ
diff --git a/packages/provider_devtools_extension/web/icons/Icon-192.png b/packages/provider_devtools_extension/web/icons/Icon-192.png
new file mode 100644
index 00000000..b749bfef
Binary files /dev/null and b/packages/provider_devtools_extension/web/icons/Icon-192.png differ
diff --git a/packages/provider_devtools_extension/web/icons/Icon-512.png b/packages/provider_devtools_extension/web/icons/Icon-512.png
new file mode 100644
index 00000000..88cfd48d
Binary files /dev/null and b/packages/provider_devtools_extension/web/icons/Icon-512.png differ
diff --git a/packages/provider_devtools_extension/web/icons/Icon-maskable-192.png b/packages/provider_devtools_extension/web/icons/Icon-maskable-192.png
new file mode 100644
index 00000000..eb9b4d76
Binary files /dev/null and b/packages/provider_devtools_extension/web/icons/Icon-maskable-192.png differ
diff --git a/packages/provider_devtools_extension/web/icons/Icon-maskable-512.png b/packages/provider_devtools_extension/web/icons/Icon-maskable-512.png
new file mode 100644
index 00000000..d69c5669
Binary files /dev/null and b/packages/provider_devtools_extension/web/icons/Icon-maskable-512.png differ
diff --git a/packages/provider_devtools_extension/web/index.html b/packages/provider_devtools_extension/web/index.html
new file mode 100644
index 00000000..402143db
--- /dev/null
+++ b/packages/provider_devtools_extension/web/index.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <!--
+    If you are serving your web app in a path other than the root, change the
+    href value below to reflect the base path you are serving from.
+
+    The path provided below has to start and end with a slash "/" in order for
+    it to work correctly.
+
+    For more details:
+    * https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
+
+    This is a placeholder for base href that will be replaced by the value of
+    the `--base-href` argument provided to `flutter build`.
+  -->
+  <base href="$FLUTTER_BASE_HREF">
+
+  <meta charset="UTF-8">
+  <meta content="IE=Edge" http-equiv="X-UA-Compatible">
+  <meta name="description" content=""A new Flutter project."">
+
+  <!-- iOS meta tags & icons -->
+  <meta name="apple-mobile-web-app-capable" content="yes">
+  <meta name="apple-mobile-web-app-status-bar-style" content="black">
+  <meta name="apple-mobile-web-app-title" content="provider_devtools_extension">
+  <link rel="apple-touch-icon" href="icons/Icon-192.png">
+
+  <!-- Favicon -->
+  <link rel="icon" type="image/png" href="favicon.png"/>
+
+  <title>provider_devtools_extension</title>
+  <link rel="manifest" href="manifest.json">
+
+  <script>
+    // The value below is injected by flutter build, do not touch.
+    const serviceWorkerVersion = null;
+  </script>
+  <!-- This script adds the flutter initialization JS code -->
+  <script src="flutter.js" defer></script>
+</head>
+<body>
+  <script>
+    window.addEventListener('load', function(ev) {
+      // Download main.dart.js
+      _flutter.loader.loadEntrypoint({
+        serviceWorker: {
+          serviceWorkerVersion: serviceWorkerVersion,
+        },
+        onEntrypointLoaded: function(engineInitializer) {
+          engineInitializer.initializeEngine().then(function(appRunner) {
+            appRunner.runApp();
+          });
+        }
+      });
+    });
+  </script>
+</body>
+</html>
diff --git a/packages/provider_devtools_extension/web/manifest.json b/packages/provider_devtools_extension/web/manifest.json
new file mode 100644
index 00000000..57825ce7
--- /dev/null
+++ b/packages/provider_devtools_extension/web/manifest.json
@@ -0,0 +1,35 @@
+{
+    "name": "provider_devtools_extension",
+    "short_name": "provider_devtools_extension",
+    "start_url": ".",
+    "display": "standalone",
+    "background_color": "#0175C2",
+    "theme_color": "#0175C2",
+    "description": ""A new Flutter project."",
+    "orientation": "portrait-primary",
+    "prefer_related_applications": false,
+    "icons": [
+        {
+            "src": "icons/Icon-192.png",
+            "sizes": "192x192",
+            "type": "image/png"
+        },
+        {
+            "src": "icons/Icon-512.png",
+            "sizes": "512x512",
+            "type": "image/png"
+        },
+        {
+            "src": "icons/Icon-maskable-192.png",
+            "sizes": "192x192",
+            "type": "image/png",
+            "purpose": "maskable"
+        },
+        {
+            "src": "icons/Icon-maskable-512.png",
+            "sizes": "512x512",
+            "type": "image/png",
+            "purpose": "maskable"
+        }
+    ]
+}
diff --git a/resources/translations/bn_BD/README.md b/resources/translations/bn_BD/README.md
index 8878311c..88f94f63 100644
--- a/resources/translations/bn_BD/README.md
+++ b/resources/translations/bn_BD/README.md
@@ -1,4 +1,4 @@
-[English](https://github.com/rrousselGit/provider/blob/master/README.md)| [French](https://github.com/rrousselGit/provider/blob/master/resources/translations/fr_FR/README.md) | [Português](https://github.com/rrousselGit/provider/blob/master/resources/translations/pt_br/README.md) | [简体中文](https://github.com/rrousselGit/provider/blob/master/resources/translations/zh-CN/README.md) | [Español](https://github.com/rrousselGit/provider/blob/master/resources/translations/es_MX/README.md) | [한국어](https://github.com/rrousselGit/provider/blob/master/resources/translations/ko-KR/README.md) | [বাংলা](/resources/translations/bn_BD/README.md) | [日本語](https://github.com/rrousselGit/provider/blob/master/resources/translations/ja_JP/README.md)
+[English](https://github.com/rrousselGit/provider/blob/master/packages/provider/README.md) | [French](https://github.com/rrousselGit/provider/blob/master/resources/translations/fr_FR/README.md) | [Português](https://github.com/rrousselGit/provider/blob/master/resources/translations/pt_br/README.md) | [简体中文](https://github.com/rrousselGit/provider/blob/master/resources/translations/zh-CN/README.md) | [Español](https://github.com/rrousselGit/provider/blob/master/resources/translations/es_MX/README.md) | [한국어](https://github.com/rrousselGit/provider/blob/master/resources/translations/ko-KR/README.md) | [বাংলা](https://github.com/rrousselGit/provider/blob/master/resources/translations/bn_BD/README.md) | [日本語](https://github.com/rrousselGit/provider/blob/master/resources/translations/ja_JP/README.md) | [Turkish](https://github.com/rrousselGit/provider/blob/master/resources/translations/tr_TR/README.md) | [Italian](https://github.com/rrousselGit/provider/blob/master/resources/translations/it_IT/README.md)
 
 <a href="https://github.com/rrousselGit/provider/actions"><img src="https://github.com/rrousselGit/provider/workflows/Build/badge.svg" alt="Build Status"></a>
 [![codecov](https://codecov.io/gh/rrousselGit/provider/branch/master/graph/badge.svg)](https://codecov.io/gh/rrousselGit/provider) <a href="https://discord.gg/Bbumvej"><img src="https://img.shields.io/discord/765557403865186374.svg?logo=discord&color=blue" alt="Discord"></a>
diff --git a/resources/translations/es_MX/README.md b/resources/translations/es_MX/README.md
index 245b2ff7..418a8cb5 100644
--- a/resources/translations/es_MX/README.md
+++ b/resources/translations/es_MX/README.md
@@ -1,4 +1,4 @@
-[English](https://github.com/rrousselGit/provider/blob/master/README.md) | [French](https://github.com/rrousselGit/provider/blob/master/resources/translations/fr_FR/README.md) | [Português](https://github.com/rrousselGit/provider/blob/master/resources/translations/pt_br/README.md) | [简体中文](https://github.com/rrousselGit/provider/blob/master/resources/translations/zh-CN/README.md) | [Español](./resources/translations/es_MX/README.md) | [한국어](https://github.com/rrousselGit/provider/blob/master/resources/translations/ko-KR/README.md) | [বাংলা](/resources/translations/bn_BD/README.md) | [日本語](https://github.com/rrousselGit/provider/blob/master/resources/translations/ja_JP/README.md)
+[English](https://github.com/rrousselGit/provider/blob/master/packages/provider/README.md) | [French](https://github.com/rrousselGit/provider/blob/master/resources/translations/fr_FR/README.md) | [Português](https://github.com/rrousselGit/provider/blob/master/resources/translations/pt_br/README.md) | [简体中文](https://github.com/rrousselGit/provider/blob/master/resources/translations/zh-CN/README.md) | [Español](https://github.com/rrousselGit/provider/blob/master/resources/translations/es_MX/README.md) | [한국어](https://github.com/rrousselGit/provider/blob/master/resources/translations/ko-KR/README.md) | [বাংলা](https://github.com/rrousselGit/provider/blob/master/resources/translations/bn_BD/README.md) | [日本語](https://github.com/rrousselGit/provider/blob/master/resources/translations/ja_JP/README.md) | [Turkish](https://github.com/rrousselGit/provider/blob/master/resources/translations/tr_TR/README.md) | [Italian](https://github.com/rrousselGit/provider/blob/master/resources/translations/it_IT/README.md)
 
 <a href="https://github.com/rrousselGit/provider/actions"><img src="https://github.com/rrousselGit/provider/workflows/Build/badge.svg" alt="Build Status"></a>
 [![codecov](https://codecov.io/gh/rrousselGit/provider/branch/master/graph/badge.svg)](https://codecov.io/gh/rrousselGit/provider) [![Gitter](https://badges.gitter.im/flutter_provider/community.svg)](https://gitter.im/flutter_provider/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
diff --git a/resources/translations/fr_FR/README.md b/resources/translations/fr_FR/README.md
index dbb42eaf..c7d1c73b 100644
--- a/resources/translations/fr_FR/README.md
+++ b/resources/translations/fr_FR/README.md
@@ -1,4 +1,4 @@
-[English](https://github.com/rrousselGit/provider/blob/master/README.md) | [French](https://github.com/rrousselGit/provider/blob/master/resources/translations/fr_FR/README.md) | [Português](https://github.com/rrousselGit/provider/blob/master/resources/translations/pt_br/README.md) | [简体中文](https://github.com/rrousselGit/provider/blob/master/resources/translations/zh-CN/README.md) | [Español](https://github.com/rrousselGit/provider/blob/master/resources/translations/es_MX/README.md) | [한국어](https://github.com/rrousselGit/provider/blob/master/resources/translations/ko-KR/README.md) | [বাংলা](/resources/translations/bn_BD/README.md) | [日本語](https://github.com/rrousselGit/provider/blob/master/resources/translations/ja_JP/README.md)
+[English](https://github.com/rrousselGit/provider/blob/master/packages/provider/README.md) | [French](https://github.com/rrousselGit/provider/blob/master/resources/translations/fr_FR/README.md) | [Português](https://github.com/rrousselGit/provider/blob/master/resources/translations/pt_br/README.md) | [简体中文](https://github.com/rrousselGit/provider/blob/master/resources/translations/zh-CN/README.md) | [Español](https://github.com/rrousselGit/provider/blob/master/resources/translations/es_MX/README.md) | [한국어](https://github.com/rrousselGit/provider/blob/master/resources/translations/ko-KR/README.md) | [বাংলা](https://github.com/rrousselGit/provider/blob/master/resources/translations/bn_BD/README.md) | [日本語](https://github.com/rrousselGit/provider/blob/master/resources/translations/ja_JP/README.md) | [Turkish](https://github.com/rrousselGit/provider/blob/master/resources/translations/tr_TR/README.md) | [Italian](https://github.com/rrousselGit/provider/blob/master/resources/translations/it_IT/README.md)
 
 <a href="https://github.com/rrousselGit/provider/actions"><img src="https://github.com/rrousselGit/provider/workflows/Build/badge.svg" alt="Build Status"></a>
 [![codecov](https://codecov.io/gh/rrousselGit/provider/branch/master/graph/badge.svg)](https://codecov.io/gh/rrousselGit/provider) <a href="https://discord.gg/Bbumvej"><img src="https://img.shields.io/discord/765557403865186374.svg?logo=discord&color=blue" alt="Discord"></a>
diff --git a/resources/translations/it_IT/README.md b/resources/translations/it_IT/README.md
new file mode 100644
index 00000000..a2c95f39
--- /dev/null
+++ b/resources/translations/it_IT/README.md
@@ -0,0 +1,733 @@
+[English](https://github.com/rrousselGit/provider/blob/master/packages/provider/README.md) | [French](https://github.com/rrousselGit/provider/blob/master/resources/translations/fr_FR/README.md) | [Português](https://github.com/rrousselGit/provider/blob/master/resources/translations/pt_br/README.md) | [简体中文](https://github.com/rrousselGit/provider/blob/master/resources/translations/zh-CN/README.md) | [Español](https://github.com/rrousselGit/provider/blob/master/resources/translations/es_MX/README.md) | [한국어](https://github.com/rrousselGit/provider/blob/master/resources/translations/ko-KR/README.md) | [বাংলা](https://github.com/rrousselGit/provider/blob/master/resources/translations/bn_BD/README.md) | [日本語](https://github.com/rrousselGit/provider/blob/master/resources/translations/ja_JP/README.md) | [Turkish](https://github.com/rrousselGit/provider/blob/master/resources/translations/tr_TR/README.md) | [Italian](https://github.com/rrousselGit/provider/blob/master/resources/translations/it_IT/README.md)
+
+<a href="https://github.com/rrousselGit/provider/actions"><img src="https://github.com/rrousselGit/provider/workflows/Build/badge.svg" alt="Build Status"></a>
+[![codecov](https://codecov.io/gh/rrousselGit/provider/branch/master/graph/badge.svg)](https://codecov.io/gh/rrousselGit/provider) <a href="https://discord.gg/Bbumvej"><img src="https://img.shields.io/discord/765557403865186374.svg?logo=discord&color=blue" alt="Discord"></a>
+
+[<img src="https://raw.githubusercontent.com/rrousselGit/provider/master/resources/flutter_favorite.png" width="200" />](https://flutter.dev/docs/development/packages-and-plugins/favorites)
+
+Un wrapper attorno a [InheritedWidget] per renderli più facili da usare e più riutilizzabili.
+
+Utilizzando `provider` invece di scrivere manualmente [InheritedWidget], ottieni:
+
+- una gestione semplificata dell'allocazione e della rimozione delle risorse
+- caricamento ritardato (lazy-loading)
+- una drastica riduzione del codice boilerplate rispetto alla creazione di una nuova classe ogni volta
+- compatibilità con gli strumenti di sviluppo – utilizzando Provider, lo stato della tua applicazione sarà visibile negli strumenti di sviluppo di Flutter
+- un modo comune per consumare questi [InheritedWidget] (Vedi [Provider.of]/[Consumer]/[Selector])
+- una maggiore scalabilità per le classi con un meccanismo di ascolto che cresce esponenzialmente in complessità (come [ChangeNotifier], che è O(N) per l'invio di notifiche).
+
+Per ulteriori informazioni su `provider`, consulta la sua [documentazione](https://pub.dev/documentation/provider/latest/provider/provider-library.html).
+
+Vedi anche:
+
+- [La documentazione ufficiale di Flutter sulla gestione dello stato](https://flutter.dev/docs/development/data-and-backend/state-mgmt/simple), che mostra come usare `provider` + [ChangeNotifier]
+- [Esempio di architettura Flutter](https://github.com/brianegan/flutter_architecture_samples/tree/master/change_notifier_provider), che contiene un'implementazione di quell'app utilizzando `provider` + [ChangeNotifier]
+- [flutter_bloc](https://github.com/felangel/bloc) e [Mobx](https://github.com/mobxjs/mobx.dart), che utilizzano un `provider` nella loro architettura
+
+## Migrazione da 4.x.x a 5.0.0-nullsafety
+
+- `initialData` per entrambi `FutureProvider` e `StreamProvider` ora è richiesto.
+
+  Per migrare, ciò che prima era:
+
+  ```dart
+  FutureProvider<int>(
+    create: (context) => Future.value(42),
+    child: MyApp(),
+  )
+
+  Widget build(BuildContext context) {
+    final value = context.watch<int>();
+    return Text('$value');
+  }
+  ```
+
+  ora è:
+
+  ```dart
+  FutureProvider<int?>(
+    initialValue: null,
+    create: (context) => Future.value(42),
+    child: MyApp(),
+  )
+
+  Widget build(BuildContext context) {
+    // assicurati di specificare ? in watch<int?>
+    final value = context.watch<int?>();
+    return Text('$value');
+  }
+  ```
+
+  - `ValueListenableProvider` è stato rimosso
+
+  Per migrare, puoi invece usare `Provider` combinato con `ValueListenableBuilder`:
+
+  ```dart
+  ValueListenableBuilder<int>(
+    valueListenable: myValueListenable,
+    builder: (context, value, _) {
+      return Provider<int>.value(
+        value: value,
+        child: MyApp(),
+      );
+    }
+  )
+  ```
+
+## Utilizzo
+
+### Esporre un valore
+
+#### Esporre una nuova istanza di un oggetto
+
+I provider ti permettono non solo di esporre un valore, ma anche di crearlo, ascoltarlo e disporne.
+
+Per esporre un oggetto appena creato, utilizza il costruttore predefinito di un provider.
+Non utilizzare il costruttore `.value` se desideri **creare** un oggetto, altrimenti potresti avere effetti collaterali indesiderati.
+
+Consulta [questa risposta su StackOverflow](https://stackoverflow.com/questions/52249578/how-to-deal-with-unwanted-widget-build) che spiega perché l'utilizzo del costruttore `.value` per creare valori è sconsigliato.
+
+- **DEVI** creare un nuovo oggetto all'interno di `create`.
+
+```dart
+Provider(
+  create: (_) => MyModel(),
+  child: ...
+)
+```
+
+- **NON** usare `Provider.value` per creare il tuo oggetto.
+
+```dart
+ChangeNotifierProvider.value(
+  value: MyModel(),
+  child: ...
+)
+```
+
+- **NON** creare il tuo oggetto a partire da variabili che possono cambiare nel tempo.
+
+  In una situazione del genere, il tuo oggetto non si aggiornerebbe mai quando il valore cambia.
+
+```dart
+int count;
+
+Provider(
+  create: (_) => MyModel(count),
+  child: ...
+)
+```
+
+Se desideri passare variabili che possono cambiare nel tempo al tuo oggetto,
+considera l'uso di `ProxyProvider`:
+
+```dart
+int count;
+
+ProxyProvider0(
+  update: (_, __) => MyModel(count),
+  child: ...
+)
+```
+
+**NOTA**:
+
+Quando si utilizza la callback `create`/`update` di un provider, è importante notare che questa callback viene chiamata in modo lazy (pigro) per impostazione predefinita.
+
+Ciò significa che finché il valore non viene richiesto almeno una volta, le callback `create`/`update` non verranno chiamate.
+
+Questo comportamento può essere disabilitato se si desidera pre-calcolare una logica, utilizzando il parametro `lazy`:
+
+```dart
+MyProvider(
+  create: (_) => Something(),
+  lazy: false,
+)
+```
+
+#### Riutilizzare un'istanza di oggetto esistente:
+
+Se hai già un'istanza di un oggetto e vuoi esporla, è meglio utilizzare il costruttore `.value` di un provider.
+
+Se non lo fai, potresti chiamare il metodo `dispose` del tuo oggetto mentre è ancora in uso.
+
+- **DEVI** utilizzare `ChangeNotifierProvider.value` per fornire un [ChangeNotifier] esistente.
+
+```dart
+MyChangeNotifier variable;
+
+ChangeNotifierProvider.value(
+  value: variable,
+  child: ...
+)
+```
+
+- **NON** riutilizzare un [ChangeNotifier] esistente utilizzando il costruttore predefinito
+
+```dart
+MyChangeNotifier variable;
+
+ChangeNotifierProvider(
+  create: (_) => variable,
+  child: ...
+)
+```
+
+### Leggere un valore
+
+Il modo più semplice per leggere un valore è utilizzare i metodi di estensione su [BuildContext]:
+
+- `context.watch<T>()`, che fa sì che il widget ascolti le modifiche su `T`
+- `context.read<T>()`, che restituisce `T` senza ascoltarlo
+- `context.select<T, R>(R cb(T value))`, che consente a un widget di ascoltare solo una piccola parte di `T`.
+
+Si può anche usare il metodo statico `Provider.of<T>(context)`, che si comporterà in modo simile a `watch`. Quando il parametro `listen` è impostato su `false` (come in `Provider.of<T>(context, listen: false)`), si comporterà in modo simile a `read`.
+
+Vale la pena notare che `context.read<T>()` non farà ricostruire un widget quando il valore cambia e non può essere chiamato all'interno di `StatelessWidget.build`/`State.build`.
+D'altra parte, può essere chiamato liberamente al di fuori di questi metodi.
+
+Questi metodi cercheranno nell'albero dei widget a partire dal widget associato al `BuildContext` passato e restituiranno la variabile più vicina di tipo `T` trovata (o lanceranno un'eccezione se nulla viene trovato).
+
+Questa operazione è O(1). Non comporta l'attraversamento dell'albero dei widget.
+
+Combinato con il primo esempio di [esporre un valore](#esporre-un-valore), questo widget leggerà la `String` esposta e renderizzerà "Hello World."
+
+```dart
+class Home extends StatelessWidget {
+  @override
+  Widget build(BuildContext context) {
+    return Text(
+      // Non dimenticare di passare il tipo di oggetto che vuoi ottenere a `watch`!
+      context.watch<String>(),
+    );
+  }
+}
+```
+
+In alternativa, invece di utilizzare questi metodi, possiamo usare [Consumer] e [Selector].
+
+Questi possono essere utili per ottimizzazioni delle prestazioni o quando è difficile
+ottenere un `BuildContext` discendente del provider.
+
+Consulta le [FAQ](https://github.com/rrousselGit/provider#my-widget-rebuilds-too-often-what-can-i-do)
+o la documentazione di [Consumer](https://pub.dev/documentation/provider/latest/provider/Consumer-class.html)
+e [Selector](https://pub.dev/documentation/provider/latest/provider/Selector-class.html)
+per ulteriori informazioni.
+
+### Dipendere facoltativamente da un provider
+
+A volte, potremmo voler supportare casi in cui un provider non esiste. Un
+esempio potrebbe essere per widget riutilizzabili che potrebbero essere utilizzati in varie posizioni,
+anche al di fuori di un provider.
+
+Per fare ciò, quando chiami `context.watch`/`context.read`, rendi il tipo generico
+nullable. In modo tale che invece di:
+
+```dart
+context.watch<Model>()
+```
+
+che lancerà un `ProviderNotFoundException` se non vengono trovati provider corrispondenti, fai:
+
+```dart
+context.watch<Model?>()
+```
+
+che cercherà di ottenere un provider corrispondente. Ma se nessuno viene trovato,
+verrà restituito `null` invece di lanciare un'eccezione.
+
+### MultiProvider
+
+Quando si iniettano molti valori in applicazioni grandi, `Provider` può diventare rapidamente
+piuttosto annidato:
+
+```dart
+Provider<Something>(
+  create: (_) => Something(),
+  child: Provider<SomethingElse>(
+    create: (_) => SomethingElse(),
+    child: Provider<AnotherThing>(
+      create: (_) => AnotherThing(),
+      child: someWidget,
+    ),
+  ),
+),
+```
+
+In:
+
+```dart
+MultiProvider(
+  providers: [
+    Provider<Something>(create: (_) => Something()),
+    Provider<SomethingElse>(create: (_) => SomethingElse()),
+    Provider<AnotherThing>(create: (_) => AnotherThing()),
+  ],
+  child: someWidget,
+)
+``` 
+
+Il comportamento di entrambi gli esempi è strettamente lo stesso. `MultiProvider` cambia solo
+l'aspetto del codice.
+
+### ProxyProvider
+
+A partire dalla versione 3.0.0, esiste un nuovo tipo di provider: `ProxyProvider`.
+
+`ProxyProvider` è un provider che combina più valori da altri provider in un nuovo oggetto e invia il risultato a `Provider`.
+
+Questo nuovo oggetto verrà aggiornato ogni volta che uno dei provider da cui dipendiamo viene aggiornato.
+
+Il seguente esempio utilizza `ProxyProvider` per costruire traduzioni basate su un contatore proveniente da un altro provider.
+
+```dart
+Widget build(BuildContext context) {
+  return MultiProvider(
+    providers: [
+      ChangeNotifierProvider(create: (_) => Counter()),
+      ProxyProvider<Counter, Translations>(
+        update: (_, counter, __) => Translations(counter.value),
+      ),
+    ],
+    child: Foo(),
+  );
+}
+
+class Translations {
+  const Translations(this._value);
+
+  final int _value;
+
+  String get title => 'You clicked $_value times';
+}
+```
+
+Esistono diverse varianti, come:
+
+- `ProxyProvider` vs `ProxyProvider2` vs `ProxyProvider3`, ...
+
+  Il numero dopo il nome della classe indica quanti altri provider da cui `ProxyProvider` dipende.
+
+- `ProxyProvider` vs `ChangeNotifierProxyProvider` vs `ListenableProxyProvider`, ...
+
+  Funzionano tutti in modo simile, ma invece di inviare il risultato a un `Provider`,
+  un `ChangeNotifierProxyProvider` invierà il suo valore a un `ChangeNotifierProvider`.
+
+### FAQ
+
+#### Posso ispezionare il contenuto dei miei oggetti?
+
+Flutter viene fornito con un [devtool](https://github.com/flutter/devtools) che mostra
+l'albero dei widget in un dato momento.
+
+Poiché i provider sono widget, sono anche visibili in quel devtool:
+
+<img src="https://raw.githubusercontent.com/rrousselGit/provider/master/resources/devtools_providers.jpg" width="200" />
+
+Da lì, se clicchi su un provider, sarai in grado di vedere il valore che espone:
+
+<img src="https://raw.githubusercontent.com/rrousselGit/provider/master/resources/expanded_devtools.jpg" width="200" />
+
+(screenshot dei devtools usando la cartella `example`)
+
+#### Il devtool mostra solo "Instance of MyClass". Cosa posso fare?
+
+Per impostazione predefinita, il devtool si basa su `toString`, che per default restituisce "Instance of MyClass".
+
+Per avere qualcosa di più utile, hai due soluzioni:
+
+- utilizza l'API [Diagnosticable](https://api.flutter.dev/flutter/foundation/Diagnosticable-mixin.html) di Flutter.
+
+  Nella maggior parte dei casi, utilizza [DiagnosticableTreeMixin] sui tuoi oggetti, seguito da un'implementazione personalizzata di [debugFillProperties](https://api.flutter.dev/flutter/foundation/DiagnosticableTreeMixin/debugFillProperties.html).
+
+  ```dart
+  class MyClass with DiagnosticableTreeMixin {
+    MyClass({this.a, this.b});
+
+    final int a;
+    final String b;
+
+    @override
+    void debugFillProperties(DiagnosticPropertiesBuilder properties) {
+      super.debugFillProperties(properties);
+      // elenca qui tutte le proprietà della tua classe.
+      // Per ulteriori informazioni, consultare la documentazione di debugFillProperties.
+      properties.add(IntProperty('a', a));
+      properties.add(StringProperty('b', b));
+    }
+  }
+  ```
+
+- Sovrascrivi `toString`.
+
+  Se non puoi utilizzare [DiagnosticableTreeMixin] (ad esempio, se la tua classe è in un pacchetto che non dipende da Flutter), puoi sovrascrivere `toString`.
+
+  Questo è più semplice rispetto all'uso di [DiagnosticableTreeMixin], ma è meno potente:
+  Non sarai in grado di espandere/contrarre i dettagli del tuo oggetto.
+  
+    ```dart
+    class MyClass with DiagnosticableTreeMixin {
+      MyClass({this.a, this.b});
+  
+      final int a;
+      final String b;
+  
+      @override
+      String toString() {
+        return '$runtimeType(a: $a, b: $b)';
+      }
+    }
+    ```
+
+#### Ho un'eccezione quando ottengo i Providers all'interno di `initState`. Cosa posso fare?
+
+Questa eccezione si verifica perché stai cercando di ascoltare un provider da un
+ciclo di vita che non verrà mai più chiamato.
+
+Significa che dovresti utilizzare un altro ciclo di vita (`build`), oppure specificare esplicitamente che non ti interessano gli aggiornamenti.
+
+Pertanto, invece di:
+
+```dart
+initState() {
+  super.initState();
+  print(context.watch<Foo>().value);
+}
+```
+
+puoi fare:
+
+```dart
+Value value;
+
+Widget build(BuildContext context) {
+  final value = context.watch<Foo>().value;
+  if (value != this.value) {
+    this.value = value;
+    print(value);
+  }
+}
+```
+
+che stamperà `value` ogni volta che cambia (e solo quando cambia).
+
+In alternativa, puoi fare:
+
+```dart
+initState() {
+  super.initState();
+  print(context.read<Foo>().value);
+}
+```
+
+che stamperà `value` una sola volta _e ignorerà gli aggiornamenti._
+
+#### Come gestire il hot-reload sui miei oggetti?
+
+Puoi far implementare al tuo oggetto fornito `ReassembleHandler`:
+
+```dart
+class Example extends ChangeNotifier implements ReassembleHandler {
+  @override
+  void reassemble() {
+    print('Did hot-reload');
+  }
+}
+```
+
+Poi usato tipicamente con `provider`:
+
+```dart
+ChangeNotifierProvider(create: (_) => Example()),
+```
+
+#### Uso [ChangeNotifier], e ho un'eccezione quando lo aggiorno. Cosa succede?
+
+Questo probabilmente accade perché stai modificando il [ChangeNotifier] da uno dei suoi discendenti _mentre l'albero dei widget è in fase di costruzione_.
+
+Una situazione tipica in cui ciò accade è quando si avvia una richiesta http, dove il futuro è memorizzato all'interno del notifier:
+
+```dart
+initState() {
+  super.initState();
+  context.read<MyNotifier>().fetchSomething();
+}
+```
+
+Questo non è consentito perché l'aggiornamento dello stato è sincrono.
+
+Ciò significa che alcuni widget potrebbero essere costruiti _prima_ che avvenga la mutazione (ottenendo un valore obsoleto), mentre altri widget verranno costruiti _dopo_ che la mutazione è completata (ottenendo un nuovo valore). Questo potrebbe causare incoerenze nella tua UI e quindi non è permesso.
+
+Invece, dovresti eseguire quella mutazione in un luogo che influenzi
+l'intero albero in modo uniforme:
+
+- direttamente all'interno del `create` del tuo provider/costruttore del tuo modello:
+
+  ```dart
+  class MyNotifier with ChangeNotifier {
+    MyNotifier() {
+      _fetchSomething();
+    }
+
+    Future<void> _fetchSomething() async {}
+  }
+  ```
+  Questo è utile quando non c'è un "parametro esterno".
+
+- in modo asincrono alla fine del frame:
+  ```dart
+  initState() {
+    super.initState();
+    Future.microtask(() =>
+      context.read<MyNotifier>().fetchSomething(someValue);
+    );
+  }
+  ```
+  È leggermente meno ideale, ma consente di passare parametri alla mutazione.
+ 
+#### Devo usare [ChangeNotifier] per stati complessi?
+
+No.
+
+Puoi usare qualsiasi oggetto per rappresentare il tuo stato. Ad esempio, un'alternativa
+è usare `Provider.value()` combinato con un `StatefulWidget`.
+
+Ecco un esempio di contatore che utilizza tale architettura:
+
+```dart
+class Example extends StatefulWidget {
+  const Example({Key key, this.child}) : super(key: key);
+
+  final Widget child;
+
+  @override
+  ExampleState createState() => ExampleState();
+}
+
+class ExampleState extends State<Example> {
+  int _count;
+
+  void increment() {
+    setState(() {
+      _count++;
+    });
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Provider.value(
+      value: _count,
+      child: Provider.value(
+        value: this,
+        child: widget.child,
+      ),
+    );
+  }
+}
+```
+dove possiamo leggere lo stato facendo:
+
+```dart
+return Text(context.watch<int>().toString());
+```
+
+e modificare lo stato con:
+
+```dart
+return FloatingActionButton(
+  onPressed: () => context.read<ExampleState>().increment(),
+  child: Icon(Icons.plus_one),
+);
+```
+
+In alternativa, puoi creare il tuo provider.
+
+#### Posso creare il mio Provider?
+
+Sì. `provider` espone tutti i piccoli componenti che costituiscono un provider completo.
+
+Questo include:
+
+- `SingleChildStatelessWidget`, per far funzionare qualsiasi widget con `MultiProvider`.
+  Questa interfaccia è esposta come parte di `package:provider/single_child_widget`.
+
+- [InheritedProvider], il `InheritedWidget` generico ottenuto quando si usa `context.watch`.
+
+Ecco un esempio di un provider personalizzato che utilizza `ValueNotifier` come stato:
+https://gist.github.com/rrousselGit/4910f3125e41600df3c2577e26967c91
+
+#### Il mio widget si ricostruisce troppo spesso. Cosa posso fare?
+
+Invece di `context.watch`, puoi usare `context.select` per ascoltare solo un insieme specifico di proprietà sull'oggetto ottenuto.
+
+Ad esempio, mentre puoi scrivere:
+
+```dart
+Widget build(BuildContext context) {
+  final person = context.watch<Person>();
+  return Text(person.name);
+}
+```
+
+Questo potrebbe causare la ricostruzione del widget se qualcosa oltre a `name` cambia.
+
+Invece, puoi usare `context.select` per ascoltare solo la proprietà `name`:
+
+```dart
+Widget build(BuildContext context) {
+  final name = context.select((Person p) => p.name);
+  return Text(name);
+}
+```
+
+In questo modo, il widget non verrà ricostruito inutilmente se qualcosa oltre a `name` cambia.
+
+Allo stesso modo, puoi usare [Consumer]/[Selector]. Il loro argomento opzionale `child` consente di ricostruire solo una parte particolare dell'albero dei widget:
+
+```dart
+Foo(
+  child: Consumer<A>(
+    builder: (_, a, child) {
+      return Bar(a: a, child: child);
+    },
+    child: Baz(),
+  ),
+)
+```
+
+In questo esempio, solo `Bar` verrà ricostruito quando `A` viene aggiornato. `Foo` e `Baz` non verranno ricostruiti inutilmente.
+
+#### Posso ottenere due provider diversi utilizzando lo stesso tipo?
+
+No. Anche se puoi avere più provider che condividono lo stesso tipo, un widget sarà in grado di ottenere solo uno di essi: il più vicino antenato.
+
+Invece, sarebbe utile se dessi esplicitamente a entrambi i provider un tipo diverso.
+
+Invece di:
+
+```dart
+Provider<String>(
+  create: (_) => 'England',
+  child: Provider<String>(
+    create: (_) => 'London',
+    child: ...,
+  ),
+),
+```
+
+Preferisci:
+
+```dart
+Provider<Country>(
+  create: (_) => Country('England'),
+  child: Provider<City>(
+    create: (_) => City('London'),
+    child: ...,
+  ),
+),
+```
+
+#### Posso consumare un'interfaccia e fornire un'implementazione?
+
+Sì, è necessario fornire un'indicazione al compilatore per indicare che l'interfaccia sarà consumata, con l'implementazione fornita in `create`.
+
+```dart
+abstract class ProviderInterface with ChangeNotifier {
+  ...
+}
+
+class ProviderImplementation with ChangeNotifier implements ProviderInterface {
+  ...
+}
+
+class Foo extends StatelessWidget {
+  @override
+  build(context) {
+    final provider = Provider.of<ProviderInterface>(context);
+    return ...
+  }
+}
+
+ChangeNotifierProvider<ProviderInterface>(
+  create: (_) => ProviderImplementation(),
+  child: Foo(),
+),
+```
+
+### Provider esistenti
+
+`provider` espone alcuni tipi diversi di "provider" per diversi tipi di oggetti.
+
+La lista completa di tutti gli oggetti disponibili è [qui](https://pub.dev/documentation/provider/latest/provider/provider-library.html)
+
+| nome                                                                                                                          | descrizione                                                                                                                                                            |
+| ----------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| [Provider](https://pub.dartlang.org/documentation/provider/latest/provider/Provider-class.html)                               | La forma più basilare di provider. Prende un valore e lo espone, qualunque sia il valore.                                                                               |
+| [ListenableProvider](https://pub.dartlang.org/documentation/provider/latest/provider/ListenableProvider-class.html)           | AUn provider specifico per oggetti Listenable. ListenableProvider ascolterà l'oggetto e chiederà ai widget che dipendono da esso di ricostruirsi ogni volta che il listener viene chiamato. |
+| [ChangeNotifierProvider](https://pub.dartlang.org/documentation/provider/latest/provider/ChangeNotifierProvider-class.html)   | Una specifica di ListenableProvider per ChangeNotifier. Chiamerà automaticamente `ChangeNotifier.dispose` quando necessario.                                             |
+| [ValueListenableProvider](https://pub.dartlang.org/documentation/provider/latest/provider/ValueListenableProvider-class.html) | Ascolta un ValueListenable e espone solo `ValueListenable.value`.                                                         |
+| [StreamProvider](https://pub.dartlang.org/documentation/provider/latest/provider/StreamProvider-class.html)                   | Ascolta uno Stream e espone l'ultimo valore emesso.                                                                                                      |
+| [FutureProvider](https://pub.dartlang.org/documentation/provider/latest/provider/FutureProvider-class.html)                   | Prende un `Future` e aggiorna i dipendenti quando il future si completa.                                                                                                    |
+
+### La mia applicazione lancia un StackOverflowError perché ho troppi provider, cosa posso fare?
+
+Se hai un numero molto grande di provider (150+), è possibile che alcuni dispositivi lancino un `StackOverflowError` perché finisci per costruire troppi widget contemporaneamente.
+
+In questa situazione, hai alcune soluzioni:
+
+- Se la tua applicazione ha una splash-screen, prova a montare i tuoi provider nel tempo anziché tutti in una volta.
+
+  Potresti fare:
+
+  ```dart
+  MultiProvider(
+    providers: [
+      if (step1) ...[
+        <lots of providers>,
+      ],
+      if (step2) ...[
+        <some more providers>
+      ]
+    ],
+  )
+  ```
+
+  dove, durante l'animazione della tua splash screen, potresti fare:
+
+  ```dart
+  bool step1 = false;
+  bool step2 = false;
+  @override
+  initState() {
+    super.initState();
+    Future(() {
+      setState(() => step1 = true);
+      Future(() {
+        setState(() => step2 = true);
+      });
+    });
+  }
+  ```
+
+- Considera di non utilizzare `MultiProvider`.
+  `MultiProvider` funziona aggiungendo un widget tra ogni provider. Non utilizzare `MultiProvider` può
+  aumentare il limite prima che venga raggiunto un `StackOverflowError`.
+
+## Sponsor
+
+<p align="center">
+  <a href="https://raw.githubusercontent.com/rrousselGit/freezed/master/sponsorkit/sponsors.svg">
+    <img src='https://raw.githubusercontent.com/rrousselGit/freezed/master/sponsorkit/sponsors.svg'/>
+  </a>
+</p>
+
+[provider.of]: https://pub.dev/documentation/provider/latest/provider/Provider/of.html
+[selector]: https://pub.dev/documentation/provider/latest/provider/Selector-class.html
+[consumer]: https://pub.dev/documentation/provider/latest/provider/Consumer-class.html
+[changenotifier]: https://api.flutter.dev/flutter/foundation/ChangeNotifier-class.html
+[inheritedwidget]: https://api.flutter.dev/flutter/widgets/InheritedWidget-class.html
+[inheritedprovider]: https://pub.dev/documentation/provider/latest/provider/InheritedProvider-class.html
+[diagnosticabletreemixin]: https://api.flutter.dev/flutter/foundation/DiagnosticableTreeMixin-mixin.html
+
+
+
diff --git a/resources/translations/ja_JP/README.md b/resources/translations/ja_JP/README.md
index 392b03dd..c0fda3a4 100644
--- a/resources/translations/ja_JP/README.md
+++ b/resources/translations/ja_JP/README.md
@@ -1,4 +1,4 @@
-[English](https://github.com/rrousselGit/provider/blob/master/README.md) | [French](https://github.com/rrousselGit/provider/blob/master/resources/translations/fr_FR/README.md) | [Português](https://github.com/rrousselGit/provider/blob/master/resources/translations/pt_br/README.md) | [简体中文](https://github.com/rrousselGit/provider/blob/master/resources/translations/zh-CN/README.md) | [Español](https://github.com/rrousselGit/provider/blob/master/resources/translations/es_MX/README.md) | [한국어](https://github.com/rrousselGit/provider/blob/master/resources/translations/ko-KR/README.md) | [বাংলা](/resources/translations/bn_BD/README.md) | [日本語](https://github.com/rrousselGit/provider/blob/master/resources/translations/ja_JP/README.md)
+[English](https://github.com/rrousselGit/provider/blob/master/packages/provider/README.md) | [French](https://github.com/rrousselGit/provider/blob/master/resources/translations/fr_FR/README.md) | [Português](https://github.com/rrousselGit/provider/blob/master/resources/translations/pt_br/README.md) | [简体中文](https://github.com/rrousselGit/provider/blob/master/resources/translations/zh-CN/README.md) | [Español](https://github.com/rrousselGit/provider/blob/master/resources/translations/es_MX/README.md) | [한국어](https://github.com/rrousselGit/provider/blob/master/resources/translations/ko-KR/README.md) | [বাংলা](https://github.com/rrousselGit/provider/blob/master/resources/translations/bn_BD/README.md) | [日本語](https://github.com/rrousselGit/provider/blob/master/resources/translations/ja_JP/README.md) | [Turkish](https://github.com/rrousselGit/provider/blob/master/resources/translations/tr_TR/README.md) | [Italian](https://github.com/rrousselGit/provider/blob/master/resources/translations/it_IT/README.md)
 
 <a href="https://github.com/rrousselGit/provider/actions"><img src="https://github.com/rrousselGit/provider/workflows/Build/badge.svg" alt="Build Status"></a>
 [![codecov](https://codecov.io/gh/rrousselGit/provider/branch/master/graph/badge.svg)](https://codecov.io/gh/rrousselGit/provider) <a href="https://discord.gg/Bbumvej"><img src="https://img.shields.io/discord/765557403865186374.svg?logo=discord&color=blue" alt="Discord"></a>
diff --git a/resources/translations/ko-KR/README.md b/resources/translations/ko-KR/README.md
index 1d4a4c79..fa51de2f 100644
--- a/resources/translations/ko-KR/README.md
+++ b/resources/translations/ko-KR/README.md
@@ -1,4 +1,4 @@
-[English](https://github.com/rrousselGit/provider/blob/master/README.md) | [French](https://github.com/rrousselGit/provider/blob/master/resources/translations/fr_FR/README.md) | [Português](https://github.com/rrousselGit/provider/blob/master/resources/translations/pt_br/README.md) | [简体中文](https://github.com/rrousselGit/provider/blob/master/resources/translations/zh-CN/README.md) | [Español](https://github.com/rrousselGit/provider/blob/master/resources/translations/es_MX/README.md) | [한국어](https://github.com/rrousselGit/provider/blob/master/resources/translations/ko-KR/README.md) | [বাংলা](/resources/translations/bn_BD/README.md) | [日本語](https://github.com/rrousselGit/provider/blob/master/resources/translations/ja_JP/README.md)
+[English](https://github.com/rrousselGit/provider/blob/master/packages/provider/README.md) | [French](https://github.com/rrousselGit/provider/blob/master/resources/translations/fr_FR/README.md) | [Português](https://github.com/rrousselGit/provider/blob/master/resources/translations/pt_br/README.md) | [简体中文](https://github.com/rrousselGit/provider/blob/master/resources/translations/zh-CN/README.md) | [Español](https://github.com/rrousselGit/provider/blob/master/resources/translations/es_MX/README.md) | [한국어](https://github.com/rrousselGit/provider/blob/master/resources/translations/ko-KR/README.md) | [বাংলা](https://github.com/rrousselGit/provider/blob/master/resources/translations/bn_BD/README.md) | [日本語](https://github.com/rrousselGit/provider/blob/master/resources/translations/ja_JP/README.md) | [Turkish](https://github.com/rrousselGit/provider/blob/master/resources/translations/tr_TR/README.md) | [Italian](https://github.com/rrousselGit/provider/blob/master/resources/translations/it_IT/README.md)
 
 <a href="https://github.com/rrousselGit/provider/actions"><img src="https://github.com/rrousselGit/provider/workflows/Build/badge.svg" alt="Build Status"></a>
 [![codecov](https://codecov.io/gh/rrousselGit/provider/branch/master/graph/badge.svg)](https://codecov.io/gh/rrousselGit/provider) <a href="https://discord.gg/Bbumvej"><img src="https://img.shields.io/discord/765557403865186374.svg?logo=discord&color=blue" alt="Discord"></a>
@@ -660,7 +660,7 @@ ChangeNotifierProvider<ProviderInterface>(
 
 | 이름                                                                                                                          | 설명                                                                                                                                                        |
 | ----------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| [Provider](https://pub.dartlang.org/documentation/provider/latest/provider/Provider-class.html)                               | The most 기본적인 provider 형태. 어떤 값이던 간에 값을 노출시킵니다.                                                                                        |
+| [Provider](https://pub.dartlang.org/documentation/provider/latest/provider/Provider-class.html)                               | The most 기본적인 provider 형태. 어떤 값이든 간에 값을 노출시킵니다.                                                                                        |
 | [ListenableProvider](https://pub.dartlang.org/documentation/provider/latest/provider/ListenableProvider-class.html)           | Listenable 객체를 위한 특수한 provider. ListenableProvider는 listener가 호출될 때마다 오브젝트를 수신하고 오브젝트에 종속된 위젯을 재구성하도록 요청합니다. |
 | [ChangeNotifierProvider](https://pub.dartlang.org/documentation/provider/latest/provider/ChangeNotifierProvider-class.html)   | ChangeNotifier용 ListenableProvider 사양. 필요할 때 자동으로 `ChangeNotifier.dispose`를 호출합니다.                                                         |
 | [ValueListenableProvider](https://pub.dartlang.org/documentation/provider/latest/provider/ValueListenableProvider-class.html) | ValueListenable을 수신하고, `ValueListenable.value`만을 노출합니다.                                                                                         |
diff --git a/resources/translations/pt_br/README.md b/resources/translations/pt_br/README.md
index 2555ead0..f652c44d 100644
--- a/resources/translations/pt_br/README.md
+++ b/resources/translations/pt_br/README.md
@@ -1,4 +1,4 @@
-[English](https://github.com/rrousselGit/provider/blob/master/README.md) | [French](https://github.com/rrousselGit/provider/blob/master/resources/translations/fr_FR/README.md) | [Português](https://github.com/rrousselGit/provider/blob/master/resources/translations/pt_br/README.md) | [简体中文](https://github.com/rrousselGit/provider/blob/master/resources/translations/zh-CN/README.md) | [Español](https://github.com/rrousselGit/provider/blob/master/resources/translations/es_MX/README.md) | [한국어](https://github.com/rrousselGit/provider/blob/master/resources/translations/ko-KR/README.md) | [বাংলা](/resources/translations/bn_BD/README.md) | [日本語](https://github.com/rrousselGit/provider/blob/master/resources/translations/ja_JP/README.md)
+[English](https://github.com/rrousselGit/provider/blob/master/packages/provider/README.md) | [French](https://github.com/rrousselGit/provider/blob/master/resources/translations/fr_FR/README.md) | [Português](https://github.com/rrousselGit/provider/blob/master/resources/translations/pt_br/README.md) | [简体中文](https://github.com/rrousselGit/provider/blob/master/resources/translations/zh-CN/README.md) | [Español](https://github.com/rrousselGit/provider/blob/master/resources/translations/es_MX/README.md) | [한국어](https://github.com/rrousselGit/provider/blob/master/resources/translations/ko-KR/README.md) | [বাংলা](https://github.com/rrousselGit/provider/blob/master/resources/translations/bn_BD/README.md) | [日本語](https://github.com/rrousselGit/provider/blob/master/resources/translations/ja_JP/README.md) | [Turkish](https://github.com/rrousselGit/provider/blob/master/resources/translations/tr_TR/README.md) | [Italian](https://github.com/rrousselGit/provider/blob/master/resources/translations/it_IT/README.md)
 
 [![Build Status](https://travis-ci.org/rrousselGit/provider.svg?branch=master)](https://travis-ci.org/rrousselGit/provider)
 [![pub package](https://img.shields.io/pub/v/provider.svg)](https://pub.dev/packages/provider) [![codecov](https://codecov.io/gh/rrousselGit/provider/branch/master/graph/badge.svg)](https://codecov.io/gh/rrousselGit/provider) [![Gitter](https://badges.gitter.im/flutter_provider/community.svg)](https://gitter.im/flutter_provider/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
diff --git a/resources/translations/tr_TR/README.md b/resources/translations/tr_TR/README.md
new file mode 100644
index 00000000..4a15d485
--- /dev/null
+++ b/resources/translations/tr_TR/README.md
@@ -0,0 +1,745 @@
+[English](https://github.com/rrousselGit/provider/blob/master/packages/provider/README.md) | [French](https://github.com/rrousselGit/provider/blob/master/resources/translations/fr_FR/README.md) | [Português](https://github.com/rrousselGit/provider/blob/master/resources/translations/pt_br/README.md) | [简体中文](https://github.com/rrousselGit/provider/blob/master/resources/translations/zh-CN/README.md) | [Español](https://github.com/rrousselGit/provider/blob/master/resources/translations/es_MX/README.md) | [한국어](https://github.com/rrousselGit/provider/blob/master/resources/translations/ko-KR/README.md) | [বাংলা](https://github.com/rrousselGit/provider/blob/master/resources/translations/bn_BD/README.md) | [日本語](https://github.com/rrousselGit/provider/blob/master/resources/translations/ja_JP/README.md) | [Turkish](https://github.com/rrousselGit/provider/blob/master/resources/translations/tr_TR/README.md) | [Italian](https://github.com/rrousselGit/provider/blob/master/resources/translations/it_IT/README.md)
+
+<a href="https://github.com/rrousselGit/provider/actions"><img src="https://github.com/rrousselGit/provider/workflows/Build/badge.svg" alt="Build Status"></a>
+[![codecov](https://codecov.io/gh/rrousselGit/provider/branch/master/graph/badge.svg)](https://codecov.io/gh/rrousselGit/provider) <a href="https://discord.gg/Bbumvej"><img src="https://img.shields.io/discord/765557403865186374.svg?logo=discord&color=blue" alt="Discord"></a>
+
+[<img src="https://raw.githubusercontent.com/rrousselGit/provider/master/resources/flutter_favorite.png" width="200" />](https://flutter.dev/docs/development/packages-and-plugins/favorites)
+
+[InheritedWidget]'ı sarmalayarak kullanımını kolaylaştırır ve 
+daha fazla yeniden kullanılabilir hale getirir.
+
+Elle [InheritedWidget] yazmak yerine `provider` kullanarak şunları elde edersiniz:
+
+- kaynakların(resources) basitleştirilmiş tahsisi/tasfiyesi
+- lazy-load eklentisini
+- her seferinde yeni bir sınıf oluşturmaya kıyasla büyük ölçüde azaltılmış bir boilerplate
+- devtool dostu - Provider kullanarak, uygulamanızın durumu Flutter devtool'da görünür olacaktır
+- [InheritedWidget]ları kullanmanın daha esnek bir yolu olarak yeni widgetlar(Bkz. [Provider.of]/[Consumer]/[Selector])
+- katlanarak büyüyen bir dinleme mekanizmasına sahip sınıflar için artırılmış ölçeklenebilirlik
+  karmaşıklıktadır (bildirim gönderimi için O(N) olan [ChangeNotifier] gibi).
+
+`provider` hakkında daha detaylı bilgi için, dokümanı inceleyebilirsiniz [documentation](https://pub.dev/documentation/provider/latest/provider/provider-library.html).
+
+Ayrıca: 
+
+- [Resmi Flutter state management belgeleri](https://flutter.dev/docs/development/data-and-backend/state-mgmt/simple), `provider` + [ChangeNotifier]'ın nasıl kullanılacağını göstermektedir.
+- `provider` + [ChangeNotifier] kullanılarak geliştirilmiş bir uygulama örneği [Flutter state management mimarisi örneği](https://github.com/brianegan/flutter_architecture_samples/tree/master/change_notifier_provider)
+- State management mimarilerinde bir `provider` kullanan [flutter_bloc](https://github.com/felangel/bloc) ve [Mobx](https://github.com/mobxjs/mobx.dart)
+
+## 4.x.x'den 5.0.0-nullsafety'ye geçiş
+
+- Hem `FutureProvider` hem de `StreamProvider` için `initialData` artık gerekli.
+
+  Geçiş işlemleri için:
+
+  ```dart
+  FutureProvider<int>(
+    create: (context) => Future.value(42),
+    child: MyApp(),
+  )
+
+  Widget build(BuildContext context) {
+    final value = context.watch<int>();
+    return Text('$value');
+  }
+  ```
+
+  yenilenmiş hali:
+
+  ```dart
+  FutureProvider<int?>(
+    initialValue: null,
+    create: (context) => Future.value(42),
+    child: MyApp(),
+  )
+
+  Widget build(BuildContext context) {
+    // ? ile işaretlendiğine emin ol watch<int?>
+    final value = context.watch<int?>();
+    return Text('$value');
+  }
+  ```
+
+- `ValueListenableProvider` kaldırıldı
+
+  Geçiş yapmak için bunun yerine `ValueListenableBuilder` ile birlikte `Provider` kullanabilirsiniz:
+
+  ```dart
+  ValueListenableBuilder<int>(
+    valueListenable: myValueListenable,
+    builder: (context, value, _) {
+      return Provider<int>.value(
+        value: value,
+        child: MyApp(),
+      );
+    }
+  )
+  ```
+
+## Kullanım
+
+### Bir değeri expose etme
+
+#### Yeni bir obje örneğini expose etme
+
+Provider'lar yalnızca bir değeri expose etmenizi değil, aynı zamanda onu oluşturmanıza, dinlemenize ve atmanıza da olanak tanır.
+
+Yeni oluşturulmuş bir objeyi expose etmek için, bir providerın varsayılan constructer'ını kullanın.
+Bir nesneyi **create** etmek istiyorsanız `.value` yapıcısını kullanmayın
+aksi takdirde istenmeyen yan etkiler oluşacaktır.
+
+Bakınız [StackOverflow yanıtı](https://stackoverflow.com/questions/52249578/how-to-deal-with-unwanted-widget-build)
+Bu da değer oluşturmak için `.value` constructor'ı kullanılmasının neden istenmediğini açıklıyor.
+
+- **Yapmanız Gereken** `create` methodunun altında yeni bir obje oluşturmak.
+
+```dart
+Provider(
+  create: (_) => MyModel(),
+  child: ...
+)
+```
+
+- **Yapmayın!** `Provider.value` ile obje oluşturmak.
+
+```dart
+ChangeNotifierProvider.value(
+  value: MyModel(),
+  child: ...
+)
+```
+
+- **Yapmayın!** objenizi zaman içerisinde değişebilecek değişkenler üzerine kurmak.
+
+  Böyle bir durumda, oluşturduğunuz obje değiştiğinde veriler asla
+  update edilmeyecektir.
+
+```dart
+int count;
+
+Provider(
+  create: (_) => MyModel(count),
+  child: ...
+)
+```
+
+Eğer atadığınız obje zaman içerisinde değişecekse,
+`ProxyProvider` kullanmayı tercih edin:
+
+```dart
+int count;
+
+ProxyProvider0(
+  update: (_, __) => MyModel(count),
+  child: ...
+)
+```
+
+**DIPNOT**:
+
+Provider'ın sağladığı `create`/`update` callback methodlarını kullanırken, kullandığınız bu callbacklerin
+default olarak lazily tanımlamasına uyduğunu unutmayın.
+
+Bu şu anlama geliyor, `create`/`update` methodları en az bir kez çağırılana kadar asla dinlenmeyecekler.
+
+Bu durumu değiştirerek, veriler üzerinde işlem yapmak isterseniz, `lazy` parametresini kullanabilirsiniz.
+
+```dart
+MyProvider(
+  create: (_) => Something(),
+  lazy: false,
+)
+```
+
+#### Mevcut bir object instance'ını yeniden kullanma:
+
+Zaten halihazırda bir obje instance'ına sahipseniz ve onu tekrar kullanmak istiyorsanız, `.value` constructor'ı aradığınız şey olacaktır.
+
+Yanlış kullanıldığı durumda, zaten kullanımda olan objeniz için `dispose` method'ı devreye girebilir.
+
+- **Yapmanız Gereken** [ChangeNotifier] içerisinde bulunan veriyi
+  `ChangeNotifierProvider.value` ile çağırın.
+
+```dart
+MyChangeNotifier variable;
+
+ChangeNotifierProvider.value(
+  value: variable,
+  child: ...
+)
+```
+
+- **Yapmayın!** [ChangeNotifier] ile halihazırda çağırılmış veriyi kullanmak.
+
+```dart
+MyChangeNotifier variable;
+
+ChangeNotifierProvider(
+  create: (_) => variable,
+  child: ...
+)
+```
+
+### Veri dinleme işlemleri
+
+Bir veriyi dinlemenin en basit yolu [BuildContext] üzerinde bulunan extension methodları aracılığı ile veriye erişmektir.
+
+- `context.watch<T>()`, `T` üzerindeki widget değişimleri dinler.
+- `context.read<T>()`, `T` okuması yapar fakat dinleme işlemi yapmaz.
+- `context.select<T, R>(R cb(T value))`, `T` verisinin sadece belli bir kısmı için dinleme işlemi yapar.
+
+Benzer şekilde davranacak olan `Provider.of<T>(context)` static methodu da dinleme işlemi için
+kullanılabilir. `listen` parametresi `false` olarak belirlendiğinde (`Provider.of<T>(context, listen: false)` 'da olduğu gibi), aynı
+`read` işlemi yaptığımız durumdaki gibi davranacaktır.
+
+`context.read<T>()` işleminin widget rebuild işlemini triggerlamayacak olduğunu not 
+ediniz. ayrıca `StatelessWidget.build`/`State.build` içerisinde de çağırılamaz.
+Fakat, bu methodların dışında istenilen şekilde kullanılabilir.
+
+Bu methodların her biri ilişkili olduğu widget ağacının başlangıcında bulunan 
+`BuildContext` methoduna dönecek ve en yakın `T` verisi ile eşlenecektir.
+(eğer eşlenecek bir veri bulamazsa boşa düşecek).
+
+Bu işlem bir O(1) işlemi gibidir. Widgetın bulunduğu ağaçta bir şey içermez.
+
+İlk örnekle birleştirdiğmiz durumda [veri dinleme işlemleri](#bir_değeri_expose_etme), `String` verisi expose edilerek okunacak ve "Hello World" render edilecektir.
+
+```dart
+class Home extends StatelessWidget {
+  @override
+  Widget build(BuildContext context) {
+    return Text(
+      // "watch" işlemi ile elde etmek istediğiniz objenin tipini belirtmeyi unutmayın!
+      context.watch<String>(),
+    );
+  }
+}
+```
+
+Alternatif olarak, bu yöntemleri kullanmak yerine, [Consumer] veya [Selector] kullanılabilir.
+
+Bu yöntemler performans açısından yararlı olabilir, ayrıca `BuildContext` elde etmek
+zor olduğunda da kullanılabilir.
+
+Ayrıca daha fazla bilgi [FAQ](https://github.com/rrousselGit/provider#my-widget-rebuilds-too-often-what-can-i-do), [Consumer](https://pub.dev/documentation/provider/latest/provider/Consumer-class.html)  dokümanı ve [Selector](https://pub.dev/documentation/provider/latest/provider/Selector-class.html) dokümanına bakabilirsiniz.
+
+### Provider'ın opsiyonel olduğu durumlar
+
+Bazen, providerın mevcut olmadığı durumları desteklemek isteyebiliriz. Örneğin, 
+oluşturduğumuz bir widget projenin farklı noktalarında tekrar kullanılabilir, ve bu 
+durum providerın dışında gerçekleşebilir.
+
+Böyle durumlarda, `context.watch`/`context.read` yöntemini kullanarak, generic bir 
+type'ı null olmaktan kurtarabiliriz. Öyle ki:
+
+```dart
+context.watch<Model>()
+```
+
+bu durum eğer matchlenebileceği bir provider bulamaz ise `ProviderNotFoundException`
+ fırlatacaktır, fakat:
+
+```dart
+context.watch<Model?>()
+```
+
+match olabileceği bir provider arayacaktır. Eğer bulamazsa bu sefer `null` olarak 
+dönecektir.
+
+### MultiProvider
+
+Büyük projelerde çok fazla değeri projeye entegre etmeniz gerektiğinde 
+oluşturduğunuz `Provider`'ları iç içe kullanmak yerine:
+
+```dart
+Provider<Something>(
+  create: (_) => Something(),
+  child: Provider<SomethingElse>(
+    create: (_) => SomethingElse(),
+    child: Provider<AnotherThing>(
+      create: (_) => AnotherThing(),
+      child: someWidget,
+    ),
+  ),
+),
+```
+
+Bu şekilde kullanın:
+
+```dart
+MultiProvider(
+  providers: [
+    Provider<Something>(create: (_) => Something()),
+    Provider<SomethingElse>(create: (_) => SomethingElse()),
+    Provider<AnotherThing>(create: (_) => AnotherThing()),
+  ],
+  child: someWidget,
+)
+```
+
+Aslında iki yöntem de aynı işlevi yerine getiriyor.`MultiProvider` yalnızca 
+yazdığınız kodun okunabilirliğine katkı sağlıyor.
+
+### ProxyProvider
+
+3.0.0 sürümü ile birlikte, yeni bir provider türü eklendi: `ProxyProvider`.
+
+`ProxyProvider` farklı provider verilerini birleştirerek yenilerini üretmenize olanak sağlayan bir `Provider` türü.
+
+Yeni oluşturulan obje, providerlarınızdan herhangi birinde gerçekleşen bir update'i dinleyecek ve update oluşması durumunda otomatik olarak güncellenecek.
+
+Aşağıdaki örnek `ProxyProvider` başka bir providerda oluşan counter dinlemesi ile objenin build edilmesini gösteriyor.
+
+```dart
+Widget build(BuildContext context) {
+  return MultiProvider(
+    providers: [
+      ChangeNotifierProvider(create: (_) => Counter()),
+      ProxyProvider<Counter, Translations>(
+        update: (_, counter, __) => Translations(counter.value),
+      ),
+    ],
+    child: Foo(),
+  );
+}
+
+class Translations {
+  const Translations(this._value);
+
+  final int _value;
+
+  String get title => 'You clicked $_value times';
+}
+```
+
+Birden fazla varyasonu destekliyor, örneğin:
+
+- `ProxyProvider` vs `ProxyProvider2` vs `ProxyProvider3`, ...
+
+  `ProxyProvider` için çalışma sırası üstünlüğü numaralandırıldığı konuma bağlı 
+  olarak yönetilir.
+
+- `ProxyProvider` vs `ChangeNotifierProxyProvider` vs `ListenableProxyProvider`, ...
+
+  Hepsi aynı şekilde çalışır, ancak bir `Provider`'a result yollamak yerine, 
+  `ChangeNotifierProxyProvider` resultları `ChangeNotifierProvider`'a yollar.
+
+### FAQ
+
+#### Nesnelerin içeriğini nasıl inceleyebilirim?
+
+Flutter anlık olarak widget bilgilerini görebileceğiniz [devtool](https://github.com/flutter/devtools) ile gelir.
+
+Providerlar temelde birer widget oldukları için, devtool üzerinde onları da inceleyebilirsiniz.
+
+<img src="https://raw.githubusercontent.com/rrousselGit/provider/master/resources/devtools_providers.jpg" width="200" />
+
+Görselde providera tıklarsanız, expose edilen valueları görebilirsiniz.
+
+<img src="https://raw.githubusercontent.com/rrousselGit/provider/master/resources/expanded_devtools.jpg" width="200" />
+
+(devtool'da alınan screenshotlar `example` klasörünü kullanmakta)
+
+#### Devtool sadece "Instance of MyClass" gösteriyor. Ne yapmalıyım?
+
+Buna sebep olan durum, devtool'un default olarak `toString` kullanması ve "Instance of MyClass" dönmesidir.
+
+Daha faydalı olması açısından, iki çözüm bulunur:
+
+- Flutter tarafından sağlanan [Diagnosticable](https://api.flutter.dev/flutter/foundation/Diagnosticable-mixin.html) API kullanın. 
+
+  Bir çok senaryo için, objeler için [DiagnosticableTreeMixin], ve custom bir şekilde entegre edilmiş [debugFillProperties](https://api.flutter.dev/flutter/foundation/DiagnosticableTreeMixin/debugFillProperties.html) sorunu çözebilir.
+
+  ```dart
+  class MyClass with DiagnosticableTreeMixin {
+    MyClass({this.a, this.b});
+
+    final int a;
+    final String b;
+
+    @override
+    void debugFillProperties(DiagnosticPropertiesBuilder properties) {
+      super.debugFillProperties(properties);
+      // list all the properties of your class here.
+      // See the documentation of debugFillProperties for more information.
+      properties.add(IntProperty('a', a));
+      properties.add(StringProperty('b', b));
+    }
+  }
+  ```
+
+- `toString` methodunu geçersiz kılın.
+
+  [DiagnosticableTreeMixin]'ı (class içerisindeki bir package sorun çıkartıyorsa)
+  kullanamıyorsanız, `toString`'i methodunu geçersiz kılmayı deneyebilirsiniz.
+
+  Bu yöntem [DiagnosticableTreeMixin] kullanmaktan daha basittir fakat daha 
+  güçsüzdür. Bu yöntemle objeleri detay açısından expand/collapse yapamazsınız.
+
+  ```dart
+  class MyClass with DiagnosticableTreeMixin {
+    MyClass({this.a, this.b});
+
+    final int a;
+    final String b;
+
+    @override
+    String toString() {
+      return '$runtimeType(a: $a, b: $b)';
+    }
+  }
+  ```
+
+#### `initState` içerisinde providerlara ulaşmakta sorun yaşıyorum. Ne yapmalıyım?
+
+Bu hatanın oluşmasının sebebi bir providerı life-cycle açısından bir daha 
+kullanılmayacak bir method içerisinde dinlemeye çalışmanızdan dolayıdır.
+
+Bu durumda ya başka bir life-cycle (`build`) methodu kullanın veya güncelleme 
+durumlarını dinlemekten vazgeçin.
+
+Örneğin, bunun yerine:
+
+```dart
+initState() {
+  super.initState();
+  print(context.watch<Foo>().value);
+}
+```
+
+bunu kullanabilirsiniz:
+
+```dart
+Value value;
+
+Widget build(BuildContext context) {
+  final value = context.watch<Foo>().value;
+  if (value != this.value) {
+    this.value = value;
+    print(value);
+  }
+}
+```
+
+bu şekilde `value` değiştiğinde terminale çıktı alacaksınız.(sadece değiştiğinde)
+
+Alternatif olarak, bunu kullanabilirsiniz:
+
+```dart
+initState() {
+  super.initState();
+  print(context.read<Foo>().value);
+}
+```
+
+Bu yöntem `value` bir kez print edecek ve sonraki _updateleri görmezden gelecektir._
+
+#### Objelerimi nasıl hot-reload edebilirim?
+
+Provide edilmiş objenin entegre edilmesini `ReassembleHandler` yapın:
+
+```dart
+class Example extends ChangeNotifier implements ReassembleHandler {
+  @override
+  void reassemble() {
+    print('Did hot-reload');
+  }
+}
+```
+
+Sonrasında ise tipik `provider` yapısını kullanın.
+
+```dart
+ChangeNotifierProvider(create: (_) => Example()),
+```
+
+#### [ChangeNotifier] kullanıyorum, veri update ettiğimde hata alıyorum. Bu neden oluyor?
+
+Bu genellikle [ChangeNotifier]'ın içerdiği öğelerin çağırıldığı widget ağacının build edildiği sırada değiştirilmesinden dolayı olur.
+
+Yaygın bir senaryo olarak ele alırsak, notifier içerisinde store edilmiş bir future'ı başlattığınız sırada oluşması beklenebilir:
+
+```dart
+initState() {
+  super.initState();
+  context.read<MyNotifier>().fetchSomething();
+}
+```
+
+Bu yöntemin kullanılamamasının sebebi state update durumunun senkron bir işlem olmasıdır.
+
+Bu şu anlama geliyor. Bazı widgetlar mutasyon _öncesinde_ build edilir. (önceki değeri alır), bazı widgetlar _sonrasında_ build edilir (yeni değeri alır). Bu durum kullanıcı arayüzünde beklenmeyen problemler yaratabileceği için kullanımına izin verilmez.
+
+Bunun yerine, bu mutasyonu tüm widget ağacını eşit şekilde etkileyebilecek şekilde
+yapınız:
+
+- direkt olarak modelinizin provider/constructor'ınızın bulunduğu `create` methodu içerisinde:
+
+```dart
+  class MyNotifier with ChangeNotifier {
+    MyNotifier() {
+      _fetchSomething();
+    }
+
+    Future<void> _fetchSomething() async {}
+  }
+  ```
+
+  Bu yöntem "ekstra parametlerin" olmadığı durumlarda faydalı olabilir.
+
+- frame sonunda asenkron bir şekilde:
+  ```dart
+  initState() {
+    super.initState();
+    Future.microtask(() =>
+      context.read<MyNotifier>().fetchSomething(someValue);
+    );
+  }
+  ```
+  Bu çok ideal bir yöntem değildir, ama parametreleri mutasyon içerisinde kullanabilmenizi sağlar.
+
+#### [ChangeNotifier]'ı complex state durumlarında kullanmalı mıyım?
+
+Hayır.
+
+Bu durumda nesneleri state olarak tanımlayabilirsiniz. Örneğin, farklı bir yapı 
+olarak `Provider.value()` ve `StatefulWidget` kombinasyonunu kullanabilirsiniz.
+
+Burada bir sayaç örneği bulabilirsiniz:
+
+```dart
+class Example extends StatefulWidget {
+  const Example({Key key, this.child}) : super(key: key);
+
+  final Widget child;
+
+  @override
+  ExampleState createState() => ExampleState();
+}
+
+class ExampleState extends State<Example> {
+  int _count;
+
+  void increment() {
+    setState(() {
+      _count++;
+    });
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Provider.value(
+      value: _count,
+      child: Provider.value(
+        value: this,
+        child: widget.child,
+      ),
+    );
+  }
+}
+```
+
+state'i dinlemek için:
+
+```dart
+return Text(context.watch<int>().toString());
+```
+
+state üzerinde işlem yapmak için:
+
+```dart
+return FloatingActionButton(
+  onPressed: () => context.read<ExampleState>().increment(),
+  child: Icon(Icons.plus_one),
+);
+```
+
+Alternatif olarak kendi provider yapınızı da kurabilirsiniz.
+
+#### Kendi Provider'ımı oluşturabilir miyim?
+
+Evet. `provider` aslında tüm küçük componentleri kullanarak hazırlanmış bir tam kapsayıcı providerdır diyebiliriz.
+
+Neleri içerir: 
+
+- `MultiProvider` ile çalışması için `SingleChildStatelessWidget` widgetları kurmak.
+  Bu burada bulunan yapıyı kullanır `package:provider/single_child_widget`
+
+- `context.watch` işlemi yaparak klasik `InheritedWidget` elde etmenizi sağlayan [InheritedProvider].
+
+Burada `ValueNotifier`'ı state olarak kullanan bir örnek bulabilirsiniz:
+https://gist.github.com/rrousselGit/4910f3125e41600df3c2577e26967c91
+
+#### Widgetlarım çok sık rebuild oluyor. Ne yapmalıyım?
+
+`context.watch` kullanmak yerine, dinleme işlemlerini daha spesifik ve belirli objeler üzerinde yapmak için `context.select` kullanabilirsiniz.
+
+Örneğin, yazma işlemi yaparken:
+
+```dart
+Widget build(BuildContext context) {
+  final person = context.watch<Person>();
+  return Text(person.name);
+}
+```
+
+Bu işlem `name` değeri dışında bir değişiklik olursa da widgetın rebuild olmasına neden olacaktır.
+
+Fakat, `name` dinleme işlemini `context.select` ile yapmak sadece onun dinlenmesini sağlayacaktır.
+
+```dart
+Widget build(BuildContext context) {
+  final name = context.select((Person p) => p.name);
+  return Text(name);
+}
+```
+
+Bu yöntem sayesinde, `name` dışında başka değerlerde oluşan değişiklikler rebuild triggerlamayacaktır.
+
+Benzer olarak, [Consumer]/[Selector] da kullanılabilir. Bu widgetlarda opsiyonel olarak sunulan `child` argumenti widget ağacının geri kalanı için rebuild işlemini azaltmak için kullanılabilir:
+
+```dart
+Foo(
+  child: Consumer<A>(
+    builder: (_, a, child) {
+      return Bar(a: a, child: child);
+    },
+    child: Baz(),
+  ),
+)
+```
+
+Bu örnekte, `A` update edildiğinde sadece`Bar` rebuild edilecektir. 
+`Foo` ve `Baz` gereksiz şekilde rebuild olmayacaktır.
+
+#### Aynı tip veri için iki farklı provider verisini kullanabilir miyim?
+
+Hayır. Evet aynı anda farklı providerlar aracılığı ile veri paylaşımı yapabilirsiniz fakat böyle bir durumda widget en yakın olduğu veriyi kullanacaktır.
+
+Bunun yerine, iki providera da daha belirgin tanımlamalar yapmak yardımcı olacaktır.
+
+Örneğin, bu örnek yerine:
+
+```dart
+Provider<String>(
+  create: (_) => 'England',
+  child: Provider<String>(
+    create: (_) => 'London',
+    child: ...,
+  ),
+),
+```
+
+Bunu tercih edin:
+
+```dart
+Provider<Country>(
+  create: (_) => Country('England'),
+  child: Provider<City>(
+    create: (_) => City('London'),
+    child: ...,
+  ),
+),
+```
+
+#### Arayüzü kullanarak bir entegrasyon oluşturabilir miyim ?
+
+Evet, bir entegrasyon oluşturmak için `create` içerisinde bir implementasyon sağlanmalıdır.
+
+```dart
+abstract class ProviderInterface with ChangeNotifier {
+  ...
+}
+
+class ProviderImplementation with ChangeNotifier implements ProviderInterface {
+  ...
+}
+
+class Foo extends StatelessWidget {
+  @override
+  build(context) {
+    final provider = Provider.of<ProviderInterface>(context);
+    return ...
+  }
+}
+
+ChangeNotifierProvider<ProviderInterface>(
+  create: (_) => ProviderImplementation(),
+  child: Foo(),
+),
+```
+
+### Mevcut providerlar
+
+`provider` farklı obje tiplerdeki objeler için farklı "provider"lar sağlar.
+
+Mevcut tüm obje listesi için [here](https://pub.dev/documentation/provider/latest/provider/provider-library.html)
+
+| name                                                                                                                          | description                                                                                                                                                            |
+| ----------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| [Provider](https://pub.dartlang.org/documentation/provider/latest/provider/Provider-class.html)                               | Provider'ın en basit hali. Bir değeri alır ve onu expose eder.                                                                               |
+| [ListenableProvider](https://pub.dartlang.org/documentation/provider/latest/provider/ListenableProvider-class.html)           | Dinlenebilir bir obje için spesifik bir provider türü. ListenableProvider objeyi dinlerken aynı zamanda widgetı rebuild edilmesi konusunda uyarma işlemlerini listener çağırıldığında tetikler.|
+| [ChangeNotifierProvider](https://pub.dartlang.org/documentation/provider/latest/provider/ChangeNotifierProvider-class.html)   | ListenableProvider'ın  ChangeNotifier için özelleştirilmiş hali. Otomatik olarak gerekli durumlarda `ChangeNotifier.dispose` çağırır.                                               |
+| [ValueListenableProvider](https://pub.dartlang.org/documentation/provider/latest/provider/ValueListenableProvider-class.html) | ValueListenable dinlemesi yaparak sadece `ValueListenable.value` değerini expose edin.                                                                                                   |
+| [StreamProvider](https://pub.dartlang.org/documentation/provider/latest/provider/StreamProvider-class.html)                   | Bir stream'i dinler ve son entegre edilmiş değeri expose eder.                                                                                                                |
+| [FutureProvider](https://pub.dartlang.org/documentation/provider/latest/provider/FutureProvider-class.html)                   | `Future` alır ve bağımlılık durumunun tamamlanmasını kontrol eder.                                                                                                     |
+
+### Application'ım StackOverflowError hatası veriyor çünkü çok fazla providera sahibim, ne yapmalıyım?
+
+Çok fazla sayıda providera sahipseniz (150+), bazı cihazlarda `StackOverflowError` hatası almanız olasıdır. Bunun nedeni çok fazla sayıda providerın aynı anda rebuild edilmesidir.
+
+Bu durumda yapabileceğiniz birkaç çözüm bulunmaktadır:
+
+- Eğer application'ınınız bir splash-screen'e sahipse, tüm providerları aynı anda değil de zaman içerisinde mount etmeyi deneyin.
+
+  Şu şekilde yapılabilir:
+
+   ```dart
+  MultiProvider(
+    providers: [
+      if (step1) ...[
+        <lots of providers>,
+      ],
+      if (step2) ...[
+        <some more providers>
+      ]
+    ],
+  )
+  ```
+
+  splash screen animasyonunuz sırasında, bu çözümü de uygulayabilirsiniz:
+
+  ```dart
+  bool step1 = false;
+  bool step2 = false;
+  @override
+  initState() {
+    super.initState();
+    Future(() {
+      setState(() => step1 = true);
+      Future(() {
+        setState(() => step2 = true);
+      });
+    });
+  }
+  ```
+
+- `MultiProvider` kullanmayı bırakmayı düşünebilirsiniz.
+  `MultiProvider` her provider için bir widget ekler. `MultiProvider` kullanmamak 
+  `StackOverflowError` hatasına ulaşmadan önceki limiti arttırabilir.
+
+## Sponsorlar
+
+<p align="center">
+  <a href="https://raw.githubusercontent.com/rrousselGit/freezed/master/sponsorkit/sponsors.svg">
+    <img src='https://raw.githubusercontent.com/rrousselGit/freezed/master/sponsorkit/sponsors.svg'/>
+  </a>
+</p>
+
+[provider.of]: https://pub.dev/documentation/provider/latest/provider/Provider/of.html
+[selector]: https://pub.dev/documentation/provider/latest/provider/Selector-class.html
+[consumer]: https://pub.dev/documentation/provider/latest/provider/Consumer-class.html
+[changenotifier]: https://api.flutter.dev/flutter/foundation/ChangeNotifier-class.html
+[inheritedwidget]: https://api.flutter.dev/flutter/widgets/InheritedWidget-class.html
+[inheritedprovider]: https://pub.dev/documentation/provider/latest/provider/InheritedProvider-class.html
+[diagnosticabletreemixin]: https://api.flutter.dev/flutter/foundation/DiagnosticableTreeMixin-mixin.html
\ No newline at end of file
diff --git a/resources/translations/zh-CN/README.md b/resources/translations/zh-CN/README.md
index fba8ec12..e78d358b 100644
--- a/resources/translations/zh-CN/README.md
+++ b/resources/translations/zh-CN/README.md
@@ -1,4 +1,4 @@
-[English](https://github.com/rrousselGit/provider/blob/master/README.md) | [French](https://github.com/rrousselGit/provider/blob/master/resources/translations/fr_FR/README.md) | [Português](https://github.com/rrousselGit/provider/blob/master/resources/translations/pt_br/README.md) | [简体中文](https://github.com/rrousselGit/provider/blob/master/resources/translations/zh-CN/README.md) | [Español](https://github.com/rrousselGit/provider/blob/master/resources/translations/es_MX/README.md) | [한국어](https://github.com/rrousselGit/provider/blob/master/resources/translations/ko-KR/README.md) | [বাংলা](/resources/translations/bn_BD/README.md) | [日本語](https://github.com/rrousselGit/provider/blob/master/resources/translations/ja_JP/README.md)
+[English](https://github.com/rrousselGit/provider/blob/master/packages/provider/README.md) | [French](https://github.com/rrousselGit/provider/blob/master/resources/translations/fr_FR/README.md) | [Português](https://github.com/rrousselGit/provider/blob/master/resources/translations/pt_br/README.md) | [简体中文](https://github.com/rrousselGit/provider/blob/master/resources/translations/zh-CN/README.md) | [Español](https://github.com/rrousselGit/provider/blob/master/resources/translations/es_MX/README.md) | [한국어](https://github.com/rrousselGit/provider/blob/master/resources/translations/ko-KR/README.md) | [বাংলা](https://github.com/rrousselGit/provider/blob/master/resources/translations/bn_BD/README.md) | [日本語](https://github.com/rrousselGit/provider/blob/master/resources/translations/ja_JP/README.md) | [Turkish](https://github.com/rrousselGit/provider/blob/master/resources/translations/tr_TR/README.md) | [Italian](https://github.com/rrousselGit/provider/blob/master/resources/translations/it_IT/README.md)
 
 <a href="https://github.com/rrousselGit/provider/actions"><img src="https://github.com/rrousselGit/provider/workflows/Build/badge.svg" alt="Build Status"></a>
 [![codecov](https://codecov.io/gh/rrousselGit/provider/branch/master/graph/badge.svg)](https://codecov.io/gh/rrousselGit/provider) [![Gitter](https://badges.gitter.im/flutter_provider/community.svg)](https://gitter.im/flutter_provider/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
@@ -84,7 +84,7 @@
 
 #### 暴露一个新的对象实例
 
-Provider 不仅可以暴露出一个值,同时也可以创建、监听和销毁它。
+Providers 不仅可以暴露出一个值,同时也可以创建、监听和销毁它。
 
 要暴露一个新创建的对象,你可以使用这个 provider 的默认构造。
 而如果你想在开始监听时再 **创建** 一个对象,
diff --git a/scripts/flutter_test.sh b/scripts/flutter_test.sh
deleted file mode 100755
index b76104b1..00000000
--- a/scripts/flutter_test.sh
+++ /dev/null
@@ -1,9 +0,0 @@
-set -e # abort CI if an error happens
-cd $1
-flutter packages get
-flutter format --set-exit-if-changed lib test
-flutter analyze --no-current-package lib test/
-flutter test --no-pub --coverage $(ls test/*_test.dart | grep -v _legacy_)
-flutter test --no-pub --coverage --no-sound-null-safety $(ls test/*_legacy_test.dart)
-# resets to the original state
-cd -
diff --git a/test/common_legacy.dart b/test/common_legacy.dart
deleted file mode 100644
index 2075db82..00000000
--- a/test/common_legacy.dart
+++ /dev/null
@@ -1,12 +0,0 @@
-// @dart=2.11
-import 'package:provider/provider.dart';
-
-/// Given `T`, returns a `Provider<T>`.
-///
-/// For use in legacy tests: they can't instantiate a `Provider<T?>` directly
-/// because they can't write `<T?>`. But, they can pass around a `Provider<T?`>.
-Provider<T> legacyProviderOfValue<T>(T value, Provider child) =>
-    Provider<T>.value(
-      value: value,
-      child: child,
-    );
diff --git a/test/inherited_provider_legacy_test.dart b/test/inherited_provider_legacy_test.dart
deleted file mode 100644
index 5e52c45e..00000000
--- a/test/inherited_provider_legacy_test.dart
+++ /dev/null
@@ -1,46 +0,0 @@
-// Mixed mode: test is legacy, runtime is legacy, package:provider is null safe.
-// @dart=2.11
-import 'package:flutter/widgets.dart';
-import 'package:flutter_test/flutter_test.dart';
-import 'package:provider/provider.dart';
-
-import 'common.dart';
-
-BuildContext get context => find.byType(Context).evaluate().single;
-
-class Context extends StatelessWidget {
-  const Context({Key key}) : super(key: key);
-
-  @override
-  Widget build(BuildContext context) {
-    return Container();
-  }
-}
-
-void main() {
-  testWidgets('allows nulls in mixed mode', (tester) async {
-    // ignore: avoid_returning_null
-    int initialValueBuilder(BuildContext _) => null;
-
-    await tester.pumpWidget(
-      InheritedProvider<int>(
-        create: initialValueBuilder,
-        child: const Context(),
-      ),
-    );
-
-    expect(Provider.of<int>(context, listen: false), equals(null));
-    expect(Provider.of<int>(context, listen: false), equals(null));
-  });
-
-  testWidgets(
-      'throw ProviderNotFoundException in mixed mode if no provider exists',
-      (tester) async {
-    await tester.pumpWidget(const Context());
-
-    expect(
-      () => context.read<int>(),
-      throwsProviderNotFound<int>(),
-    );
-  });
-}
diff --git a/test/provider_legacy_mixing_impl.dart b/test/provider_legacy_mixing_impl.dart
deleted file mode 100644
index 61932638..00000000
--- a/test/provider_legacy_mixing_impl.dart
+++ /dev/null
@@ -1,297 +0,0 @@
-/////// Mixed mode: test is legacy, runtime is legacy, package:provider is null safe.
-import 'package:flutter/widgets.dart';
-import 'package:flutter_test/flutter_test.dart';
-import 'package:provider/provider.dart';
-
-// ignore: import_of_legacy_library_into_null_safe
-import 'common_legacy.dart';
-
-void main() {
-  // See `provider_test.dart` for corresponding sound mode test.
-  testWidgets('unsound provide T* inject T', (tester) async {
-    late double value;
-
-    final builder = Builder(
-      builder: (context) {
-        // Look up a Provider<double>.
-        value = Provider.of<double>(context, listen: false);
-        return Container();
-      },
-    );
-
-    await tester.pumpWidget(
-      // Install a Provider<double*>.
-      legacyProviderOfValue<double>(
-        24,
-        Provider<int>.value(
-          value: 42,
-          child: builder,
-        ),
-      ),
-    );
-
-    // Provider<double> not found, uses Provider<double?> instead.
-    expect(value, equals(24.0));
-  });
-
-  testWidgets('unsound provide T* inject T?', (tester) async {
-    late double? value;
-
-    final builder = Builder(
-      builder: (context) {
-        // Look up a Provider<double?>.
-        value = Provider.of<double?>(context, listen: false);
-        return Container();
-      },
-    );
-
-    await tester.pumpWidget(
-      // Install a Provider<double>.
-      legacyProviderOfValue<double>(
-        24,
-        Provider<int>.value(
-          value: 42,
-          child: builder,
-        ),
-      ),
-    );
-
-    // Provider<double> not found, uses Provider<double?> instead.
-    expect(value, equals(24.0));
-  });
-
-  testWidgets('unsound provide T inject T', (tester) async {
-    late double value;
-
-    final builder = Builder(
-      builder: (context) {
-        // Look up a Provider<double>.
-        value = Provider.of<double>(context, listen: false);
-        return Container();
-      },
-    );
-
-    await tester.pumpWidget(
-      // Install a Provider<double>.
-      Provider<double>.value(
-        value: 24,
-        child: Provider<int>.value(
-          value: 42,
-          child: builder,
-        ),
-      ),
-    );
-
-    // Provider<double> not found, uses Provider<double?> instead.
-    expect(value, equals(24.0));
-  });
-
-  testWidgets('unsound provide T inject T?', (tester) async {
-    late double? value;
-
-    final builder = Builder(
-      builder: (context) {
-        // Look up a Provider<double>.
-        value = Provider.of<double?>(context, listen: false);
-        return Container();
-      },
-    );
-
-    await tester.pumpWidget(
-      // Install a Provider<double>.
-      Provider<double>.value(
-        value: 24,
-        child: Provider<int>.value(
-          value: 42,
-          child: builder,
-        ),
-      ),
-    );
-
-    // Provider<double> not found, uses Provider<double?> instead.
-    expect(value, equals(24.0));
-  });
-
-  testWidgets('unsound provide T inject T', (tester) async {
-    late double? value;
-
-    final builder = Builder(
-      builder: (context) {
-        // Look up a Provider<double>.
-        value = Provider.of<double>(context, listen: false);
-        return Container();
-      },
-    );
-
-    await tester.pumpWidget(
-      // Install a Provider<double>.
-      Provider<double>.value(
-        value: 24,
-        child: Provider<int>.value(
-          value: 42,
-          child: builder,
-        ),
-      ),
-    );
-
-    // Provider<double> not found, uses Provider<double?> instead.
-    expect(value, equals(24.0));
-  });
-
-  testWidgets('unsound provide T? inject T', (tester) async {
-    late double? value;
-
-    final builder = Builder(
-      builder: (context) {
-        // Look up a Provider<double>.
-        value = Provider.of<double>(context, listen: false);
-        return Container();
-      },
-    );
-
-    await tester.pumpWidget(
-      // Install a Provider<double?>.
-      Provider<double?>.value(
-        value: 24,
-        child: Provider<int>.value(
-          value: 42,
-          child: builder,
-        ),
-      ),
-    );
-
-    // Provider<double> not found, uses Provider<double?> instead.
-    expect(value, equals(24.0));
-  });
-
-  testWidgets('unsound provide T? inject T?', (tester) async {
-    late double? value;
-
-    final builder = Builder(
-      builder: (context) {
-        // Look up a Provider<double>.
-        value = Provider.of<double?>(context, listen: false);
-        return Container();
-      },
-    );
-
-    await tester.pumpWidget(
-      // Install a Provider<double?>.
-      Provider<double?>.value(
-        value: 24,
-        child: Provider<int>.value(
-          value: 42,
-          child: builder,
-        ),
-      ),
-    );
-
-    // Provider<double> not found, uses Provider<double?> instead.
-    expect(value, equals(24.0));
-  });
-
-  // See `provider_test.dart` for corresponding sound mode test.
-  testWidgets('unsound provide null T* inject T', (tester) async {
-    late double value;
-
-    final builder = Builder(
-      builder: (context) {
-        // Look up a Provider<double>.
-        value = Provider.of<double>(context, listen: false);
-        return Container();
-      },
-    );
-
-    await tester.pumpWidget(
-      // Install a Provider<double*>.
-      legacyProviderOfValue<double>(
-        null,
-        Provider<int>.value(
-          value: 42,
-          child: builder,
-        ),
-      ),
-    );
-
-    // Provider<double> not found, uses Provider<double?> instead.
-    expect(value, equals(null));
-  });
-
-  testWidgets('unsound provide null T* inject T?', (tester) async {
-    late double? value;
-
-    final builder = Builder(
-      builder: (context) {
-        // Look up a Provider<double?>.
-        value = Provider.of<double?>(context, listen: false);
-        return Container();
-      },
-    );
-
-    await tester.pumpWidget(
-      // Install a Provider<double>.
-      legacyProviderOfValue<double>(
-        null,
-        Provider<int>.value(
-          value: 42,
-          child: builder,
-        ),
-      ),
-    );
-
-    // Provider<double> not found, uses Provider<double?> instead.
-    expect(value, equals(null));
-  });
-
-  testWidgets('unsound provide null T? inject T', (tester) async {
-    late double value;
-
-    final builder = Builder(
-      builder: (context) {
-        // Look up a Provider<double>.
-        value = Provider.of<double>(context, listen: false);
-        return Container();
-      },
-    );
-
-    await tester.pumpWidget(
-      // Install a Provider<double?>.
-      Provider<double?>.value(
-        value: null,
-        child: Provider<int>.value(
-          value: 42,
-          child: builder,
-        ),
-      ),
-    );
-
-    // Provider<double> not found, uses Provider<double?> instead.
-    expect(value, equals(null));
-  });
-
-  testWidgets('unsound provide null T? inject T?', (tester) async {
-    late double? value;
-
-    final builder = Builder(
-      builder: (context) {
-        // Look up a Provider<double>.
-        value = Provider.of<double?>(context, listen: false);
-        return Container();
-      },
-    );
-
-    await tester.pumpWidget(
-      // Install a Provider<double?>.
-      Provider<double?>.value(
-        value: null,
-        child: Provider<int>.value(
-          value: 42,
-          child: builder,
-        ),
-      ),
-    );
-
-    // Provider<double> not found, uses Provider<double?> instead.
-    expect(value, equals(null));
-  });
-}
diff --git a/test/provider_legacy_mixing_test.dart b/test/provider_legacy_mixing_test.dart
deleted file mode 100644
index e8e9ac8e..00000000
--- a/test/provider_legacy_mixing_test.dart
+++ /dev/null
@@ -1,4 +0,0 @@
-// @dart=2.11
-import 'provider_legacy_mixing_impl.dart' as impl;
-
-void main() => impl.main();
diff --git a/test/provider_legacy_test.dart b/test/provider_legacy_test.dart
deleted file mode 100644
index 65919784..00000000
--- a/test/provider_legacy_test.dart
+++ /dev/null
@@ -1,168 +0,0 @@
-// Mixed mode: test is legacy, runtime is legacy, package:provider is null safe.
-// @dart=2.11
-import 'package:flutter/widgets.dart';
-import 'package:flutter_test/flutter_test.dart';
-import 'package:provider/provider.dart';
-
-import 'common.dart';
-
-void main() {
-  // See `provider_test.dart` for corresponding sound mode test.
-  testWidgets('unsound provide T? inject T*', (tester) async {
-    double value;
-
-    final builder = Builder(
-      builder: (context) {
-        // Look up a Provider<double>.
-        value = Provider.of<double>(context, listen: false);
-        return Container();
-      },
-    );
-
-    await tester.pumpWidget(
-      // Install a Provider<double?>.
-      nullableProviderOfValue<double>(
-        24,
-        Provider<int>.value(
-          value: 42,
-          child: builder,
-        ),
-      ),
-    );
-
-    // Provider<double> not found, uses Provider<double?> instead.
-    expect(value, equals(24.0));
-  });
-
-  testWidgets('unsound provide T inject T*', (tester) async {
-    double value;
-
-    final builder = Builder(
-      builder: (context) {
-        // Look up a Provider<double>.
-        value = Provider.of<double>(context, listen: false);
-        return Container();
-      },
-    );
-
-    await tester.pumpWidget(
-      // Install a Provider<double>.
-      nullSafeProviderOfValue<double>(
-        24,
-        Provider<int>.value(
-          value: 42,
-          child: builder,
-        ),
-      ),
-    );
-
-    // Provider<double> not found, uses Provider<double?> instead.
-    expect(value, equals(24.0));
-  });
-
-  testWidgets('unsound provide T* inject T*', (tester) async {
-    double value;
-
-    final builder = Builder(
-      builder: (context) {
-        // Look up a Provider<double>.
-        value = Provider.of<double>(context, listen: false);
-        return Container();
-      },
-    );
-
-    await tester.pumpWidget(
-      // Install a Provider<double*>.
-      Provider<double>.value(
-        value: 24,
-        child: Provider<int>.value(
-          value: 42,
-          child: builder,
-        ),
-      ),
-    );
-
-    // Provider<double> not found, uses Provider<double?> instead.
-    expect(value, equals(24.0));
-  });
-
-  /// with nulls
-  testWidgets('unsound provide null T? inject T*', (tester) async {
-    double value;
-
-    final builder = Builder(
-      builder: (context) {
-        // Look up a Provider<double>.
-        value = Provider.of<double>(context, listen: false);
-        return Container();
-      },
-    );
-
-    await tester.pumpWidget(
-      // Install a Provider<double?>.
-      nullableProviderOfValue<double>(
-        null,
-        Provider<int>.value(
-          value: 42,
-          child: builder,
-        ),
-      ),
-    );
-
-    // Provider<double> not found, uses Provider<double?> instead.
-    expect(value, equals(null));
-  });
-
-  testWidgets('unsound provide null T inject T*', (tester) async {
-    double value;
-
-    final builder = Builder(
-      builder: (context) {
-        // Look up a Provider<double>.
-        value = Provider.of<double>(context, listen: false);
-        return Container();
-      },
-    );
-
-    await tester.pumpWidget(
-      // Install a Provider<double>.
-      nullSafeProviderOfValue<double>(
-        null,
-        Provider<int>.value(
-          value: 42,
-          child: builder,
-        ),
-      ),
-    );
-
-    // Provider<double> not found, uses Provider<double?> instead.
-    expect(value, equals(null));
-  });
-
-  testWidgets('unsound provide null T* inject T*', (tester) async {
-    double value;
-
-    final builder = Builder(
-      builder: (context) {
-        // Look up a Provider<double>.
-        value = Provider.of<double>(context, listen: false);
-        return Container();
-      },
-    );
-
-    await tester.pumpWidget(
-      // Install a Provider<double*>.
-      Provider<double>.value(
-        // ignore: avoid_redundant_argument_values
-        value: null,
-        child: Provider<int>.value(
-          value: 42,
-          child: builder,
-        ),
-      ),
-    );
-
-    // Provider<double> not found, uses Provider<double?> instead.
-    expect(value, equals(null));
-  });
-}