Skip to content

Commit

Permalink
[Inspector V2] Show widget properties in the inspector panel (#8091)
Browse files Browse the repository at this point in the history
  • Loading branch information
elliette authored Jul 31, 2024
1 parent 1862692 commit c500e5a
Show file tree
Hide file tree
Showing 19 changed files with 394 additions and 148 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,16 @@ final _log = Logger('inspector_controller');

const inspectorRefQueryParam = 'inspectorRef';

/// Data pattern containing the properties and render properties for a widget
/// tree node.
typedef WidgetTreeNodeProperties = ({
/// Properties defined directly on the widget.
List<RemoteDiagnosticsNode> widgetProperties,

/// Properties defined on the widget's render object.
List<RemoteDiagnosticsNode> renderProperties,
});

/// This class is based on the InspectorPanel class from the Flutter IntelliJ
/// plugin with some refactors to make it more of a true controller than a view.
class InspectorController extends DisposableController
Expand Down Expand Up @@ -200,6 +210,12 @@ class InspectorController extends DisposableController
ValueListenable<InspectorTreeNode?> get selectedNode => _selectedNode;
final _selectedNode = ValueNotifier<InspectorTreeNode?>(null);

ValueListenable<WidgetTreeNodeProperties> get selectedNodeProperties =>
_selectedNodeProperties;
final _selectedNodeProperties = ValueNotifier<WidgetTreeNodeProperties>(
(widgetProperties: [], renderProperties: []),
);

InspectorTreeNode? lastExpanded;

bool isActive = false;
Expand Down Expand Up @@ -595,9 +611,48 @@ class InspectorController extends DisposableController
endShowNode();

_updateSelectedErrorFromNode(_selectedNode.value);
unawaited(_loadPropertiesForNode(_selectedNode.value));
animateTo(selectedNode.value);
}

Future<void> _loadPropertiesForNode(InspectorTreeNode? node) async {
final widgetProperties = <RemoteDiagnosticsNode>[];
final renderProperties = <RemoteDiagnosticsNode>[];
final diagnostic = node?.diagnostic;
final objectGroupApi = diagnostic?.objectGroupApi;
if (diagnostic != null && objectGroupApi != null) {
try {
// Fetch widget properties:
final wProperties = await diagnostic.getProperties(objectGroupApi);
// Check if the selected node has changed, and if so return early:
if (_selectedNode.value != node) {
return;
}
widgetProperties.addAll(
wProperties.where((p) => p.propertyType != 'RenderObject'),
);
renderProperties.addAll(
wProperties.where((p) => p.propertyType == 'RenderObject'),
);
// Fetch RenderObject properties:
for (final renderObject in renderProperties) {
final rProperties = await renderObject.getProperties(objectGroupApi);
// Check if the selected node has changed, and if so return early:
if (_selectedNode.value != node) {
return;
}
renderProperties.addAll(rProperties);
}
} catch (e, st) {
_log.warning(e, st);
}
}
_selectedNodeProperties.value = (
widgetProperties: widgetProperties,
renderProperties: renderProperties
);
}

/// Update the index of the selected error based on a node that has been
/// selected in the tree.
void _updateSelectedErrorFromNode(InspectorTreeNode? node) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ import '../../shared/screen.dart';
import '../../shared/ui/search.dart';
import '../../shared/utils.dart';
import 'inspector_controller.dart';
import 'inspector_screen_details_tab.dart';
import 'inspector_tree_controller.dart';
import 'widget_details.dart';

class InspectorScreen extends Screen {
InspectorScreen() : super.fromMetaData(ScreenMetaData.inspector);
Expand Down Expand Up @@ -144,9 +144,7 @@ class InspectorScreenBodyState extends State<InspectorScreenBody>
initialFractions: const [0.33, 0.67],
children: [
inspectorTree,
InspectorDetails(
controller: controller,
),
WidgetDetails(controller: controller),
],
);
return Column(
Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Copyright 2024 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:devtools_app_shared/utils.dart';
import 'package:flutter/widgets.dart';

import '../../shared/console/eval/inspector_tree_v2.dart';
import '../../shared/diagnostics/diagnostics_node.dart';
import 'inspector_controller.dart';
import 'inspector_screen.dart';
import 'layout_explorer/box/box.dart';
import 'layout_explorer/flex/flex.dart';
import 'widget_properties/properties_view.dart';

/// Panes showing details pertaining to the selected widget.
///
/// Includes both the [FlexLayoutExplorerWidget] or [BoxLayoutExplorerWidget]
/// and the [PropertiesView].
class WidgetDetails extends StatefulWidget {
const WidgetDetails({super.key, required this.controller});

final InspectorController controller;

@override
State<WidgetDetails> createState() => _WidgetDetailsState();
}

class _WidgetDetailsState extends State<WidgetDetails> with AutoDisposeMixin {
InspectorController get controller => widget.controller;

RemoteDiagnosticsNode? get selectedNode =>
controller.selectedNode.value?.diagnostic;

@override
Widget build(BuildContext context) {
return ValueListenableBuilder<InspectorTreeNode?>(
valueListenable: controller.selectedNode,
builder: (context, _, __) {
final node = selectedNode;
if (node == null) {
return const Center(
child: Text(
'Select a widget to view its layout.',
textAlign: TextAlign.center,
overflow: TextOverflow.clip,
),
);
}

return RoundedOutlinedBorder(
child: SplitPane(
axis: isScreenWiderThan(
context,
InspectorScreenBodyState.minScreenWidthForTextBeforeScaling,
)
? Axis.horizontal
: Axis.vertical,
initialFractions: const [0.5, 0.5],
children: [
if (FlexLayoutExplorerWidget.shouldDisplay(node)) ...[
FlexLayoutExplorerWidget(controller),
] else if (BoxLayoutExplorerWidget.shouldDisplay(node)) ...[
BoxLayoutExplorerWidget(controller),
] else ...[
const Center(
child: Text(
'Currently, the Layout Explorer only supports Box and Flex-based widgets.',
textAlign: TextAlign.center,
overflow: TextOverflow.clip,
),
),
],
PropertiesView(controller: controller, node: node),
],
),
);
},
);
}
}
Loading

0 comments on commit c500e5a

Please sign in to comment.