diff --git a/packages/devtools_app/lib/src/screens/memory/panes/profile/profile_view.dart b/packages/devtools_app/lib/src/screens/memory/panes/profile/profile_view.dart index 16c3827284c..7325b420604 100644 --- a/packages/devtools_app/lib/src/screens/memory/panes/profile/profile_view.dart +++ b/packages/devtools_app/lib/src/screens/memory/panes/profile/profile_view.dart @@ -404,6 +404,7 @@ class _GCStatsTable extends StatelessWidget { columns: _columns, defaultSortColumn: _columns.first, defaultSortDirection: SortDirection.ascending, + sizeColumnsToFit: false, ), ); }, @@ -426,6 +427,8 @@ class AllocationProfileTableView extends StatefulWidget { class AllocationProfileTableViewState extends State { + static const _scrollbarHeight = 10.0; + @override void initState() { super.initState(); @@ -459,7 +462,10 @@ class AllocationProfileTableViewState // and columns) and one data row. We add a slight padding to // ensure the underlying scrollable area has enough space to not // display a scroll bar. - height: defaultRowHeight + defaultHeaderHeight * 2 + 1, + height: + defaultRowHeight + + defaultHeaderHeight * 2 + + _scrollbarHeight, child: _GCStatsTable(controller: widget.controller), ), const ThickDivider(), diff --git a/packages/devtools_app/lib/src/shared/table/_flat_table.dart b/packages/devtools_app/lib/src/shared/table/_flat_table.dart index c6fa4a85207..087148ca739 100644 --- a/packages/devtools_app/lib/src/shared/table/_flat_table.dart +++ b/packages/devtools_app/lib/src/shared/table/_flat_table.dart @@ -317,7 +317,6 @@ class FlatTableState extends State> with AutoDisposeMixin { Widget _buildRow({ required BuildContext context, - required LinkedScrollControllerGroup linkedScrollControllerGroup, required int index, required List columnWidths, required bool isPinned, @@ -327,7 +326,6 @@ class FlatTableState extends State> with AutoDisposeMixin { final data = isPinned ? pinnedData : tableController.tableData.value.data; if (index >= data.length) { return TableRow.filler( - linkedScrollControllerGroup: linkedScrollControllerGroup, columns: tableController.columns, columnGroups: tableController.columnGroups, columnWidths: columnWidths, @@ -344,7 +342,6 @@ class FlatTableState extends State> with AutoDisposeMixin { builder: (context, selected, _) { return TableRow( key: widget.keyFactory(node), - linkedScrollControllerGroup: linkedScrollControllerGroup, node: node, onPressed: (T? selection) { widget.selectionNotifier.value = selection; diff --git a/packages/devtools_app/lib/src/shared/table/_table_column.dart b/packages/devtools_app/lib/src/shared/table/_table_column.dart index 521d212b481..79d7c2a7407 100644 --- a/packages/devtools_app/lib/src/shared/table/_table_column.dart +++ b/packages/devtools_app/lib/src/shared/table/_table_column.dart @@ -129,15 +129,12 @@ class _ColumnGroupHeaderRow extends StatelessWidget { const _ColumnGroupHeaderRow({ required this.groups, required this.columnWidths, - required this.scrollController, }); final List groups; final List columnWidths; - final ScrollController scrollController; - @override Widget build(BuildContext context) { return Container( @@ -147,7 +144,6 @@ class _ColumnGroupHeaderRow extends StatelessWidget { ), child: ListView.builder( scrollDirection: Axis.horizontal, - controller: scrollController, itemCount: groups.length + groups.numSpacers, itemBuilder: (context, int i) { if (i % 2 == 1) { diff --git a/packages/devtools_app/lib/src/shared/table/_table_row.dart b/packages/devtools_app/lib/src/shared/table/_table_row.dart index 763438888ec..9161ca180fe 100644 --- a/packages/devtools_app/lib/src/shared/table/_table_row.dart +++ b/packages/devtools_app/lib/src/shared/table/_table_row.dart @@ -18,7 +18,6 @@ class TableRow extends StatefulWidget { /// [node]. const TableRow({ super.key, - required this.linkedScrollControllerGroup, required this.node, required this.columns, required this.columnWidths, @@ -44,7 +43,6 @@ class TableRow extends StatefulWidget { /// Constructs a [TableRow] that is empty. const TableRow.filler({ super.key, - required this.linkedScrollControllerGroup, required this.columns, required this.columnWidths, this.columnGroups, @@ -71,7 +69,6 @@ class TableRow extends StatefulWidget { /// of any [node]. const TableRow.tableColumnHeader({ super.key, - required this.linkedScrollControllerGroup, required this.columns, required this.columnWidths, required this.columnGroups, @@ -98,7 +95,6 @@ class TableRow extends StatefulWidget { /// [node]. const TableRow.tableColumnGroupHeader({ super.key, - required this.linkedScrollControllerGroup, required this.columnGroups, required this.columnWidths, required this.sortColumn, @@ -121,8 +117,6 @@ class TableRow extends StatefulWidget { enableHoverHandling = false, _rowType = _TableRowType.columnGroupHeader; - final LinkedScrollControllerGroup linkedScrollControllerGroup; - final T? node; final List> columns; @@ -200,8 +194,6 @@ class _TableRowState extends State> SearchableMixin { Key? contentKey; - late ScrollController scrollController; - bool isSearchMatch = false; bool isActiveSearchMatch = false; @@ -216,7 +208,6 @@ class _TableRowState extends State> void initState() { super.initState(); contentKey = ValueKey(this); - scrollController = widget.linkedScrollControllerGroup.addAndGet(); _initSearchListeners(); _rowDisplayParts = _rowDisplayPartsHelper(); @@ -242,11 +233,6 @@ class _TableRowState extends State> void didUpdateWidget(TableRow oldWidget) { super.didUpdateWidget(oldWidget); setExpanded(widget.isExpanded); - if (oldWidget.linkedScrollControllerGroup != - widget.linkedScrollControllerGroup) { - scrollController.dispose(); - scrollController = widget.linkedScrollControllerGroup.addAndGet(); - } _rowDisplayParts = _rowDisplayPartsHelper(); @@ -256,12 +242,6 @@ class _TableRowState extends State> _initSearchListeners(); } - @override - void dispose() { - scrollController.dispose(); - super.dispose(); - } - @override Widget build(BuildContext context) { final node = widget.node; @@ -530,18 +510,13 @@ class _TableRowState extends State> return _ColumnGroupHeaderRow( groups: groups, columnWidths: widget.columnWidths, - scrollController: scrollController, ); } Widget rowContent = Padding( padding: const EdgeInsets.symmetric(horizontal: defaultSpacing), - child: ExtentDelegateListView( - scrollDirection: Axis.horizontal, - physics: const ClampingScrollPhysics(), - controller: scrollController, - extentDelegate: rowExtentDelegate, - childrenDelegate: SliverChildBuilderDelegate((context, int i) { + child: Row( + children: List.generate(_rowDisplayParts.length, (int i) { final columnIndexMap = _columnIndexMapHelper(_rowDisplayParts); final displayTypeForIndex = _rowDisplayParts[i]; switch (displayTypeForIndex) { @@ -559,7 +534,7 @@ class _TableRowState extends State> case _TableRowPartDisplayType.columnGroupSpacer: return const _ColumnGroupSpacer(); } - }, childCount: _rowDisplayParts.length), + }), ), ); diff --git a/packages/devtools_app/lib/src/shared/table/_tree_table.dart b/packages/devtools_app/lib/src/shared/table/_tree_table.dart index 4bc64ef0880..1880f50a34a 100644 --- a/packages/devtools_app/lib/src/shared/table/_tree_table.dart +++ b/packages/devtools_app/lib/src/shared/table/_tree_table.dart @@ -282,7 +282,6 @@ class TreeTableState> extends State> Widget _buildRow({ required BuildContext context, - required LinkedScrollControllerGroup linkedScrollControllerGroup, required int index, required List columnWidths, required bool isPinned, @@ -293,7 +292,6 @@ class TreeTableState> extends State> node.index = index; return TableRow( key: widget.keyFactory(node), - linkedScrollControllerGroup: linkedScrollControllerGroup, node: node, onPressed: (item) => _onItemPressed(item, index), backgroundColor: alternatingColorForIndex( diff --git a/packages/devtools_app/lib/src/shared/table/table.dart b/packages/devtools_app/lib/src/shared/table/table.dart index 35b78533d5f..a7a1ce4a477 100644 --- a/packages/devtools_app/lib/src/shared/table/table.dart +++ b/packages/devtools_app/lib/src/shared/table/table.dart @@ -13,7 +13,6 @@ import 'package:flutter/services.dart'; import '../primitives/collapsible_mixin.dart'; import '../primitives/extent_delegate_list.dart'; -import '../primitives/flutter_widgets/linked_scroll_controller.dart'; import '../primitives/trees.dart'; import '../primitives/utils.dart'; import '../ui/common_widgets.dart'; @@ -32,7 +31,6 @@ part '_tree_table.dart'; typedef IndexedScrollableWidgetBuilder = Widget Function({ required BuildContext context, - required LinkedScrollControllerGroup linkedScrollControllerGroup, required int index, required List columnWidths, required bool isPinned, @@ -113,9 +111,9 @@ class DevToolsTable extends StatefulWidget { @visibleForTesting class DevToolsTableState extends State> with AutoDisposeMixin { - late LinkedScrollControllerGroup _linkedHorizontalScrollControllerGroup; late ScrollController scrollController; late ScrollController pinnedScrollController; + late ScrollController _horizontalScrollbarController; late List _data; @@ -134,13 +132,13 @@ class DevToolsTableState extends State> _initDataAndAddListeners(); - _linkedHorizontalScrollControllerGroup = LinkedScrollControllerGroup(); - final initialScrollOffset = widget.preserveVerticalScrollPosition ? widget.tableController.tableUiState.scrollOffset : 0.0; widget.tableController.initScrollController(initialScrollOffset); scrollController = widget.tableController.verticalScrollController!; + _horizontalScrollbarController = + widget.tableController.horizontalScrollController!; if (widget.startScrolledAtBottom) { WidgetsBinding.instance.addPostFrameCallback((_) { @@ -373,7 +371,6 @@ class DevToolsTableState extends State> Widget _buildItem(BuildContext context, int index, {bool isPinned = false}) { return widget.rowBuilder( context: context, - linkedScrollControllerGroup: _linkedHorizontalScrollControllerGroup, index: index, columnWidths: adjustedColumnWidths, isPinned: isPinned, @@ -406,95 +403,99 @@ class DevToolsTableState extends State> scrollController.jumpTo(tableUiState.scrollOffset); } - // TODO(kenz): add horizontal scrollbar. return LayoutBuilder( builder: (context, constraints) { final viewWidth = constraints.maxWidth; _adjustColumnWidthsForViewSize(viewWidth); - return SelectionArea( - child: SizedBox( - width: max(viewWidth, _tableWidthForOriginalColumns), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - if (showColumnGroupHeader) - TableRow.tableColumnGroupHeader( - linkedScrollControllerGroup: - _linkedHorizontalScrollControllerGroup, - columnGroups: columnGroups, - columnWidths: adjustedColumnWidths, - sortColumn: sortColumn, - sortDirection: tableUiState.sortDirection, - secondarySortColumn: - widget.tableController.secondarySortColumn, - onSortChanged: widget.tableController.sortDataAndNotify, - tall: widget.tallHeaders, - backgroundColor: widget.headerColor, - ), - // TODO(kenz): add support for excluding column headers. - TableRow.tableColumnHeader( - key: const Key('Table header'), - linkedScrollControllerGroup: - _linkedHorizontalScrollControllerGroup, - columns: widget.tableController.columns, - columnGroups: columnGroups, - columnWidths: adjustedColumnWidths, - sortColumn: sortColumn, - sortDirection: tableUiState.sortDirection, - secondarySortColumn: - widget.tableController.secondarySortColumn, - onSortChanged: widget.tableController.sortDataAndNotify, - tall: widget.tallHeaders, - backgroundColor: widget.headerColor, - ), - if (pinnedData.isNotEmpty) ...[ - SizedBox( - height: _pinnedDataHeight(constraints), - child: Scrollbar( - thumbVisibility: true, - controller: pinnedScrollController, - child: ListView.builder( - controller: pinnedScrollController, - itemCount: pinnedData.length, - itemExtent: widget.rowItemExtent, - itemBuilder: (context, index) => - _buildItem(context, index, isPinned: true), + return Scrollbar( + controller: _horizontalScrollbarController, + thumbVisibility: true, + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + controller: _horizontalScrollbarController, + child: SelectionArea( + child: SizedBox( + width: max(viewWidth, _tableWidthForOriginalColumns), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + if (showColumnGroupHeader) + TableRow.tableColumnGroupHeader( + columnGroups: columnGroups, + columnWidths: adjustedColumnWidths, + sortColumn: sortColumn, + sortDirection: tableUiState.sortDirection, + secondarySortColumn: + widget.tableController.secondarySortColumn, + onSortChanged: widget.tableController.sortDataAndNotify, + tall: widget.tallHeaders, + backgroundColor: widget.headerColor, ), + // TODO(kenz): add support for excluding column headers. + TableRow.tableColumnHeader( + key: const Key('Table header'), + columns: widget.tableController.columns, + columnGroups: columnGroups, + columnWidths: adjustedColumnWidths, + sortColumn: sortColumn, + sortDirection: tableUiState.sortDirection, + secondarySortColumn: + widget.tableController.secondarySortColumn, + onSortChanged: widget.tableController.sortDataAndNotify, + tall: widget.tallHeaders, + backgroundColor: widget.headerColor, ), - ), - const ThickDivider(), - ], - Expanded( - child: Scrollbar( - thumbVisibility: true, - controller: scrollController, - child: GestureDetector( - behavior: HitTestBehavior.translucent, - onTapDown: (a) => widget.focusNode?.requestFocus(), - child: Focus( - autofocus: true, - onKeyEvent: (_, event) => widget.handleKeyEvent != null - ? widget.handleKeyEvent!( - event, - scrollController, + if (pinnedData.isNotEmpty) ...[ + SizedBox( + height: _pinnedDataHeight(constraints), + child: Scrollbar( + thumbVisibility: true, + controller: pinnedScrollController, + child: ListView.builder( + controller: pinnedScrollController, + itemCount: pinnedData.length, + itemExtent: widget.rowItemExtent, + itemBuilder: (context, index) => + _buildItem(context, index, isPinned: true), + ), + ), + ), + const ThickDivider(), + ], + Expanded( + child: Scrollbar( + thumbVisibility: true, + controller: scrollController, + child: GestureDetector( + behavior: HitTestBehavior.translucent, + onTapDown: (a) => widget.focusNode?.requestFocus(), + child: Focus( + autofocus: true, + onKeyEvent: (_, event) => + widget.handleKeyEvent != null + ? widget.handleKeyEvent!( + event, + scrollController, + constraints, + ) + : KeyEventResult.ignored, + focusNode: widget.focusNode, + child: ListView.builder( + controller: scrollController, + itemCount: _dataRowCount( constraints, - ) - : KeyEventResult.ignored, - focusNode: widget.focusNode, - child: ListView.builder( - controller: scrollController, - itemCount: _dataRowCount( - constraints, - showColumnGroupHeader, + showColumnGroupHeader, + ), + itemExtent: widget.rowItemExtent, + itemBuilder: _buildItem, + ), ), - itemExtent: widget.rowItemExtent, - itemBuilder: _buildItem, ), ), ), - ), + ], ), - ], + ), ), ), ); diff --git a/packages/devtools_app/lib/src/shared/table/table_controller.dart b/packages/devtools_app/lib/src/shared/table/table_controller.dart index 256b546b344..c7945e8dcdf 100644 --- a/packages/devtools_app/lib/src/shared/table/table_controller.dart +++ b/packages/devtools_app/lib/src/shared/table/table_controller.dart @@ -277,10 +277,13 @@ abstract class TableControllerBase extends DisposableController { ScrollController? verticalScrollController; + ScrollController? horizontalScrollController; + void initScrollController([double initialScrollOffset = 0.0]) { verticalScrollController = ScrollController( initialScrollOffset: initialScrollOffset, ); + horizontalScrollController = ScrollController(); } void storeScrollPosition() { @@ -357,6 +360,8 @@ abstract class TableControllerBase extends DisposableController { void dispose() { verticalScrollController?.dispose(); verticalScrollController = null; + horizontalScrollController?.dispose(); + horizontalScrollController = null; _tableData.dispose(); super.dispose(); } diff --git a/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md b/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md index 6b3be3e26d4..68e396ad784 100644 --- a/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md +++ b/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md @@ -15,7 +15,8 @@ To learn more about DevTools, check out the ## General updates -TODO: Remove this section if there are not any general updates. +- Added a horizontal scrollbar to data tables to help with navigation. - + [#9482](https://github.com/flutter/devtools/pull/9482) ## Inspector updates diff --git a/packages/devtools_app/test/shared/table/table_test.dart b/packages/devtools_app/test/shared/table/table_test.dart index 88b623775f4..b2303f43dcd 100644 --- a/packages/devtools_app/test/shared/table/table_test.dart +++ b/packages/devtools_app/test/shared/table/table_test.dart @@ -1379,32 +1379,34 @@ void main() { } }); - testWidgets('properly collapses and expands the tree', ( - WidgetTester tester, - ) async { - final table = TreeTable( - columns: [_NumberColumn(), treeColumn], - dataRoots: [tree1], - dataKey: 'test-data', - treeColumn: treeColumn, - keyFactory: (d) => Key(d.name), - defaultSortColumn: treeColumn, - defaultSortDirection: SortDirection.ascending, - ); - await tester.pumpWidget(wrap(table)); - await tester.pumpAndSettle(); + testWidgetsWithWindowSize( + 'properly collapses and expands the tree', + windowSize, + (WidgetTester tester) async { + final table = TreeTable( + columns: [_NumberColumn(), treeColumn], + dataRoots: [tree1], + dataKey: 'test-data', + treeColumn: treeColumn, + keyFactory: (d) => Key(d.name), + defaultSortColumn: treeColumn, + defaultSortDirection: SortDirection.ascending, + ); + await tester.pumpWidget(wrap(table)); + await tester.pumpAndSettle(); - expect(tree1.isExpanded, true); - await tester.tap(find.byKey(const Key('Foo'))); - await tester.pumpAndSettle(); - expect(tree1.isExpanded, false); - await tester.tap(find.byKey(const Key('Foo'))); - await tester.pumpAndSettle(); - expect(tree1.isExpanded, true); - await tester.tap(find.byKey(const Key('Bar'))); - await tester.pumpAndSettle(); - expect(tree1.children[0].isExpanded, false); - }); + expect(tree1.isExpanded, true); + await tester.tap(find.byKey(const Key('Foo'))); + await tester.pumpAndSettle(); + expect(tree1.isExpanded, false); + await tester.tap(find.byKey(const Key('Foo'))); + await tester.pumpAndSettle(); + expect(tree1.isExpanded, true); + await tester.tap(find.byKey(const Key('Bar'))); + await tester.pumpAndSettle(); + expect(tree1.children[0].isExpanded, false); + }, + ); testWidgets('starts with sorted data', (WidgetTester tester) async { expect(tree1.children[0].name, 'Bar'); @@ -1569,85 +1571,87 @@ void main() { }); }); - testWidgets('properly colors rows with alternating colors', ( - WidgetTester tester, - ) async { - final data = TestData('Foo', 0) - ..children.addAll([ - TestData('Bar', 1) - ..children.addAll([ - TestData('Baz', 2), - TestData('Qux', 3), - TestData('Snap', 4), - ]), - TestData('Crackle', 5), - ]) - ..expandCascading(); - final table = TreeTable( - columns: [_NumberColumn(), treeColumn], - dataRoots: [data], - dataKey: 'test-data', - treeColumn: treeColumn, - keyFactory: (d) => Key(d.name), - defaultSortColumn: treeColumn, - defaultSortDirection: SortDirection.ascending, - ); + testWidgetsWithWindowSize( + 'properly colors rows with alternating colors', + windowSize, + (WidgetTester tester) async { + final data = TestData('Foo', 0) + ..children.addAll([ + TestData('Bar', 1) + ..children.addAll([ + TestData('Baz', 2), + TestData('Qux', 3), + TestData('Snap', 4), + ]), + TestData('Crackle', 5), + ]) + ..expandCascading(); + final table = TreeTable( + columns: [_NumberColumn(), treeColumn], + dataRoots: [data], + dataKey: 'test-data', + treeColumn: treeColumn, + keyFactory: (d) => Key(d.name), + defaultSortColumn: treeColumn, + defaultSortDirection: SortDirection.ascending, + ); - final fooFinder = find.byKey(const Key('Foo')); - final barFinder = find.byKey(const Key('Bar')); - final bazFinder = find.byKey(const Key('Baz')); - final quxFinder = find.byKey(const Key('Qux')); - final snapFinder = find.byKey(const Key('Snap')); - final crackleFinder = find.byKey(const Key('Crackle')); + final fooFinder = find.byKey(const Key('Foo')); + final barFinder = find.byKey(const Key('Bar')); + final bazFinder = find.byKey(const Key('Baz')); + final quxFinder = find.byKey(const Key('Qux')); + final snapFinder = find.byKey(const Key('Snap')); + final crackleFinder = find.byKey(const Key('Crackle')); - // Expected ARGB color values. - const color1Value = '(a: 1.0, r: 0.94, g: 0.94, b: 0.94)'; - const color2Value = '(a: 1.0, r: 1.0, g: 1.0, b: 1.0)'; - const rowSelectedColorValue = '(a: 1.0, r: 1.0, g: 1.0, b: 1.0)'; + // Expected ARGB color values. + const color1Value = '(a: 1.0, r: 0.94, g: 0.94, b: 0.94)'; + const color2Value = '(a: 1.0, r: 1.0, g: 1.0, b: 1.0)'; + const rowSelectedColorValue = '(a: 1.0, r: 1.0, g: 1.0, b: 1.0)'; - await tester.pumpWidget(wrap(table)); - await tester.pumpAndSettle(); - expect(tree1.isExpanded, true); - - expect(fooFinder, findsOneWidget); - expect(barFinder, findsOneWidget); - expect(bazFinder, findsOneWidget); - expect(quxFinder, findsOneWidget); - expect(snapFinder, findsOneWidget); - expect(crackleFinder, findsOneWidget); - TableRow fooRow = tester.widget(fooFinder); - TableRow barRow = tester.widget(barFinder); - final TableRow bazRow = tester.widget(bazFinder); - final TableRow quxRow = tester.widget(quxFinder); - final TableRow snapRow = tester.widget(snapFinder); - TableRow crackleRow = tester.widget(crackleFinder); - - expect(fooRow.backgroundColor!.toArgbString(), color1Value); - expect(barRow.backgroundColor!.toArgbString(), color2Value); - expect(bazRow.backgroundColor!.toArgbString(), color1Value); - expect(quxRow.backgroundColor!.toArgbString(), color2Value); - expect(snapRow.backgroundColor!.toArgbString(), color1Value); - expect(crackleRow.backgroundColor!.toArgbString(), color2Value); - - await tester.tap(barFinder); - await tester.pumpAndSettle(); - expect(fooFinder, findsOneWidget); - expect(barFinder, findsOneWidget); - expect(bazFinder, findsNothing); - expect(quxFinder, findsNothing); - expect(snapFinder, findsNothing); - expect(crackleFinder, findsOneWidget); - fooRow = tester.widget(fooFinder); - barRow = tester.widget(barFinder); - crackleRow = tester.widget(crackleFinder); - - expect(fooRow.backgroundColor!.toArgbString(), color1Value); - // [barRow] has the rowSelected color after being tapped. - expect(barRow.backgroundColor!.toArgbString(), rowSelectedColorValue); - // [crackleRow] has a different background color after collapsing previous - // row (Bar). - expect(crackleRow.backgroundColor!.toArgbString(), color1Value); - }); + await tester.pumpWidget(wrap(table)); + await tester.pumpAndSettle(); + expect(tree1.isExpanded, true); + + expect(fooFinder, findsOneWidget); + expect(barFinder, findsOneWidget); + expect(bazFinder, findsOneWidget); + expect(quxFinder, findsOneWidget); + expect(snapFinder, findsOneWidget); + expect(crackleFinder, findsOneWidget); + TableRow fooRow = tester.widget(fooFinder); + TableRow barRow = tester.widget(barFinder); + final TableRow bazRow = tester.widget(bazFinder); + final TableRow quxRow = tester.widget(quxFinder); + final TableRow snapRow = tester.widget(snapFinder); + TableRow crackleRow = tester.widget(crackleFinder); + + expect(fooRow.backgroundColor!.toArgbString(), color1Value); + expect(barRow.backgroundColor!.toArgbString(), color2Value); + expect(bazRow.backgroundColor!.toArgbString(), color1Value); + expect(quxRow.backgroundColor!.toArgbString(), color2Value); + expect(snapRow.backgroundColor!.toArgbString(), color1Value); + expect(crackleRow.backgroundColor!.toArgbString(), color2Value); + + await tester.tap(barFinder); + await tester.pumpAndSettle(); + expect(fooFinder, findsOneWidget); + expect(barFinder, findsOneWidget); + expect(bazFinder, findsNothing); + expect(quxFinder, findsNothing); + expect(snapFinder, findsNothing); + expect(crackleFinder, findsOneWidget); + fooRow = tester.widget(fooFinder); + barRow = tester.widget(barFinder); + crackleRow = tester.widget(crackleFinder); + + expect(fooRow.backgroundColor!.toArgbString(), color1Value); + // [barRow] has the rowSelected color after being tapped. + expect(barRow.backgroundColor!.toArgbString(), rowSelectedColorValue); + // [crackleRow] has a different background color after collapsing previous + // row (Bar). + expect(crackleRow.backgroundColor!.toArgbString(), color1Value); + }, + ); test('fails when TreeColumn is not in column list', () { expect(() { diff --git a/packages/devtools_app/test/test_infra/goldens/memory/load_offline_data_trace_tab.png b/packages/devtools_app/test/test_infra/goldens/memory/load_offline_data_trace_tab.png index bacc8c8b52a..21eb64c9555 100644 Binary files a/packages/devtools_app/test/test_infra/goldens/memory/load_offline_data_trace_tab.png and b/packages/devtools_app/test/test_infra/goldens/memory/load_offline_data_trace_tab.png differ diff --git a/packages/devtools_app/test/test_infra/goldens/memory_diff_empty1.png b/packages/devtools_app/test/test_infra/goldens/memory_diff_empty1.png index 3952622201d..869ff8e2767 100644 Binary files a/packages/devtools_app/test/test_infra/goldens/memory_diff_empty1.png and b/packages/devtools_app/test/test_infra/goldens/memory_diff_empty1.png differ diff --git a/packages/devtools_app/test/test_infra/goldens/memory_diff_empty2.png b/packages/devtools_app/test/test_infra/goldens/memory_diff_empty2.png index 3952622201d..869ff8e2767 100644 Binary files a/packages/devtools_app/test/test_infra/goldens/memory_diff_empty2.png and b/packages/devtools_app/test/test_infra/goldens/memory_diff_empty2.png differ diff --git a/packages/devtools_app/test/test_infra/goldens/memory_diff_selected_class.png b/packages/devtools_app/test/test_infra/goldens/memory_diff_selected_class.png index 15e52122572..67d1d9f376f 100644 Binary files a/packages/devtools_app/test/test_infra/goldens/memory_diff_selected_class.png and b/packages/devtools_app/test/test_infra/goldens/memory_diff_selected_class.png differ diff --git a/packages/devtools_app/test/test_infra/goldens/memory_diff_snapshot_except_diff.png b/packages/devtools_app/test/test_infra/goldens/memory_diff_snapshot_except_diff.png index bac036ccb89..b34779c27de 100644 Binary files a/packages/devtools_app/test/test_infra/goldens/memory_diff_snapshot_except_diff.png and b/packages/devtools_app/test/test_infra/goldens/memory_diff_snapshot_except_diff.png differ diff --git a/packages/devtools_app/test/test_infra/goldens/memory_diff_snapshot_except_single.png b/packages/devtools_app/test/test_infra/goldens/memory_diff_snapshot_except_single.png index 282a6cb1197..cf36edd7e82 100644 Binary files a/packages/devtools_app/test/test_infra/goldens/memory_diff_snapshot_except_single.png and b/packages/devtools_app/test/test_infra/goldens/memory_diff_snapshot_except_single.png differ diff --git a/packages/devtools_app/test/test_infra/goldens/memory_diff_snapshot_scene_diff.png b/packages/devtools_app/test/test_infra/goldens/memory_diff_snapshot_scene_diff.png index bac036ccb89..b34779c27de 100644 Binary files a/packages/devtools_app/test/test_infra/goldens/memory_diff_snapshot_scene_diff.png and b/packages/devtools_app/test/test_infra/goldens/memory_diff_snapshot_scene_diff.png differ diff --git a/packages/devtools_app/test/test_infra/goldens/memory_diff_snapshot_scene_single.png b/packages/devtools_app/test/test_infra/goldens/memory_diff_snapshot_scene_single.png index 282a6cb1197..cf36edd7e82 100644 Binary files a/packages/devtools_app/test/test_infra/goldens/memory_diff_snapshot_scene_single.png and b/packages/devtools_app/test/test_infra/goldens/memory_diff_snapshot_scene_single.png differ diff --git a/packages/devtools_app/test/test_infra/goldens/memory_diff_snapshot_showAll_diff.png b/packages/devtools_app/test/test_infra/goldens/memory_diff_snapshot_showAll_diff.png index bac036ccb89..b34779c27de 100644 Binary files a/packages/devtools_app/test/test_infra/goldens/memory_diff_snapshot_showAll_diff.png and b/packages/devtools_app/test/test_infra/goldens/memory_diff_snapshot_showAll_diff.png differ diff --git a/packages/devtools_app/test/test_infra/goldens/memory_diff_snapshot_showAll_single.png b/packages/devtools_app/test/test_infra/goldens/memory_diff_snapshot_showAll_single.png index 282a6cb1197..cf36edd7e82 100644 Binary files a/packages/devtools_app/test/test_infra/goldens/memory_diff_snapshot_showAll_single.png and b/packages/devtools_app/test/test_infra/goldens/memory_diff_snapshot_showAll_single.png differ diff --git a/packages/devtools_app/test/test_infra/goldens/memory_diff_three_snapshots1.png b/packages/devtools_app/test/test_infra/goldens/memory_diff_three_snapshots1.png index 15e52122572..67d1d9f376f 100644 Binary files a/packages/devtools_app/test/test_infra/goldens/memory_diff_three_snapshots1.png and b/packages/devtools_app/test/test_infra/goldens/memory_diff_three_snapshots1.png differ diff --git a/packages/devtools_app/test/test_infra/goldens/memory_diff_three_snapshots2.png b/packages/devtools_app/test/test_infra/goldens/memory_diff_three_snapshots2.png index 15e52122572..67d1d9f376f 100644 Binary files a/packages/devtools_app/test/test_infra/goldens/memory_diff_three_snapshots2.png and b/packages/devtools_app/test/test_infra/goldens/memory_diff_three_snapshots2.png differ