Skip to content

[Product Quality] Fix copy-to-clipboard behavior in the console #9204

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
May 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -373,10 +373,33 @@ class DartObjectNode extends TreeNode<DartObjectNode> {
if (text != null) return text!;

final instanceRef = ref!.instanceRef;
final value = instanceRef is InstanceRef
? instanceRef.valueAsString
: instanceRef;
return '$name - $value';
if (instanceRef != null && !name.isNullOrEmpty) {
final length = instanceRef.length;
// Show the variable name, kind, and length for instance kinds that have a
// length (maps, lists, sets, etc).
if (instanceRef.length != null) {
return '$name - ${instanceRef.kind} ($length)';
}

// Show the variable name and value for instance kinds without a length
//(e.g. strings, booleans, ints).
return '$name - ${instanceRef.valueAsString}';
}

// Use the diagnostics node (if it exists). This is only provided for
// Inspector nodes.
final diagnostic = ref?.diagnostic;
final description = diagnostic?.description;
if (description != null) {
final separator = diagnostic!.separator;
final textPreview = diagnostic.json['textPreview'];
return textPreview != null
? '$description$separator $textPreview'
: description;
}

// Fallback to returning the runtime type as a catch-all.
return ref.runtimeType.toString();
}

/// Selects the object in the Flutter Widget inspector.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -553,7 +553,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.14;
MACOSX_DEPLOYMENT_TARGET = 10.15;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx;
SWIFT_COMPILATION_MODE = wholemodule;
Expand Down Expand Up @@ -632,7 +632,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.14;
MACOSX_DEPLOYMENT_TARGET = 10.15;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
Expand Down Expand Up @@ -679,7 +679,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.14;
MACOSX_DEPLOYMENT_TARGET = 10.15;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx;
SWIFT_COMPILATION_MODE = wholemodule;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ To learn more about DevTools, check out the
DevTools. - [#9125](https://github.com/flutter/devtools/pull/9125)
- Dismiss stale banner messages when the connected app state changes. - [#9148](https://github.com/flutter/devtools/pull/9148)
- Fix a focus traversal issue with search fields. [#9166](https://github.com/flutter/devtools/pull/9166)
- Fix an issue where copying all logs in the console would show `null` for any inspected widgets. - [#9204](https://github.com/flutter/devtools/pull/9204)

## Inspector updates

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,18 +114,18 @@ void main() {
await pumpDebuggerScreen(tester, debuggerController);
expect(find.text('Variables'), findsOneWidget);

final listFinder = find.text('Root 1: List (2 items)');
final listFinder = find.text('root1: List (2 items)');

expect(listFinder, findsOneWidget);

final mapFinder = find.textContaining('Root 2: Map (2 items)');
final mapFinder = find.textContaining('root2: Map (2 items)');
final mapElement1Finder = find.textContaining("['key1']: 1.0");
final mapElement2Finder = find.textContaining("['key2']: 2.0");

expect(listFinder, findsOneWidget);
expect(mapFinder, findsOneWidget);
expect(find.textContaining("Root 3: 'test str...'"), findsOneWidget);
expect(find.textContaining('Root 4: true'), findsOneWidget);
expect(find.textContaining("root3: 'test str...'"), findsOneWidget);
expect(find.textContaining('root4: true'), findsOneWidget);

// Initially list is not expanded.
expect(find.textContaining('0: 3'), findsNothing);
Expand All @@ -148,7 +148,7 @@ void main() {
expect(mapElement2Finder, findsOneWidget);

// Expect a tooltip for the set instance.
final setFinder = find.text('Root 5: Set (2 items)');
final setFinder = find.text('root5: Set (2 items)');
expect(setFinder, findsOneWidget);

// Initially set is not expanded.
Expand All @@ -174,7 +174,7 @@ void main() {

await pumpDebuggerScreen(tester, debuggerController);

final listFinder = find.text('Root 1: List (243,621 items)');
final listFinder = find.text('root1: List (243,621 items)');
await verifyGroupings(tester, parentFinder: listFinder);
},
);
Expand All @@ -191,7 +191,7 @@ void main() {

await pumpDebuggerScreen(tester, debuggerController);

final mapFinder = find.text('Root 1: Map (243,621 items)');
final mapFinder = find.text('root1: Map (243,621 items)');
await verifyGroupings(tester, parentFinder: mapFinder);
},
);
Expand All @@ -208,7 +208,7 @@ void main() {

await pumpDebuggerScreen(tester, debuggerController);

final setFinder = find.text('Root 1: Set (243,621 items)');
final setFinder = find.text('root1: Set (243,621 items)');
await verifyGroupings(tester, parentFinder: setFinder);
},
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,4 +105,41 @@ void main() {
expect(str.childCount, equals(0));
expect(str.isPartialObject, isFalse);
});

group('toString', () {
test('string variable', () {
final str = buildStringVariable('Hello there!');
expect(str.toString(), equals('root1 - Hello there!'));
});

test('boolean variable', () {
final boolean = buildBooleanVariable(true);
expect(boolean.toString(), equals('root1 - true'));
});

test('set variable', () {
final set = buildSetVariable(length: 3);
expect(set.toString(), equals('root1 - Set (3)'));
});

test('map variable', () {
final map = buildMapVariable(length: 3);
expect(map.toString(), equals('root1 - Map (3)'));
});

test('string variable', () {
final list = buildListVariable(length: 3);
expect(list.toString(), equals('root1 - List (3)'));
});

testWidgets('Text widget', (WidgetTester tester) async {
final textWidget = buildTextWidgetVariable();
expect(textWidget.toString(), equals('Text: Hello world!'));
});

testWidgets('Row widget', (WidgetTester tester) async {
final rowWidget = buildRowWidgetVariable();
expect(rowWidget.toString(), equals('Row'));
});
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file or at https://developers.google.com/open-source/licenses/bsd.

import 'dart:convert';

import 'package:devtools_app/src/shared/diagnostics/dart_object_node.dart';
import 'package:devtools_app/src/shared/diagnostics/diagnostics_node.dart';
import 'package:devtools_app/src/shared/diagnostics/generic_instance_reference.dart';

import 'package:vm_service/vm_service.dart';
Expand Down Expand Up @@ -213,6 +216,22 @@ DartObjectNode buildBooleanVariable(bool value) {
);
}

DartObjectNode buildTextWidgetVariable() {
return DartObjectNode.fromValue(
value: null,
isolateRef: _isolateRef,
diagnostic: _textWidgetDiagnosticNode,
);
}

DartObjectNode buildRowWidgetVariable() {
return DartObjectNode.fromValue(
value: null,
isolateRef: _isolateRef,
diagnostic: _rowWidgetDiagnosticNode,
);
}

InstanceRef _buildInstanceRefForMap({required int length}) => InstanceRef(
id: _incrementRef(),
kind: InstanceKind.kMap,
Expand Down Expand Up @@ -266,5 +285,63 @@ int _rootNumber = 0;

String _incrementRoot() {
_rootNumber++;
return 'Root $_rootNumber';
return 'root$_rootNumber';
}

final _textWidgetDiagnosticNode = RemoteDiagnosticsNode(
jsonDecode(_textWidgetDiagnosticJson),
null,
false,
null,
);

final _rowWidgetDiagnosticNode = RemoteDiagnosticsNode(
jsonDecode(_rowWidgetDiagnosticJson),
null,
false,
null,
);

const _textWidgetDiagnosticJson = '''
{
"description": "Text",
"type": "_ElementDiagnosticableTreeNode",
"style": "dense",
"hasChildren": true,
"allowWrap": false,
"summaryTree": true,
"locationId": 0,
"creationLocation": {
"file": "file:///Users/prismo/flutter_app/main.dart",
"line": 109,
"column": 23,
"name": "Text"
},
"createdByLocalProject": true,
"textPreview": "Hello world!",
"children": [],
"widgetRuntimeType": "Text",
"stateful": false
}
''';

const _rowWidgetDiagnosticJson = '''
{
"description": "Row",
"type": "_ElementDiagnosticableTreeNode",
"hasChildren": true,
"allowWrap": false,
"summaryTree": true,
"locationId": 0,
"creationLocation": {
"file": "file:///Users/prismo/flutter_app/main.dart",
"line": 109,
"column": 23,
"name": "Row"
},
"createdByLocalProject": true,
"children": [],
"widgetRuntimeType": "Row",
"stateful": false
}
''';