Make timeline flame chart section labels sticky (#2079)
diff --git a/packages/devtools_app/lib/src/charts/flame_chart.dart b/packages/devtools_app/lib/src/charts/flame_chart.dart index 2c2c3f5..44657f0 100644 --- a/packages/devtools_app/lib/src/charts/flame_chart.dart +++ b/packages/devtools_app/lib/src/charts/flame_chart.dart
@@ -501,9 +501,13 @@ child: GestureDetector( behavior: HitTestBehavior.opaque, onTapUp: _handleTapUp, - child: SizedBox( + child: Container( height: rowHeightWithPadding, width: widget.width, + color: alternatingColorForIndexWithContext( + nodes.first.sectionIndex, + context, + ), // TODO(kenz): investigate if `addAutomaticKeepAlives: false` and // `addRepaintBoundaries: false` are needed here for perf improvement. child: ExtentDelegateListView( @@ -756,19 +760,6 @@ this.sectionIndex, }); - FlameChartNode.sectionLabel({ - this.key, - @required this.text, - @required this.textColor, - @required this.backgroundColor, - @required double top, - @required double width, - }) : rect = Rect.fromLTRB(rowPadding, top, width, top + rowHeight), - tooltip = text, - data = null, - onSelected = ((_) {}), - selectable = false; - static const _selectedTextColor = Colors.black; // We would like this value to be smaller, but zoom performance does not allow // for that. We should decrease this value if we can improve flame chart zoom
diff --git a/packages/devtools_app/lib/src/common_widgets.dart b/packages/devtools_app/lib/src/common_widgets.dart index d0620dd..64c1411 100644 --- a/packages/devtools_app/lib/src/common_widgets.dart +++ b/packages/devtools_app/lib/src/common_widgets.dart
@@ -767,3 +767,23 @@ TextStyle get selectedTextStyle => TextStyle(color: textSelectionColor); } + +/// Gets an alternating color to use for indexed UI elements. +Color alternatingColorForIndexWithContext(int index, BuildContext context) { + final theme = Theme.of(context); + final color = theme.canvasColor; + return _colorForIndex(color, index, isDarkTheme: theme.isDarkTheme); +} + +Color alternatingColorForIndex(int index, {@required bool isDarkTheme}) { + final color = defaultBackgroundColor; + return _colorForIndex(color, index, isDarkTheme: isDarkTheme); +} + +Color _colorForIndex(Color color, int index, {@required bool isDarkTheme}) { + if (index % 2 == 1) { + return color; + } else { + return isDarkTheme ? color.brighten() : color.darken(); + } +}
diff --git a/packages/devtools_app/lib/src/profiler/cpu_profile_flame_chart.dart b/packages/devtools_app/lib/src/profiler/cpu_profile_flame_chart.dart index b214aa6..c841db3 100644 --- a/packages/devtools_app/lib/src/profiler/cpu_profile_flame_chart.dart +++ b/packages/devtools_app/lib/src/profiler/cpu_profile_flame_chart.dart
@@ -62,7 +62,7 @@ textColor: Colors.black, data: stackFrame, onSelected: (dynamic frame) => widget.onSelected(frame), - ); + )..sectionIndex = 0; rows[row].addNode(node);
diff --git a/packages/devtools_app/lib/src/split.dart b/packages/devtools_app/lib/src/split.dart index 4dc40fd..ef14dd8 100644 --- a/packages/devtools_app/lib/src/split.dart +++ b/packages/devtools_app/lib/src/split.dart
@@ -290,9 +290,7 @@ } class DefaultSplitter extends StatelessWidget { - const DefaultSplitter({ - @required this.isHorizontal, - }); + const DefaultSplitter({@required this.isHorizontal}); static const double size = 24.0;
diff --git a/packages/devtools_app/lib/src/table.dart b/packages/devtools_app/lib/src/table.dart index da78179..135613e 100644 --- a/packages/devtools_app/lib/src/table.dart +++ b/packages/devtools_app/lib/src/table.dart
@@ -6,7 +6,7 @@ import 'auto_dispose_mixin.dart'; import 'collapsible_mixin.dart'; import 'common_widgets.dart'; -import 'common_widgets.dart' show ColorExtension, ScrollControllerAutoScroll; +import 'common_widgets.dart' show ScrollControllerAutoScroll; import 'flutter_widgets/linked_scroll_controller.dart'; import 'table_data.dart'; import 'theme.dart'; @@ -153,7 +153,7 @@ onPressed: widget.onItemSelected, columns: widget.columns, columnWidths: columnWidths, - backgroundColor: TableRow.colorFor(context, index), + backgroundColor: alternatingColorForIndexWithContext(index, context), ); } @@ -445,7 +445,7 @@ onPressed: (item) => _onItemPressed(item, index), backgroundColor: isNodeSelected ? Theme.of(context).selectedRowColor - : TableRow.colorFor(context, index), + : alternatingColorForIndexWithContext(index, context), columns: widget.columns, columnWidths: columnWidths, expandableColumn: widget.treeColumn, @@ -916,18 +916,6 @@ final Function(ColumnData<T> column, SortDirection direction) onSortChanged; - /// Gets a color to use for this row at a given index. - static Color colorFor(BuildContext context, int index) { - final theme = Theme.of(context); - final color = theme.canvasColor; - - if (index % 2 == 1) { - return color; - } else { - return theme.isDarkTheme ? color.brighten() : color.darken(); - } - } - @override _TableRowState<T> createState() => _TableRowState<T>(); }
diff --git a/packages/devtools_app/lib/src/timeline/timeline_flame_chart.dart b/packages/devtools_app/lib/src/timeline/timeline_flame_chart.dart index fe3ea22..53bd8bc 100644 --- a/packages/devtools_app/lib/src/timeline/timeline_flame_chart.dart +++ b/packages/devtools_app/lib/src/timeline/timeline_flame_chart.dart
@@ -1,6 +1,7 @@ // Copyright 2019 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 'dart:collection'; import 'dart:math' as math; import 'dart:ui'; @@ -10,6 +11,7 @@ import 'package:provider/provider.dart'; import '../charts/flame_chart.dart'; +import '../common_widgets.dart'; import '../geometry.dart'; import '../theme.dart'; import '../ui/colors.dart'; @@ -18,6 +20,11 @@ import 'timeline_controller.dart'; import 'timeline_model.dart'; +// TODO(kenz): for sections with more than one row deep, add empty row for +// section label. + +// TODO(kenz): make flame chart sections collapsible. + class TimelineFlameChart extends FlameChart<TimelineData, TimelineEvent> { TimelineFlameChart( TimelineData data, { @@ -34,15 +41,18 @@ ); static double _calculateStartInset(TimelineData data) { - // TODO(kenz): we need to calculate start inset based on the width of the - // section labels. We should also set a max, ellipsize, and rely on tooltip - // to give the full name in the event that the section name exceeds max. - // - // Alternatively, we could make the label section a column of it's own that - // is resizeable. It would need to link scroll controllers with the list - // view holding the flame chart nodes. This would make section labels sticky - // to the left as an inherent bonus. - return 140.0; + const spaceFor0msText = 55.0; + const maxStartInset = 300.0; + var maxMeasuredWidth = 0.0; + for (String groupName in data.eventGroups.keys) { + final textPainter = TextPainter( + text: TextSpan(text: groupName), + textDirection: TextDirection.ltr, + )..layout(); + maxMeasuredWidth = + math.max(maxMeasuredWidth, textPainter.width + 2 * densePadding); + } + return math.min(maxStartInset, maxMeasuredWidth) + spaceFor0msText; } /// Offset for drawing async guidelines. @@ -232,32 +242,6 @@ ); sections.add(section); - // Add section label node. - Color sectionLabelBackgroundColor; - switch (groupName) { - case TimelineData.uiKey: - sectionLabelBackgroundColor = mainUiColor; - break; - case TimelineData.rasterKey: - sectionLabelBackgroundColor = mainRasterColor; - break; - case TimelineData.unknownKey: - sectionLabelBackgroundColor = mainUnknownColor; - break; - default: - sectionLabelBackgroundColor = mainAsyncColor; - } - - final currentSectionLabel = FlameChartNode.sectionLabel( - text: groupName, - textColor: Colors.black, - backgroundColor: sectionLabelBackgroundColor, - top: flameChartNodeTop, - width: 120.0, - ); - - rows[currentRowIndex].addNode(currentSectionLabel, index: 0); - // Increment for next section. currentRowIndex += group.displaySize; currentSectionIndex++; @@ -316,6 +300,17 @@ _calculateVerticalGuidelineStartY(event) - rowHeight, ), ), + CustomPaint( + painter: SectionLabelPainter( + _timelineController.data.eventGroups, + isDarkTheme: Theme.of(context).isDarkTheme, + zoom: zoom, + constraints: constraints, + verticalScrollOffset: verticalScrollOffset, + horizontalScrollOffset: horizontalScrollOffset, + chartStartInset: widget.startInset, + ), + ), ]; } @@ -452,6 +447,88 @@ } } +class SectionLabelPainter extends FlameChartPainter { + SectionLabelPainter( + this.eventGroups, { + @required this.isDarkTheme, + @required double zoom, + @required BoxConstraints constraints, + @required double verticalScrollOffset, + @required double horizontalScrollOffset, + @required double chartStartInset, + }) : super( + zoom: zoom, + constraints: constraints, + verticalScrollOffset: verticalScrollOffset, + horizontalScrollOffset: horizontalScrollOffset, + chartStartInset: chartStartInset, + ); + + final bool isDarkTheme; + + final SplayTreeMap<String, TimelineEventGroup> eventGroups; + + @override + void paint(Canvas canvas, Size size) { + canvas.clipRect(Rect.fromLTWH( + 0.0, + rowHeight, // We do not want to paint inside the timestamp section. + constraints.maxWidth, + constraints.maxHeight - rowHeight, + )); + + // Start at row height to account for timestamps at top of chart. + var startSectionPx = sectionSpacing * 2; + for (String groupName in eventGroups.keys) { + final group = eventGroups[groupName]; + final labelTop = startSectionPx - verticalScrollOffset; + + final textPainter = TextPainter( + text: TextSpan( + text: groupName, + style: const TextStyle(color: chartTextColor), + ), + textDirection: TextDirection.ltr, + )..layout(); + + final labelWidth = textPainter.width + 2 * densePadding; + final backgroundColor = alternatingColorForIndex( + eventGroups.values.toList().indexOf(group), + isDarkTheme: isDarkTheme, + ); + final backgroundWithAlpha = Color.fromRGBO( + backgroundColor.red, + backgroundColor.green, + backgroundColor.blue, + 0.75, + ); + + canvas.drawRect( + Rect.fromLTWH( + 0.0, + labelTop, + labelWidth, + rowHeightWithPadding, + ), + Paint()..color = backgroundWithAlpha, + ); + + textPainter.paint( + canvas, + Offset(densePadding, labelTop + rowPadding), + ); + + startSectionPx += group.displaySizePx; + } + } + + @override + bool shouldRepaint(SectionLabelPainter oldDelegate) { + return verticalScrollOffset != oldDelegate.verticalScrollOffset || + eventGroups != oldDelegate.eventGroups; + } +} + class AsyncGuidelinePainter extends FlameChartPainter { AsyncGuidelinePainter({ @required double zoom, @@ -596,10 +673,6 @@ static const baseGridIntervalPx = 150.0; static const timestampOffset = 6.0; - static const timestampColor = ThemedColor( - Color(0xFF24292E), - Color(0xFFFAFBFC), - ); final double chartEndInset; @@ -655,7 +728,7 @@ final textPainter = TextPainter( text: TextSpan( text: timestampText, - style: const TextStyle(color: timestampColor), + style: const TextStyle(color: chartTextColor), ), textAlign: TextAlign.right, textDirection: TextDirection.ltr,
diff --git a/packages/devtools_app/test/flame_chart_test.dart b/packages/devtools_app/test/flame_chart_test.dart index 81c1ff3..0839999 100644 --- a/packages/devtools_app/test/flame_chart_test.dart +++ b/packages/devtools_app/test/flame_chart_test.dart
@@ -151,15 +151,15 @@ final linkedScrollControllerGroup = LinkedScrollControllerGroup(); final testRow = ScrollingFlameChartRow( linkedScrollControllerGroup: linkedScrollControllerGroup, - nodes: testNodesWithLabel, - width: 610.0, // 610.0 fits all test nodes and sideInsets of 70.0. + nodes: testNodes, + width: 680.0, // 680.0 fits all test nodes and sideInsets of 70.0. startInset: sideInset, selected: null, zoom: FlameChart.minZoomLevel, ); final zoomedTestRow = ScrollingFlameChartRow( linkedScrollControllerGroup: linkedScrollControllerGroup, - nodes: testNodesWithLabel, + nodes: testNodes, // 1080.0 fits all test nodes at zoom level 2.0 and sideInsets of 70.0. width: 1080.0, startInset: sideInset, @@ -200,9 +200,8 @@ expect(find.byWidget(currentRow), findsOneWidget); expect(find.byType(MouseRegion), findsOneWidget); - final sizedBoxFinder = find.byType(SizedBox); - final SizedBox box = tester.firstWidget(sizedBoxFinder); - expect(box.height, equals(rowHeightWithPadding)); + // 1 for row container and 4 for node containers. + expect(tester.widgetList(find.byType(Container)).length, equals(5)); }); testWidgets('builds for empty nodes list', (WidgetTester tester) async { @@ -228,7 +227,6 @@ (WidgetTester tester) async { final rowState = await pumpRowAndGetState(tester); expect(rowState.binarySearchForNode(-10.0), isNull); - expect(rowState.binarySearchForNode(10.0), equals(labelNode)); expect(rowState.binarySearchForNode(49.0), isNull); expect(rowState.binarySearchForNode(70.0), equals(testNode)); expect(rowState.binarySearchForNode(120.0), equals(testNode2)); @@ -241,7 +239,6 @@ (WidgetTester tester) async { final rowState = await pumpRowAndGetState(tester, row: zoomedTestRow); expect(rowState.binarySearchForNode(-10.0), isNull); - expect(rowState.binarySearchForNode(10.0), equals(labelNode)); expect(rowState.binarySearchForNode(49.0), isNull); expect(rowState.binarySearchForNode(70.0), equals(testNode)); expect(rowState.binarySearchForNode(130.0), equals(testNode)); @@ -417,26 +414,9 @@ }); group('NodeListExtension', () { - test( - 'toPaddedZoomedIntervals calculation is accurate for unzoomed row with' - ' label', () { - final paddedZoomedIntervals = testNodesWithLabel.toPaddedZoomedIntervals( - zoom: 1.0, - chartStartInset: sideInset, - chartWidth: 610.0, - ); - expect(paddedZoomedIntervals[0], equals(const Range(0.0, 70.0))); - expect(paddedZoomedIntervals[1], equals(const Range(70.0, 120.0))); - expect(paddedZoomedIntervals[2], equals(const Range(120.0, 180.0))); - expect(paddedZoomedIntervals[3], equals(const Range(180.0, 240.0))); - expect(paddedZoomedIntervals[4], equals(const Range(240.0, 1000540.0))); - }); - - test( - 'toPaddedZoomedIntervals calculation is accurate for unzoomed row ' - 'without label', () { - final paddedZoomedIntervals = - testNodesWithoutLabel.toPaddedZoomedIntervals( + test('toPaddedZoomedIntervals calculation is accurate for unzoomed row', + () { + final paddedZoomedIntervals = testNodes.toPaddedZoomedIntervals( zoom: 1.0, chartStartInset: sideInset, chartWidth: 610.0, @@ -447,26 +427,8 @@ expect(paddedZoomedIntervals[3], equals(const Range(240.0, 1000540.0))); }); - test( - 'toPaddedZoomedIntervals calculation is accurate for zoomed row with ' - 'label', () { - final paddedZoomedIntervals = testNodesWithLabel.toPaddedZoomedIntervals( - zoom: 2.0, - chartStartInset: sideInset, - chartWidth: 1080.0, - ); - expect(paddedZoomedIntervals[0], equals(const Range(0.0, 70.0))); - expect(paddedZoomedIntervals[1], equals(const Range(70.0, 170.0))); - expect(paddedZoomedIntervals[2], equals(const Range(170.0, 290.0))); - expect(paddedZoomedIntervals[3], equals(const Range(290.0, 410.0))); - expect(paddedZoomedIntervals[4], equals(const Range(410.0, 1001010.0))); - }); - - test( - 'toPaddedZoomedIntervals calculation is accurate for zoomed row without' - ' label', () { - final paddedZoomedIntervals = - testNodesWithoutLabel.toPaddedZoomedIntervals( + test('toPaddedZoomedIntervals calculation is accurate for zoomed row', () { + final paddedZoomedIntervals = testNodes.toPaddedZoomedIntervals( zoom: 2.0, chartStartInset: sideInset, chartWidth: 1080.0, @@ -480,137 +442,82 @@ group('FlameChartUtils', () { test('leftPaddingForNode returns correct value for un-zoomed row', () { - // Row with label. expect( - FlameChartUtils.leftPaddingForNode(0, testNodesWithLabel, - chartZoom: 1.0, chartStartInset: sideInset), - equals(2.0)); - expect( - FlameChartUtils.leftPaddingForNode(1, testNodesWithLabel, - chartZoom: 1.0, chartStartInset: sideInset), - equals(0.0)); - expect( - FlameChartUtils.leftPaddingForNode(2, testNodesWithLabel, - chartZoom: 1.0, chartStartInset: sideInset), - equals(0.0)); - expect( - FlameChartUtils.leftPaddingForNode(3, testNodesWithLabel, - chartZoom: 1.0, chartStartInset: sideInset), - equals(0.0)); - expect( - FlameChartUtils.leftPaddingForNode(4, testNodesWithLabel, - chartZoom: 1.0, chartStartInset: sideInset), - equals(0.0)); - - // Row without label. - expect( - FlameChartUtils.leftPaddingForNode(0, testNodesWithoutLabel, + FlameChartUtils.leftPaddingForNode(0, testNodes, chartZoom: 1.0, chartStartInset: sideInset), equals(70.0)); expect( - FlameChartUtils.leftPaddingForNode(1, testNodesWithoutLabel, + FlameChartUtils.leftPaddingForNode(1, testNodes, chartZoom: 1.0, chartStartInset: sideInset), equals(0.0)); expect( - FlameChartUtils.leftPaddingForNode(2, testNodesWithoutLabel, + FlameChartUtils.leftPaddingForNode(2, testNodes, chartZoom: 1.0, chartStartInset: sideInset), equals(0.0)); expect( - FlameChartUtils.leftPaddingForNode(3, testNodesWithoutLabel, + FlameChartUtils.leftPaddingForNode(3, testNodes, chartZoom: 1.0, chartStartInset: sideInset), equals(0.0)); }); test('rightPaddingForNode returns correct value for un-zoomed row', () { - // Row with label. expect( - FlameChartUtils.rightPaddingForNode(0, testNodesWithLabel, - chartZoom: 1.0, chartStartInset: sideInset, chartWidth: 610.0), - equals(48.0)); - expect( - FlameChartUtils.rightPaddingForNode(1, testNodesWithLabel, + FlameChartUtils.rightPaddingForNode(0, testNodes, chartZoom: 1.0, chartStartInset: sideInset, chartWidth: 610.0), equals(20.0)); expect( - FlameChartUtils.rightPaddingForNode(2, testNodesWithLabel, + FlameChartUtils.rightPaddingForNode(1, testNodes, chartZoom: 1.0, chartStartInset: sideInset, chartWidth: 610.0), equals(10.0)); expect( - FlameChartUtils.rightPaddingForNode(3, testNodesWithLabel, + FlameChartUtils.rightPaddingForNode(2, testNodes, chartZoom: 1.0, chartStartInset: sideInset, chartWidth: 610.0), equals(10.0)); expect( - FlameChartUtils.rightPaddingForNode(4, testNodesWithLabel, - chartZoom: 1.0, chartStartInset: sideInset, chartWidth: 610.0), - equals(1000000.0)); - - // Row without label. - expect( - FlameChartUtils.rightPaddingForNode(0, testNodesWithoutLabel, - chartZoom: 1.0, chartStartInset: sideInset, chartWidth: 610.0), - equals(20.0)); - expect( - FlameChartUtils.rightPaddingForNode(1, testNodesWithoutLabel, - chartZoom: 1.0, chartStartInset: sideInset, chartWidth: 610.0), - equals(10.0)); - expect( - FlameChartUtils.rightPaddingForNode(2, testNodesWithoutLabel, - chartZoom: 1.0, chartStartInset: sideInset, chartWidth: 610.0), - equals(10.0)); - expect( - FlameChartUtils.rightPaddingForNode(3, testNodesWithoutLabel, + FlameChartUtils.rightPaddingForNode(3, testNodes, chartZoom: 1.0, chartStartInset: sideInset, chartWidth: 610.0), equals(1000000.0)); }); test('leftPaddingForNode returns correct value for zoomed row', () { expect( - FlameChartUtils.leftPaddingForNode(0, testNodesWithLabel, + FlameChartUtils.leftPaddingForNode(0, testNodes, chartZoom: 2.0, chartStartInset: sideInset), - equals(2.0)); + equals(70.0)); expect( - FlameChartUtils.leftPaddingForNode(1, testNodesWithLabel, + FlameChartUtils.leftPaddingForNode(1, testNodes, chartZoom: 2.0, chartStartInset: sideInset), equals(0.0)); expect( - FlameChartUtils.leftPaddingForNode(2, testNodesWithLabel, + FlameChartUtils.leftPaddingForNode(2, testNodes, chartZoom: 2.0, chartStartInset: sideInset), equals(0.0)); expect( - FlameChartUtils.leftPaddingForNode(3, testNodesWithLabel, - chartZoom: 2.0, chartStartInset: sideInset), - equals(0.0)); - expect( - FlameChartUtils.leftPaddingForNode(4, testNodesWithLabel, + FlameChartUtils.leftPaddingForNode(3, testNodes, chartZoom: 2.0, chartStartInset: sideInset), equals(0.0)); }); test('rightPaddingForNode returns correct value for zoomed row', () { expect( - FlameChartUtils.rightPaddingForNode(0, testNodesWithLabel, - chartZoom: 2.0, chartStartInset: sideInset, chartWidth: 1080.0), - equals(48.0)); - expect( - FlameChartUtils.rightPaddingForNode(1, testNodesWithLabel, + FlameChartUtils.rightPaddingForNode(0, testNodes, chartZoom: 2.0, chartStartInset: sideInset, chartWidth: 1080.0), equals(40.0)); expect( - FlameChartUtils.rightPaddingForNode(2, testNodesWithLabel, + FlameChartUtils.rightPaddingForNode(1, testNodes, chartZoom: 2.0, chartStartInset: sideInset, chartWidth: 1080.0), equals(20.0)); expect( - FlameChartUtils.rightPaddingForNode(3, testNodesWithLabel, + FlameChartUtils.rightPaddingForNode(2, testNodes, chartZoom: 2.0, chartStartInset: sideInset, chartWidth: 1080.0), equals(20.0)); expect( - FlameChartUtils.rightPaddingForNode(4, testNodesWithLabel, + FlameChartUtils.rightPaddingForNode(3, testNodes, chartZoom: 2.0, chartStartInset: sideInset, chartWidth: 1080.0), equals(1000000.0)); }); test('zoomForNode returns correct values', () { - expect(FlameChartUtils.zoomForNode(labelNode, 2.0), equals(1.0)); expect(FlameChartUtils.zoomForNode(testNode, 3.0), equals(3.0)); expect(FlameChartUtils.zoomForNode(testNode4, 10.0), equals(10.0)); }); @@ -627,21 +534,7 @@ textColor: Colors.white, data: goldenAsyncTimelineEvent, onSelected: (_) {}, -); - -const labelNodeKey = Key('label node'); -final labelNode = FlameChartNode<TimelineEvent>( - key: labelNodeKey, - text: 'label test node', - tooltip: 'label test node', - // 30.0 is the minimum node width for text. - rect: const Rect.fromLTWH(2.0, 0.0, 20.0, rowHeight), - backgroundColor: Colors.blue, - textColor: Colors.white, - data: goldenAsyncTimelineEvent, - selectable: false, - onSelected: (_) {}, -); +)..sectionIndex = 0; const Key testNodeKey = Key('test node'); final testNode = FlameChartNode<TimelineEvent>( @@ -654,7 +547,7 @@ textColor: Colors.white, data: goldenAsyncTimelineEvent, onSelected: (_) {}, -); +)..sectionIndex = 0; final testNode2 = FlameChartNode<TimelineEvent>( key: narrowNodeKey, @@ -665,7 +558,7 @@ textColor: Colors.white, data: goldenAsyncTimelineEvent, onSelected: (_) {}, -); +)..sectionIndex = 0; final testNode3 = FlameChartNode<TimelineEvent>( key: narrowNodeKey, @@ -676,7 +569,7 @@ textColor: Colors.white, data: goldenAsyncTimelineEvent, onSelected: (_) {}, -); +)..sectionIndex = 0; final testNode4 = FlameChartNode<TimelineEvent>( key: narrowNodeKey, @@ -687,17 +580,9 @@ textColor: Colors.white, data: goldenAsyncTimelineEvent, onSelected: (_) {}, -); +)..sectionIndex = 0; -final testNodesWithLabel = [ - labelNode, - testNode, - testNode2, - testNode3, - testNode4, -]; - -final testNodesWithoutLabel = [ +final testNodes = [ testNode, testNode2, testNode3, @@ -714,4 +599,4 @@ textColor: Colors.white, data: goldenAsyncTimelineEvent, onSelected: (_) {}, -); +)..sectionIndex = 0;
diff --git a/packages/devtools_app/test/goldens/timeline_flame_chart_with_selected_frame.png b/packages/devtools_app/test/goldens/timeline_flame_chart_with_selected_frame.png index 5bcb780..3258b3c 100644 --- a/packages/devtools_app/test/goldens/timeline_flame_chart_with_selected_frame.png +++ b/packages/devtools_app/test/goldens/timeline_flame_chart_with_selected_frame.png Binary files differ
diff --git a/packages/devtools_app/test/timeline_flame_chart_test.dart b/packages/devtools_app/test/timeline_flame_chart_test.dart index c8bb185..4052631 100644 --- a/packages/devtools_app/test/timeline_flame_chart_test.dart +++ b/packages/devtools_app/test/timeline_flame_chart_test.dart
@@ -2,12 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:io'; + import 'package:devtools_app/src/globals.dart'; import 'package:devtools_app/src/service_manager.dart'; import 'package:devtools_app/src/timeline/timeline_flame_chart.dart'; import 'package:devtools_app/src/timeline/timeline_screen.dart'; import 'package:devtools_app/src/timeline/timeline_controller.dart'; import 'package:devtools_testing/support/timeline_test_data.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; @@ -18,7 +21,7 @@ void main() { FakeServiceManager fakeServiceManager; - group('TimelineFlameChart', () { + group('TimelineFlameChartContent', () { void _setupForTimeline(Map<String, dynamic> timelineJson) { fakeServiceManager = FakeServiceManager( useFakeService: true, @@ -82,10 +85,13 @@ await tester.pumpAndSettle(); expect(find.byType(TimelineFlameChart), findsOneWidget); - expect( + await expectLater( find.byType(TimelineFlameChart), matchesGoldenFile( 'goldens/timeline_flame_chart_with_selected_frame.png')); - }); + + // Await delay for golden comparison. + await tester.pumpAndSettle(const Duration(seconds: 2)); + }, skip: kIsWeb || !Platform.isMacOS); }); }