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 601a466756c..420bc43165b 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 @@ Future> _parseFields( 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(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 f18e8f266ba..41658c4bc69 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:flutter/material.dart'; 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 _MyAppState extends State { } } -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 00000000000..45bbc3eca95 --- /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 e7f92a52252..ee8a935ac8c 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 @@ Future runProviderControllerTests(FlutterTestEnvironment env) async { }, 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().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() + .having((e) => e.displayString, 'displayString', '42'), + ); + }); + test('rawSortedProviderNodesProvider', () async { final container = ProviderContainer(); addTearDown(container.dispose); @@ -282,6 +317,8 @@ Future runProviderControllerTests(FlutterTestEnvironment env) async { isA() .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 @@ Future runProviderControllerTests(FlutterTestEnvironment env) async { .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() + .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,