Skip to content
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 @@ -6,8 +6,8 @@ import 'package:flutter/material.dart' hide Stack;
import 'package:provider/provider.dart';
import 'package:vm_service/vm_service.dart';

import '../../flutter/common_widgets.dart';
import '../../flutter/theme.dart';
import 'common.dart';
import 'debugger_controller.dart';
import 'debugger_model.dart';

Expand Down
8 changes: 0 additions & 8 deletions packages/devtools_app/lib/src/debugger/flutter/common.dart
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,3 @@ Widget createAnimatedCircleWidget(double radius, Color color) {
decoration: BoxDecoration(color: color, shape: BoxShape.circle),
);
}

extension DebuggerTextStyleExtension on ThemeData {
TextStyle get regularTextStyle => TextStyle(color: textTheme.bodyText2.color);

TextStyle get subtleTextStyle => TextStyle(color: unselectedWidgetColor);

TextStyle get selectedTextStyle => TextStyle(color: textSelectionColor);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
import 'package:flutter/material.dart' hide Stack;
import 'package:provider/provider.dart';

import '../../flutter/common_widgets.dart';
import '../../flutter/tree.dart';
import 'common.dart';
import 'debugger_controller.dart';
import 'debugger_model.dart';

Expand Down
13 changes: 13 additions & 0 deletions packages/devtools_app/lib/src/flutter/common_widgets.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ List<Widget> headerInColumn(TextTheme textTheme, String title) {
];
}

List<Widget> subHeaderInColumn(TextTheme textTheme, String title) {
return [
Text(title, style: textTheme.subtitle2),
const PaddedDivider(padding: EdgeInsets.only(bottom: denseRowSpacing)),
];
}

/// Convenience [Divider] with [Padding] that provides a good divider in forms.
class PaddedDivider extends StatelessWidget {
const PaddedDivider({
Expand Down Expand Up @@ -672,4 +679,10 @@ extension ColorExtension on Color {
extension ThemeDataExtension on ThemeData {
/// Returns whether we are currently using a dark theme.
bool get isDarkTheme => brightness == Brightness.dark;

TextStyle get regularTextStyle => TextStyle(color: textTheme.bodyText2.color);

TextStyle get subtleTextStyle => TextStyle(color: unselectedWidgetColor);

TextStyle get selectedTextStyle => TextStyle(color: textSelectionColor);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// found in the LICENSE file.
import 'dart:async';

import 'package:flutter/foundation.dart';
import 'package:pedantic/pedantic.dart';
import 'package:vm_service/vm_service.dart' as vm_service;

Expand All @@ -18,10 +19,10 @@ import '../../profiler/cpu_profile_transformer.dart';
import '../../profiler/profile_granularity.dart';
import '../../service_manager.dart';
import '../../trace_event.dart';
import '../../ui/fake_flutter/fake_flutter.dart';
import 'timeline_model.dart';
import 'timeline_processor.dart';
import 'timeline_screen.dart';
import 'timeline_streams.dart';

/// This class contains the business logic for [timeline_screen.dart].
///
Expand Down Expand Up @@ -108,18 +109,17 @@ class TimelineController
// TODO(kenz): switch to use VmFlagManager-like pattern once
// https://github.com/dart-lang/sdk/issues/41822 is fixed.
/// Recorded timeline stream values.
Map<String, ValueListenable<bool>> get recordedStreams => _recordedStreams;
final _recordedStreams = {
'Dart': ValueNotifier<bool>(false),
'Embedder': ValueNotifier<bool>(false),
'GC': ValueNotifier<bool>(false),
'API': ValueNotifier<bool>(false),
'Compiler': ValueNotifier<bool>(false),
'CompilerVerbose': ValueNotifier<bool>(false),
'Debugger': ValueNotifier<bool>(false),
'Isolate': ValueNotifier<bool>(false),
'VM': ValueNotifier<bool>(false),
};
final recordedStreams = [
dartTimelineStream,
embedderTimelineStream,
gcTimelineStream,
apiTimelineStream,
compilerTimelineStream,
compilerVerboseTimelineStream,
debuggerTimelineStream,
isolateTimelineStream,
vmTimelineStream,
];

/// Active timeline data.
///
Expand Down Expand Up @@ -154,7 +154,11 @@ class TimelineController
_cpuProfilerService.setProfilePeriod(mediumProfilePeriod),
logError: false,
));
await setTimelineStreams(['GC', 'Dart', 'Embedder']);
await setTimelineStreams([
dartTimelineStream,
embedderTimelineStream,
gcTimelineStream,
]);
await toggleHttpRequestLogging(true);
}

Expand Down Expand Up @@ -428,27 +432,28 @@ class TimelineController
_httpTimelineLoggingEnabledNotifier.value = state;
}

Future<void> setTimelineStreams(List<String> streams) async {
Future<void> setTimelineStreams(List<RecordedTimelineStream> streams) async {
for (final stream in streams) {
assert(_recordedStreams.containsKey(stream));
_recordedStreams[stream].value = true;
assert(recordedStreams.contains(stream));
stream.toggle(true);
}
await serviceManager.service.setVMTimelineFlags(streams);
await serviceManager.service
.setVMTimelineFlags(streams.map((s) => s.name).toList());
}

// TODO(kenz): this is not as robust as we'd like. Revisit once
// https://github.com/dart-lang/sdk/issues/41822 is addressed.
Future<void> toggleTimelineStream(String name, [bool value]) async {
value ??= !_recordedStreams[name].value;
Future<void> toggleTimelineStream(RecordedTimelineStream stream) async {
final newValue = !stream.enabled.value;
final timelineFlags =
(await serviceManager.service.getVMTimelineFlags()).recordedStreams;
if (timelineFlags.contains(name) && !value) {
timelineFlags.remove(name);
} else if (!timelineFlags.contains(name) && value) {
timelineFlags.add(name);
if (timelineFlags.contains(stream.name) && !newValue) {
timelineFlags.remove(stream.name);
} else if (!timelineFlags.contains(stream.name) && newValue) {
timelineFlags.add(stream.name);
}
await serviceManager.service.setVMTimelineFlags(timelineFlags);
_recordedStreams[name].value = value;
stream.toggle(newValue);
}

/// Clears the timeline data currently stored by the controller.
Expand Down
106 changes: 71 additions & 35 deletions packages/devtools_app/lib/src/timeline/flutter/timeline_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import 'dart:async';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

Expand Down Expand Up @@ -331,12 +332,13 @@ class TimelineScreenBodyState extends State<TimelineScreenBody>
class TimelineConfigurationsDialog extends StatelessWidget {
const TimelineConfigurationsDialog(this.controller);

static const dialogWidth = 300.0;
static const dialogWidth = 700.0;

final TimelineController controller;

@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(defaultDialogRadius)),
Expand All @@ -348,59 +350,93 @@ class TimelineConfigurationsDialog extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
...headerInColumn(Theme.of(context).textTheme, 'Recorded Streams'),
..._recordedStreams(),
...headerInColumn(theme.textTheme, 'Recorded Streams'),
..._defaultRecordedStreams(theme),
const SizedBox(height: denseSpacing),
...subHeaderInColumn(theme.textTheme, 'Advanced'),
..._advancedStreams(theme),
],
),
),
);
}

List<Widget> _recordedStreams() {
List<Widget> _defaultRecordedStreams(ThemeData theme) {
return [
..._timelineStreams(theme, advanced: false),
// Special case "Network Traffic" because it is not implemented as a
// Timeline recorded stream in the VM. The user does not need to be aware of
// the distinction, however.
_buildStream(
name: 'Network',
description: ' • Http traffic',
listenable: controller.httpTimelineLoggingEnabled,
onChanged: controller.toggleHttpRequestLogging,
theme: theme,
),
];
}

List<Widget> _advancedStreams(ThemeData theme) {
return _timelineStreams(theme, advanced: true);
}

List<Widget> _timelineStreams(
ThemeData theme, {
@required bool advanced,
}) {
final settings = <Widget>[];
for (final streamName in controller.recordedStreams.keys) {
// TODO(kenz): add subtly styles description for each stream.
final title = Text(streamName);
final checkbox = ValueListenableBuilder(
valueListenable: controller.recordedStreams[streamName],
builder: (context, enabled, _) {
return Checkbox(
value: enabled,
onChanged: (_) => controller.toggleTimelineStream(streamName),
);
},
);
settings.add(
Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
children: [
checkbox,
title,
],
),
);
final streams = controller.recordedStreams
.where((s) => s.advanced == advanced)
.toList();
for (final stream in streams) {
settings.add(_buildStream(
name: stream.name,
description: ' • ${stream.description}',
listenable: stream.enabled,
onChanged: (_) => controller.toggleTimelineStream(stream),
theme: theme,
));
}
return settings;
}

// Special case "Network Traffic" because it is not implemented as a
// Timeline recorded stream in the VM. The user does not need to be aware of
// the distinction, however.
settings.add(Row(
Widget _buildStream({
@required String name,
@required String description,
@required ValueListenable listenable,
@required void Function(bool) onChanged,
@required ThemeData theme,
}) {
return Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
children: [
ValueListenableBuilder(
valueListenable: controller.httpTimelineLoggingEnabled,
valueListenable: listenable,
builder: (context, value, _) {
return Checkbox(
value: value,
onChanged: controller.toggleHttpRequestLogging,
onChanged: onChanged,
);
},
),
const Text('Network Traffic'),
Flexible(
child: RichText(
overflow: TextOverflow.visible,
text: TextSpan(
text: name,
style: theme.regularTextStyle,
children: [
TextSpan(
text: description,
style: theme.subtleTextStyle,
),
],
),
),
),
],
));
return settings;
);
}
}
Loading