Skip to content

Commit

Permalink
Update Object Inspector selector to use a drop down instead of a tab …
Browse files Browse the repository at this point in the history
…bar (#6008)

The Object Inspector's tab bar selector could be too wide to display all
the options, particularly when the selector's screen real estate is
small. This change switches the selector from a tab bar to a drop down
button.

In addition, this change fixes some UI issues with
`RoundedDropDownButton` and allows for creating
`RoundedDropDownButton`s with only some corners rounded.

Partial fix to #5765
  • Loading branch information
bkonyi authored Jul 10, 2023
1 parent c8dd245 commit 7c1557a
Show file tree
Hide file tree
Showing 5 changed files with 156 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

import '../../../shared/analytics/constants.dart' as gac;
import '../../../shared/common_widgets.dart';
import '../../../shared/split.dart';
import '../../../shared/ui/tab.dart';
import '../../../shared/ui/drop_down_button.dart';
import '../../debugger/program_explorer.dart';
import '../../debugger/program_explorer_model.dart';
import '../vm_developer_tools_controller.dart';
Expand Down Expand Up @@ -61,48 +62,120 @@ class _ObjectInspectorViewState extends State<_ObjectInspectorView>
axis: Axis.horizontal,
initialFractions: const [0.2, 0.8],
children: [
AnalyticsTabbedView(
const ObjectInspectorSelector(),
ObjectViewport(
controller: controller,
),
],
);
}
}

class ObjectInspectorSelector extends StatefulWidget {
const ObjectInspectorSelector({
super.key,
});

static const kProgramExplorer = 'Program Explorer';
static const kObjectStore = 'Object Store';
static const kClassHierarchy = 'Class Hierarchy';

@override
State<ObjectInspectorSelector> createState() =>
_ObjectInspectorSelectorState();
}

class _ObjectInspectorSelectorState extends State<ObjectInspectorSelector> {
String value = ObjectInspectorSelector.kProgramExplorer;
late ObjectInspectorViewController controller;

@override
void didChangeDependencies() {
super.didChangeDependencies();
final vmDeveloperToolsController =
Provider.of<VMDeveloperToolsController>(context);
controller = vmDeveloperToolsController.objectInspectorViewController;
unawaited(controller.init());
}

@override
Widget build(BuildContext context) {
return Column(
children: [
AnalyticsDropDownButton<String>(
gaScreen: gac.objectInspectorScreen,
tabs: [
(
tab: DevToolsTab.create(
tabName: 'Program Explorer',
gaPrefix: gac.programExplorer,
),
tabView: ProgramExplorer(
controller: controller.programExplorerController,
onNodeSelected: _onNodeSelected,
displayHeader: false,
),
gaDropDownId: gac.objectInspectorDropDown,
message: '',
isExpanded: true,
value: value,
roundedCornerOptions: const RoundedCornerOptions(
showBottomLeft: false,
showBottomRight: false,
),
items: [
_buildMenuItem(
ObjectInspectorSelector.kProgramExplorer,
gac.programExplorer,
),
(
tab: DevToolsTab.create(
tabName: 'Object Store',
gaPrefix: gac.objectStore,
),
tabView: ObjectStoreViewer(
controller: controller.objectStoreController,
onLinkTapped: controller.findAndSelectNodeForObject,
),
_buildMenuItem(
ObjectInspectorSelector.kObjectStore,
gac.objectStore,
),
(
tab: DevToolsTab.create(
tabName: 'Class Hierarchy',
gaPrefix: gac.classHierarchy,
),
tabView: ClassHierarchyExplorer(
controller: controller,
),
_buildMenuItem(
ObjectInspectorSelector.kClassHierarchy,
gac.classHierarchy,
),
],
onChanged: (newValue) => setState(() {
value = newValue!;
}),
),
ObjectViewport(
controller: controller,
Expanded(
child: RoundedOutlinedBorder(
showTopLeft: false,
showTopRight: false,
child: _selectedWidget(),
),
),
],
);
}

({DropdownMenuItem<String> item, String gaId}) _buildMenuItem(
String text,
String gaId,
) {
return (
item: DropdownMenuItem<String>(
value: text,
child: Text(text),
),
gaId: gaId,
);
}

Widget _selectedWidget() {
switch (value) {
case ObjectInspectorSelector.kProgramExplorer:
return ProgramExplorer(
controller: controller.programExplorerController,
onNodeSelected: _onNodeSelected,
displayHeader: false,
);
case ObjectInspectorSelector.kObjectStore:
return ObjectStoreViewer(
controller: controller.objectStoreController,
onLinkTapped: controller.findAndSelectNodeForObject,
);
case ObjectInspectorSelector.kClassHierarchy:
return ClassHierarchyExplorer(
controller: controller,
);
default:
throw StateError('Unexpected value: $value');
}
}

void _onNodeSelected(VMServiceObjectNode node) {
final objRef = node.object;
final location = node.location;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ const copyLogs = 'copyLogs';

// Object explorer:
const objectInspectorScreen = 'objectInspector';
const objectInspectorDropDown = 'dropdown';
const programExplorer = 'programExplorer';
const objectStore = 'objectStore';
const classHierarchy = 'classHierarchy';
Expand Down
49 changes: 44 additions & 5 deletions packages/devtools_app/lib/src/shared/common_widgets.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1104,15 +1104,31 @@ class FilterButton extends StatelessWidget {
}
}

class RoundedCornerOptions {
const RoundedCornerOptions({
this.showTopLeft = true,
this.showTopRight = true,
this.showBottomLeft = true,
this.showBottomRight = true,
});

final bool showTopLeft;
final bool showTopRight;
final bool showBottomLeft;
final bool showBottomRight;
}

class RoundedDropDownButton<T> extends StatelessWidget {
const RoundedDropDownButton({
Key? key,
this.value,
this.onChanged,
this.isDense = false,
this.isExpanded = false,
this.style,
this.selectedItemBuilder,
this.items,
this.roundedCornerOptions,
}) : super(key: key);

final T? value;
Expand All @@ -1121,29 +1137,52 @@ class RoundedDropDownButton<T> extends StatelessWidget {

final bool isDense;

final bool isExpanded;

final TextStyle? style;

final DropdownButtonBuilder? selectedItemBuilder;

final List<DropdownMenuItem<T>>? items;

final RoundedCornerOptions? roundedCornerOptions;

@override
Widget build(BuildContext context) {
final bgColor = Theme.of(context).colorScheme.backgroundColorSelected;

Radius selectRadius(bool show) {
return show ? const Radius.circular(defaultBorderRadius) : Radius.zero;
}

final showTopLeft = roundedCornerOptions?.showTopLeft ?? true;
final showTopRight = roundedCornerOptions?.showTopRight ?? true;
final showBottomLeft = roundedCornerOptions?.showBottomLeft ?? true;
final showBottomRight = roundedCornerOptions?.showBottomRight ?? true;
return RoundedOutlinedBorder(
showTopLeft: showTopLeft,
showTopRight: showTopRight,
showBottomLeft: showBottomLeft,
showBottomRight: showBottomRight,
child: Center(
child: Container(
padding: const EdgeInsets.only(
left: defaultSpacing,
right: borderPadding,
),
child: SizedBox(
height: defaultButtonHeight - 2.0, // subtract 2.0 for width of border
child: DropdownButtonHideUnderline(
child: DropdownButton<T>(
padding: const EdgeInsets.only(
left: defaultSpacing,
right: borderPadding,
),
value: value,
onChanged: onChanged,
isDense: isDense,
isExpanded: isExpanded,
borderRadius: BorderRadius.only(
topLeft: selectRadius(showTopLeft),
topRight: selectRadius(showTopRight),
bottomLeft: selectRadius(showBottomLeft),
bottomRight: selectRadius(showBottomRight),
),
style: style,
selectedItemBuilder: selectedItemBuilder,
items: items,
Expand Down
6 changes: 6 additions & 0 deletions packages/devtools_app/lib/src/shared/ui/drop_down_button.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ class AnalyticsDropDownButton<T> extends StatelessWidget {
required this.onChanged,
this.sendAnalytics = true,
this.isDense = false,
this.isExpanded = false,
this.roundedCornerOptions,
});

/// The GA ID for the screen this widget is displayed on.
Expand Down Expand Up @@ -50,6 +52,8 @@ class AnalyticsDropDownButton<T> extends StatelessWidget {
final void Function(T?)? onChanged;

final bool isDense;
final bool isExpanded;
final RoundedCornerOptions? roundedCornerOptions;

@override
Widget build(BuildContext context) {
Expand All @@ -59,10 +63,12 @@ class AnalyticsDropDownButton<T> extends StatelessWidget {
message: message,
child: RoundedDropDownButton<T>(
isDense: isDense,
isExpanded: isExpanded,
style: Theme.of(context).textTheme.bodyMedium,
value: value,
items: items?.map((e) => e.item).toList(),
onChanged: _onChanged,
roundedCornerOptions: roundedCornerOptions,
),
),
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,6 @@ void main() {
expect(find.byType(ObjectViewport), findsOneWidget);
expect(find.text('Program Explorer'), findsOneWidget);
expect(find.text('Outline'), findsOneWidget);
expect(find.text('Object Store'), findsOneWidget);
expect(find.text('Class Hierarchy'), findsOneWidget);
expect(find.text('No object selected.'), findsOneWidget);
expect(find.byTooltip('Refresh'), findsOneWidget);
},
Expand Down

0 comments on commit 7c1557a

Please sign in to comment.