Skip to content

Commit

Permalink
Add support for static tooling in DevTools and add a "Home" screen (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
kenzieschmoll committed Jul 11, 2023
1 parent 25f9a23 commit be05370
Show file tree
Hide file tree
Showing 37 changed files with 501 additions and 589 deletions.
Expand Up @@ -8,14 +8,11 @@

// ignore_for_file: avoid_print

import 'package:devtools_app/devtools_app.dart';
import 'package:devtools_app/src/screens/memory/panes/control/primary_controls.dart';
import 'package:devtools_app/src/screens/memory/panes/diff/widgets/snapshot_list.dart';
import 'package:devtools_app/src/screens/memory/shared/primitives/instance_context_menu.dart';
import 'package:devtools_app/src/shared/banner_messages.dart';
import 'package:devtools_app/src/shared/common_widgets.dart';
import 'package:devtools_app/src/shared/console/widgets/console_pane.dart';
import 'package:devtools_app/src/shared/primitives/simple_items.dart';
import 'package:devtools_app/src/shared/ui/search.dart';
import 'package:devtools_test/devtools_integration_test.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
Expand Down Expand Up @@ -43,12 +40,11 @@ void main() {
await pumpAndConnectDevTools(tester, testApp);

final evalTester = _EvalAndBrowseTester(tester);
await evalTester.prepareMemoryUI();

await _testBasicEval(evalTester);
await _testAssignment(evalTester);

await evalTester.switchToMemoryAndIncreaseEval();

await _profileOneInstance(evalTester);
await _profileAllInstances(evalTester);

Expand Down Expand Up @@ -154,7 +150,9 @@ class _EvalAndBrowseTester {
await tester.pump(longPumpDuration);
}

Future<void> switchToMemoryAndIncreaseEval() async {
/// Prepares the UI of the memory screen so that the eval-related elements are
/// visible on the screen for testing.
Future<void> prepareMemoryUI() async {
// Open memory screen.
await switchToScreen(tester, ScreenMetaData.memory);

Expand Down Expand Up @@ -208,7 +206,7 @@ class _EvalAndBrowseTester {
Finder? next,
}) async {
Future<void> action(int tryNumber) async {
logStatus('tapping #$tryNumber to find \n[$finder]\n');
logStatus('attempt #$tryNumber, tapping \n[$finder]\n');
tryNumber++;
await tester.tap(finder);
await tester.pump(duration);
Expand Down
Expand Up @@ -100,6 +100,11 @@ void main() {
await pumpAndConnectDevTools(tester, testApp);
await tester.pump(longDuration);

// TODO(kenz): re-work this integration test so that we do not have to be
// on the inspector screen for this to pass.
await switchToScreen(tester, ScreenMetaData.inspector);
await tester.pump(longDuration);

// Ensure all futures are completed before running checks.
await serviceManager.service!.allFuturesCompleted;

Expand Down
3 changes: 1 addition & 2 deletions packages/devtools_app/lib/devtools_app.dart
Expand Up @@ -6,7 +6,7 @@ export 'src/app.dart';
export 'src/extension_points/extensions_base.dart';
export 'src/extension_points/extensions_external.dart';
export 'src/framework/app_bar.dart';
export 'src/framework/landing_screen.dart';
export 'src/framework/home_screen.dart';
export 'src/framework/notifications_view.dart';
export 'src/framework/release_notes/release_notes.dart';
export 'src/framework/status_line.dart';
Expand Down Expand Up @@ -75,7 +75,6 @@ export 'src/shared/console/eval/eval_service.dart';
export 'src/shared/console/eval/inspector_tree.dart';
export 'src/shared/console/primitives/simple_items.dart';
export 'src/shared/console/widgets/description.dart';
export 'src/shared/device_dialog.dart';
export 'src/shared/diagnostics/diagnostics_node.dart';
export 'src/shared/diagnostics/inspector_service.dart';
export 'src/shared/error_badge_manager.dart';
Expand Down
2 changes: 1 addition & 1 deletion packages/devtools_app/lib/initialization.dart
Expand Up @@ -35,7 +35,7 @@ void runDevTools({
List<DevToolsScreen>? screens,
}) {
setupErrorHandling(() async {
screens ??= defaultScreens;
screens ??= defaultScreens(sampleData: sampleData);

initDevToolsLogging();

Expand Down
94 changes: 46 additions & 48 deletions packages/devtools_app/lib/src/app.dart
Expand Up @@ -10,8 +10,8 @@ import 'package:provider/provider.dart';

import 'example/conditional_screen.dart';
import 'framework/framework_core.dart';
import 'framework/home_screen.dart';
import 'framework/initializer.dart';
import 'framework/landing_screen.dart';
import 'framework/notifications_view.dart';
import 'framework/release_notes/release_notes.dart';
import 'framework/scaffold.dart';
Expand Down Expand Up @@ -201,65 +201,57 @@ class DevToolsAppState extends State<DevToolsApp> with AutoDisposeMixin {
) {
final vmServiceUri = params['uri'];
final embed = isEmbedded(params);

// Always return the landing screen if there's no VM service URI.
if (vmServiceUri?.isEmpty ?? true) {
return DevToolsScaffold.withChild(
key: const Key('landing'),
embed: embed,
child: LandingScreenBody(sampleData: widget.sampleData),
);
}
final hide = {...?params['hide']?.split(',')};

// TODO(dantup): We should be able simplify this a little, removing params['page']
// and only supporting /inspector (etc.) instead of also &page=inspector if
// all IDEs switch over to those URLs.
if (page?.isEmpty ?? true) {
page = params['page'];
}
final hide = {...?params['hide']?.split(',')};
return Initializer(
url: vmServiceUri,
allowConnectionScreenOnDisconnect: !embed,
builder: (_) {
// Force regeneration of visible screens when VM developer mode is
// enabled.
return ValueListenableBuilder<bool>(
valueListenable: preferences.vmDeveloperModeEnabled,
builder: (_, __, child) {
final screens = _visibleScreens()
.where((p) => embed && page != null ? p.screenId == page : true)
.where((p) => !hide.contains(p.screenId))
.toList();
if (screens.isEmpty) return child ?? const SizedBox.shrink();
return MultiProvider(
providers: _providedControllers(),
child: DevToolsScaffold(
embed: embed,
page: page,
screens: screens,
actions: [

final screens = _visibleScreens()
.where((p) => embed && page != null ? p.screenId == page : true)
.where((p) => !hide.contains(p.screenId))
.toList();

final connectedToVmService =
vmServiceUri != null && vmServiceUri.isNotEmpty;

Widget scaffoldBuilder() {
// Force regeneration of visible screens when VM developer mode is
// enabled.
return ValueListenableBuilder<bool>(
valueListenable: preferences.vmDeveloperModeEnabled,
builder: (_, __, child) {
return MultiProvider(
providers: _providedControllers(),
child: DevToolsScaffold(
embed: embed,
page: page,
screens: screens,
actions: [
if (connectedToVmService)
// TODO(https://github.com/flutter/devtools/issues/1941)
if (serviceManager.connectedApp!.isFlutterAppNow!) ...[
const HotReloadButton(),
const HotRestartButton(),
],
...DevToolsScaffold.defaultActions(),
],
),
);
},
child: DevToolsScaffold.withChild(
embed: embed,
child: CenteredMessage(
page != null
? 'The "$page" screen is not available for this application.'
: 'No tabs available for this application.',
...DevToolsScaffold.defaultActions(),
],
),
),
);
},
);
);
},
);
}

return connectedToVmService
? Initializer(
url: vmServiceUri,
allowConnectionScreenOnDisconnect: !embed,
builder: (_) => scaffoldBuilder(),
)
: scaffoldBuilder();
}

/// The pages that the app exposes.
Expand Down Expand Up @@ -475,8 +467,14 @@ class _AlternateCheckedModeBanner extends StatelessWidget {
///
/// Conditional screens can be added to this list, and they will automatically
/// be shown or hidden based on the [Screen.conditionalLibrary] provided.
List<DevToolsScreen> get defaultScreens {
List<DevToolsScreen> defaultScreens({
List<DevToolsJsonFile> sampleData = const [],
}) {
return devtoolsScreens ??= <DevToolsScreen>[
DevToolsScreen<void>(
HomeScreen(sampleData: sampleData),
createController: (_) {},
),
DevToolsScreen<InspectorController>(
InspectorScreen(),
createController: (_) => InspectorController(
Expand Down
61 changes: 1 addition & 60 deletions packages/devtools_app/lib/src/framework/app_bar.dart
Expand Up @@ -15,15 +15,12 @@ class DevToolsAppBar extends StatelessWidget {
const DevToolsAppBar({
super.key,
required this.tabController,
required this.title,
required this.screens,
required this.actions,
});

final TabController? tabController;

final String title;

final List<Screen> screens;

final List<Widget>? actions;
Expand All @@ -37,7 +34,6 @@ class DevToolsAppBar extends StatelessWidget {
List<Screen> visibleScreens = screens;
bool tabsOverflow({bool includeOverflowButtonWidth = false}) {
return _scaffoldHeaderWidth(
title: title,
screens: visibleScreens,
actions: actions,
textTheme: textTheme,
Expand All @@ -46,13 +42,11 @@ class DevToolsAppBar extends StatelessWidget {
MediaQuery.of(context).size.width;
}

var hideTitle = false;
var overflow = tabsOverflow();
while (overflow) {
visibleScreens = List.of(visibleScreens)..safeRemoveLast();
overflow = tabsOverflow(includeOverflowButtonWidth: true);
if (overflow && visibleScreens.isEmpty) {
hideTitle = true;
break;
}
}
Expand Down Expand Up @@ -82,12 +76,6 @@ class DevToolsAppBar extends StatelessWidget {
],
);

final leftPadding = hideTitle
? 0.0
: calculateTitleWidth(
title,
textTheme: Theme.of(context).textTheme,
);
final rightPadding = math.max(
0.0,
// Use [actions] here instead of [actionsWithSpacer] because we may
Expand All @@ -104,7 +92,6 @@ class DevToolsAppBar extends StatelessWidget {
child: Padding(
padding: EdgeInsets.only(
top: densePadding,
left: leftPadding,
right: rightPadding,
),
child: Row(
Expand Down Expand Up @@ -135,7 +122,6 @@ class DevToolsAppBar extends StatelessWidget {
return AppBar(
// Turn off the appbar's back button.
automaticallyImplyLeading: false,
title: hideTitle ? const SizedBox.shrink() : DevToolsTitle(title: title),
centerTitle: false,
toolbarHeight: defaultToolbarHeight,
actions: actionsWithSpacer,
Expand All @@ -145,18 +131,16 @@ class DevToolsAppBar extends StatelessWidget {

/// Returns the width of the scaffold title, tabs and default icons.
double _scaffoldHeaderWidth({
required String title,
required List<Screen> screens,
required List<Widget>? actions,
required TextTheme textTheme,
}) {
final titleWidth = calculateTitleWidth(title, textTheme: textTheme);
final tabsWidth = screens.fold(
0.0,
(prev, screen) => prev + screen.approximateTabWidth(textTheme),
);
final actionsWidth = (actions?.length ?? 0) * actionWidgetSize;
return titleWidth + tabsWidth + actionsWidth;
return tabsWidth + actionsWidth;
}
}

Expand Down Expand Up @@ -267,46 +251,3 @@ class SelectedTabWrapper extends StatelessWidget {
);
}
}

class DevToolsTitle extends StatelessWidget {
const DevToolsTitle({super.key, required this.title});

final String title;

static double get paddingSize =>
intermediateSpacing * 2 + VerticalLineSpacer.totalWidth;

@override
Widget build(BuildContext context) {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
title,
style: Theme.of(context).devToolsTitleStyle,
),
const SizedBox(width: intermediateSpacing),
VerticalLineSpacer(height: defaultToolbarHeight),
],
);
}
}

// TODO(kenz): make private once app bar code is refactored out of scaffold.dart
// and into this file.
double calculateTitleWidth(
String title, {
required TextTheme textTheme,
bool includeTitlePadding = true,
}) {
final painter = TextPainter(
text: TextSpan(
text: title,
style: textTheme.titleMedium,
),
textDirection: TextDirection.ltr,
)..layout();
// Approximate size of the title. Add [defaultSpacing] to account for
// title's leading padding.
return painter.width + (includeTitlePadding ? DevToolsTitle.paddingSize : 0);
}

0 comments on commit be05370

Please sign in to comment.