Edit mixin private properties (#3010)

diff --git a/packages/devtools_app/lib/src/provider/instance_viewer/instance_providers.dart b/packages/devtools_app/lib/src/provider/instance_viewer/instance_providers.dart
index 601a466..420bc43 100644
--- a/packages/devtools_app/lib/src/provider/instance_viewer/instance_providers.dart
+++ b/packages/devtools_app/lib/src/provider/instance_viewer/instance_providers.dart
@@ -415,14 +415,26 @@
   final fields = instance.fields.map((field) async {
     final owner = await eval.getClass(field.decl.owner, isAlive);
 
-    final ownerPackageName = tryParsePackageName(owner.library.uri);
+    String ownerUri;
+    String ownerName;
+    if (owner.mixin == null) {
+      ownerUri = owner.library.uri;
+      ownerName = owner.name;
+    } else {
+      final mixinClass = await eval.getClass(owner.mixin.typeClass, isAlive);
+
+      ownerUri = mixinClass.library.uri;
+      ownerName = mixinClass.name;
+    }
+
+    final ownerPackageName = tryParsePackageName(ownerUri);
 
     return ObjectField(
       name: field.decl.name,
       isFinal: field.decl.isFinal,
       ref: parseSentinel<InstanceRef>(field.value),
-      ownerName: owner.name,
-      ownerUri: owner.library.uri,
+      ownerName: ownerName,
+      ownerUri: ownerUri,
       eval: await ref.watch(libraryEvalProvider(owner.library.uri).future),
       isDefinedByDependency: ownerPackageName != appName,
     );
diff --git a/packages/devtools_testing/fixtures/provider_app/lib/main.dart b/packages/devtools_testing/fixtures/provider_app/lib/main.dart
index f18e8f2..41658c4 100644
--- a/packages/devtools_testing/fixtures/provider_app/lib/main.dart
+++ b/packages/devtools_testing/fixtures/provider_app/lib/main.dart
@@ -9,6 +9,7 @@
 import 'package:provider/provider.dart';
 import 'package:provider/single_child_widget.dart';
 
+import 'mixin.dart';
 // ignore: unused_import, allows the tests to use functions from tester.dart
 import 'tester.dart';
 
@@ -80,7 +81,7 @@
   }
 }
 
-class Counter extends ChangeNotifier {
+class Counter with ChangeNotifier, Mixin {
   int _count = 0;
   int get count => _count;
 
diff --git a/packages/devtools_testing/fixtures/provider_app/lib/mixin.dart b/packages/devtools_testing/fixtures/provider_app/lib/mixin.dart
new file mode 100644
index 0000000..45bbc3e
--- /dev/null
+++ b/packages/devtools_testing/fixtures/provider_app/lib/mixin.dart
@@ -0,0 +1,4 @@
+mixin Mixin {
+  // ignore: unused_field, prefer_final_fields, the property is used for testing
+  int _privateMixinProperty = 0;
+}
diff --git a/packages/devtools_testing/lib/provider/provider_controller_test.dart b/packages/devtools_testing/lib/provider/provider_controller_test.dart
index e7f92a5..ee8a935 100644
--- a/packages/devtools_testing/lib/provider/provider_controller_test.dart
+++ b/packages/devtools_testing/lib/provider/provider_controller_test.dart
@@ -106,6 +106,41 @@
   }, timeout: const Timeout.factor(8), skip: true);
 
   group('Provider controllers', () {
+    test('can mutate private properties from mixins', () async {
+      final container = ProviderContainer();
+      addTearDown(container.dispose);
+
+      final sub = container.listen(
+        rawInstanceProvider(
+          const InstancePath.fromProviderId('0').pathForChild(
+            const PathToProperty.objectProperty(
+              name: '_privateMixinProperty',
+              ownerUri: 'package:provider_app/mixin.dart',
+              ownerName: 'Mixin',
+            ),
+          ),
+        ).future,
+      );
+
+      var instance = await sub.read();
+
+      expect(
+        instance,
+        isA<NumInstance>().having((e) => e.displayString, 'displayString', '0'),
+      );
+
+      await instance.setter('42');
+
+      // read the instance again since it should have changed
+      instance = await sub.read();
+
+      expect(
+        instance,
+        isA<NumInstance>()
+            .having((e) => e.displayString, 'displayString', '42'),
+      );
+    });
+
     test('rawSortedProviderNodesProvider', () async {
       final container = ProviderContainer();
       addTearDown(container.dispose);
@@ -282,6 +317,8 @@
                     isA<ObjectField>()
                         .having((e) => e.ownerName, 'ownerName', 'Counter')
                         .having((e) => e.name, 'name', '_count')
+                        .having((e) => e.ownerUri, 'ownerUri',
+                            'package:provider_app/main.dart')
                         .having((e) => e.isFinal, 'isFinal', false)
                         .having((e) => e.isPrivate, 'isPrivate', true)
                         .having((e) => e.isDefinedByDependency,
@@ -290,6 +327,17 @@
                         .having(
                             (e) => e.ownerName, 'ownerName', 'ChangeNotifier')
                         .having((e) => e.name, 'name', '_listeners')
+                        .having((e) => e.ownerUri, 'ownerUri',
+                            'package:flutter/src/foundation/change_notifier.dart')
+                        .having((e) => e.isFinal, 'isFinal', false)
+                        .having((e) => e.isPrivate, 'isPrivate', true)
+                        .having((e) => e.isDefinedByDependency,
+                            'isDefinedByDependency', true),
+                    isA<ObjectField>()
+                        .having((e) => e.ownerName, 'ownerName', 'Mixin')
+                        .having((e) => e.name, 'name', '_privateMixinProperty')
+                        .having((e) => e.ownerUri, 'ownerUri',
+                            'package:provider_app/mixin.dart')
                         .having((e) => e.isFinal, 'isFinal', false)
                         .having((e) => e.isPrivate, 'isPrivate', true)
                         .having((e) => e.isDefinedByDependency,