Skip to content

Commit 79e0665

Browse files
Add documentation links to app size screen and handle null files (#9689)
1 parent af5e1b9 commit 79e0665

6 files changed

Lines changed: 137 additions & 44 deletions

File tree

packages/devtools_app/lib/devtools.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@
1010
/// Note: a regexp in the `dt update-version' command logic matches the constant
1111
/// declaration `const version =`. If you change the declaration you must also
1212
/// modify the regex in the `dt update-version' command logic.
13-
const version = '2.56.0';
13+
const version = '2.57.0-dev.0';

packages/devtools_app/lib/src/screens/app_size/app_size_screen.dart

Lines changed: 84 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,14 @@ class _AppSizeBodyState extends State<AppSizeBody>
116116
testAppSizeFile = await server.requestTestAppSizeFile(testFilePath);
117117
}
118118

119-
// TODO(kenz): add error handling if the files are null
119+
final errorMessages = <String>[
120+
if (baseAppSizeFile == null) 'base app size file: $baseFilePath',
121+
if (testAppSizeFile == null) 'test app size file: $testFilePath',
122+
];
123+
if (errorMessages.isNotEmpty) {
124+
_pushErrorMessage('Failed to load ${errorMessages.join(' and ')}.');
125+
}
126+
120127
if (baseAppSizeFile != null) {
121128
if (testAppSizeFile != null) {
122129
controller.loadDiffTreeFromJsonFiles(
@@ -306,15 +313,78 @@ class DiffTreeTypeDropdown extends StatelessWidget {
306313
}
307314
}
308315

316+
enum ImportInstructionType { analysis, diffOld, diffNew }
317+
318+
class _ImportInstructions extends StatelessWidget {
319+
const _ImportInstructions({this.type = ImportInstructionType.analysis});
320+
321+
final ImportInstructionType type;
322+
323+
@override
324+
Widget build(BuildContext context) {
325+
final theme = Theme.of(context);
326+
327+
List<InlineSpan> buildInstructionText() {
328+
final article = type == ImportInstructionType.diffNew ? 'a' : 'an';
329+
final boldText = type == ImportInstructionType.diffOld
330+
? 'original (old)'
331+
: type == ImportInstructionType.diffNew
332+
? 'modified (new)'
333+
: null;
334+
335+
return [
336+
TextSpan(
337+
text: 'Drag and drop $article ',
338+
style: theme.regularTextStyle,
339+
),
340+
if (boldText != null)
341+
TextSpan(text: boldText, style: theme.boldTextStyle),
342+
TextSpan(
343+
text:
344+
'${boldText != null ? ' ' : ''}AOT snapshot or size analysis file'
345+
' for debugging, or click "Open file".',
346+
style: theme.regularTextStyle,
347+
),
348+
];
349+
}
350+
351+
return Column(
352+
mainAxisSize: MainAxisSize.min,
353+
mainAxisAlignment: MainAxisAlignment.center,
354+
children: [
355+
RichText(
356+
textAlign: TextAlign.center,
357+
text: TextSpan(children: buildInstructionText()),
358+
),
359+
const SizedBox(height: densePadding),
360+
RichText(
361+
textAlign: TextAlign.center,
362+
text: TextSpan(
363+
children: [
364+
TextSpan(text: 'Read ', style: theme.regularTextStyle),
365+
LinkTextSpan(
366+
link: const Link(
367+
display: 'documentation',
368+
url:
369+
'https://docs.flutter.dev/tools/devtools/app-size#generating-size-files',
370+
),
371+
context: context,
372+
),
373+
TextSpan(
374+
text: ' to learn how to generate these files.',
375+
style: theme.regularTextStyle,
376+
),
377+
],
378+
),
379+
),
380+
],
381+
);
382+
}
383+
}
384+
309385
class AnalysisView extends StatefulWidget {
310386
const AnalysisView({super.key});
311387

312-
// TODO(kenz): add links to documentation on how to generate these files, and
313-
// mention the import file button once it is hooked up to a file picker.
314-
static const importInstructions =
315-
'Drag and drop an AOT snapshot or'
316-
' size analysis file for debugging';
317-
318388
@override
319389
State<AnalysisView> createState() => _AnalysisViewState();
320390
}
@@ -382,7 +452,7 @@ class _AnalysisViewState extends State<AnalysisView> with AutoDisposeMixin {
382452
children: [
383453
Flexible(
384454
child: FileImportContainer(
385-
instructions: AnalysisView.importInstructions,
455+
instructionsWidget: const _ImportInstructions(),
386456
actionText: 'Analyze Size',
387457
gaScreen: gac.appSize,
388458
gaSelectionImport: gac.importFileSingle,
@@ -407,15 +477,6 @@ class _AnalysisViewState extends State<AnalysisView> with AutoDisposeMixin {
407477
class DiffView extends StatefulWidget {
408478
const DiffView({super.key});
409479

410-
// TODO(kenz): add links to documentation on how to generate these files, and
411-
// mention the import file button once it is hooked up to a file picker.
412-
static const importOldInstructions =
413-
'Drag and drop an original (old) AOT '
414-
'snapshot or size analysis file for debugging';
415-
static const importNewInstructions =
416-
'Drag and drop a modified (new) AOT '
417-
'snapshot or size analysis file for debugging';
418-
419480
@override
420481
State<DiffView> createState() => _DiffViewState();
421482
}
@@ -490,9 +551,12 @@ class _DiffViewState extends State<DiffView> with AutoDisposeMixin {
490551
child: DualFileImportContainer(
491552
firstFileTitle: 'Old',
492553
secondFileTitle: 'New',
493-
// TODO(kenz): perhaps bold "original" and "modified".
494-
firstInstructions: DiffView.importOldInstructions,
495-
secondInstructions: DiffView.importNewInstructions,
554+
firstInstructionsWidget: const _ImportInstructions(
555+
type: ImportInstructionType.diffOld,
556+
),
557+
secondInstructionsWidget: const _ImportInstructions(
558+
type: ImportInstructionType.diffNew,
559+
),
496560
actionText: 'Analyze Diff',
497561
gaScreen: gac.appSize,
498562
gaSelectionImportFirst: gac.importFileDiffFirst,

packages/devtools_app/lib/src/shared/ui/file_import.dart

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -218,9 +218,10 @@ class __DropdownSaveButtonState extends State<_DropdownSaveButton> {
218218

219219
class FileImportContainer extends StatefulWidget {
220220
const FileImportContainer({
221-
required this.instructions,
222221
required this.gaScreen,
223222
required this.gaSelectionImport,
223+
this.instructions,
224+
this.instructionsWidget,
224225
this.title,
225226
this.backgroundColor,
226227
this.gaSelectionAction,
@@ -230,13 +231,15 @@ class FileImportContainer extends StatefulWidget {
230231
this.onFileCleared,
231232
this.extensions = const ['json'],
232233
super.key,
233-
});
234+
}) : assert((instructions == null) != (instructionsWidget == null));
234235

235236
final String? title;
236237

237238
final Color? backgroundColor;
238239

239-
final String instructions;
240+
final String? instructions;
241+
242+
final Widget? instructionsWidget;
240243

241244
/// The title of the action button.
242245
final String? actionText;
@@ -276,7 +279,10 @@ class _FileImportContainerState extends State<FileImportContainer> {
276279
Text(title, style: const TextStyle(fontSize: 18.0)),
277280
const SizedBox(height: extraLargeSpacing),
278281
],
279-
CenteredMessage(message: widget.instructions),
282+
if (widget.instructionsWidget != null)
283+
widget.instructionsWidget!
284+
else if (widget.instructions != null)
285+
CenteredMessage(message: widget.instructions),
280286
const SizedBox(height: denseSpacing),
281287
_buildImportFileRow(),
282288
if (widget.actionText != null && widget.onAction != null)
@@ -477,8 +483,10 @@ class DualFileImportContainer extends StatefulWidget {
477483
super.key,
478484
required this.firstFileTitle,
479485
required this.secondFileTitle,
480-
required this.firstInstructions,
481-
required this.secondInstructions,
486+
this.firstInstructions,
487+
this.secondInstructions,
488+
this.firstInstructionsWidget,
489+
this.secondInstructionsWidget,
482490
required this.actionText,
483491
required this.onAction,
484492
required this.gaScreen,
@@ -489,8 +497,10 @@ class DualFileImportContainer extends StatefulWidget {
489497

490498
final String firstFileTitle;
491499
final String secondFileTitle;
492-
final String firstInstructions;
493-
final String secondInstructions;
500+
final String? firstInstructions;
501+
final String? secondInstructions;
502+
final Widget? firstInstructionsWidget;
503+
final Widget? secondInstructionsWidget;
494504
final String gaScreen;
495505
final String gaSelectionImportFirst;
496506
final String gaSelectionImportSecond;
@@ -525,6 +535,7 @@ class _DualFileImportContainerState extends State<DualFileImportContainer> {
525535
title: widget.firstFileTitle,
526536
backgroundColor: backgroundColor,
527537
instructions: widget.firstInstructions,
538+
instructionsWidget: widget.firstInstructionsWidget,
528539
onFileSelected: onFirstFileSelected,
529540
onFileCleared: onFirstFileCleared,
530541
gaScreen: widget.gaScreen,
@@ -539,6 +550,7 @@ class _DualFileImportContainerState extends State<DualFileImportContainer> {
539550
title: widget.secondFileTitle,
540551
backgroundColor: backgroundColor,
541552
instructions: widget.secondInstructions,
553+
instructionsWidget: widget.secondInstructionsWidget,
542554
onFileSelected: onSecondFileSelected,
543555
onFileCleared: onSecondFileCleared,
544556
gaScreen: widget.gaScreen,

packages/devtools_app/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ publish_to: none
77

88
# Note: this version should only be updated by running the 'dt update-version'
99
# command that updates the version here and in 'devtools.dart'.
10-
version: 2.56.0
10+
version: 2.57.0-dev.0
1111

1212
repository: https://github.com/flutter/devtools/tree/master/packages/devtools_app
1313

packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ found in the LICENSE file or at https://developers.google.com/open-source/licens
66
This is a draft for future release notes that are going to land on
77
[the Flutter website](https://docs.flutter.dev/tools/devtools/release-notes).
88

9-
# DevTools 2.56.0 release notes
9+
# DevTools 2.57.0 release notes
1010

11-
The 2.56.0 release of the Dart and Flutter DevTools
11+
The 2.57.0 release of the Dart and Flutter DevTools
1212
includes the following changes among other general improvements.
1313
To learn more about DevTools, check out the
1414
[DevTools overview](/tools/devtools).
@@ -39,15 +39,16 @@ TODO: Remove this section if there are not any updates.
3939

4040
## Network profiler updates
4141

42-
- Fix crash in the Network tab when viewing binary multipart request or response bodies (#9978)
42+
- Fix crash in the Network tab when viewing binary multipart request or
43+
response bodies. [#9680](https://github.com/flutter/devtools/pull/9680)
4344

4445
## Logging updates
4546

4647
TODO: Remove this section if there are not any updates.
4748

4849
## App size tool updates
4950

50-
TODO: Remove this section if there are not any updates.
51+
- Added documentation links and improved handling for null files. [#9689](https://github.com/flutter/devtools/pull/9689)
5152

5253
## Deep links tool updates
5354

@@ -68,4 +69,4 @@ TODO: Remove this section if there are not any updates.
6869
## Full commit history
6970

7071
To find a complete list of changes in this release, check out the
71-
[DevTools git log](https://github.com/flutter/devtools/tree/v2.56.0).
72+
[DevTools git log](https://github.com/flutter/devtools/tree/v2.57.0).

packages/devtools_app/test/screens/app_size/app_size_screen_test.dart

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,9 @@ void main() {
214214
expect(find.byType(ClearButton), findsOneWidget);
215215

216216
expect(find.byType(FileImportContainer), findsOneWidget);
217-
expect(find.text(AnalysisView.importInstructions), findsOneWidget);
217+
const importInstructions =
218+
'Drag and drop an AOT snapshot or size analysis file for debugging, or click "Open file".';
219+
expect(find.richText(importInstructions), findsOneWidget);
218220
expect(find.text('No File Selected'), findsOneWidget);
219221

220222
appSizeController.loadTreeFromJsonFile(
@@ -227,7 +229,7 @@ void main() {
227229
await tester.pumpAndSettle();
228230

229231
expect(find.byType(FileImportContainer), findsNothing);
230-
expect(find.text(AnalysisView.importInstructions), findsNothing);
232+
expect(find.richText(importInstructions), findsNothing);
231233
expect(find.text('No File Selected'), findsNothing);
232234
expect(find.byType(AnalysisView), findsOneWidget);
233235
expect(
@@ -261,7 +263,9 @@ void main() {
261263
await tester.pumpAndSettle();
262264

263265
expect(find.byType(FileImportContainer), findsOneWidget);
264-
expect(find.text(AnalysisView.importInstructions), findsOneWidget);
266+
const importInstructions =
267+
'Drag and drop an AOT snapshot or size analysis file for debugging, or click "Open file".';
268+
expect(find.richText(importInstructions), findsOneWidget);
265269
expect(find.text('No File Selected'), findsOneWidget);
266270
});
267271
});
@@ -302,8 +306,12 @@ void main() {
302306

303307
expect(find.byType(DualFileImportContainer), findsOneWidget);
304308
expect(find.byType(FileImportContainer), findsNWidgets(2));
305-
expect(find.text(DiffView.importOldInstructions), findsOneWidget);
306-
expect(find.text(DiffView.importNewInstructions), findsOneWidget);
309+
const importOldInstructions =
310+
'Drag and drop an original (old) AOT snapshot or size analysis file for debugging, or click "Open file".';
311+
const importNewInstructions =
312+
'Drag and drop a modified (new) AOT snapshot or size analysis file for debugging, or click "Open file".';
313+
expect(find.richText(importOldInstructions), findsOneWidget);
314+
expect(find.richText(importNewInstructions), findsOneWidget);
307315
expect(find.text('No File Selected'), findsNWidgets(2));
308316
});
309317

@@ -323,8 +331,12 @@ void main() {
323331
await tester.pumpAndSettle();
324332

325333
expect(find.byType(FileImportContainer), findsNothing);
326-
expect(find.text(DiffView.importOldInstructions), findsNothing);
327-
expect(find.text(DiffView.importNewInstructions), findsNothing);
334+
const importOldInstructions =
335+
'Drag and drop an original (old) AOT snapshot or size analysis file for debugging, or click "Open file".';
336+
const importNewInstructions =
337+
'Drag and drop a modified (new) AOT snapshot or size analysis file for debugging, or click "Open file".';
338+
expect(find.richText(importOldInstructions), findsNothing);
339+
expect(find.richText(importNewInstructions), findsNothing);
328340
expect(find.text('No File Selected'), findsNothing);
329341

330342
expect(find.byType(DiffView), findsOneWidget);
@@ -393,8 +405,12 @@ void main() {
393405

394406
expect(find.byType(DualFileImportContainer), findsOneWidget);
395407
expect(find.byType(FileImportContainer), findsNWidgets(2));
396-
expect(find.text(DiffView.importOldInstructions), findsOneWidget);
397-
expect(find.text(DiffView.importNewInstructions), findsOneWidget);
408+
const importOldInstructions =
409+
'Drag and drop an original (old) AOT snapshot or size analysis file for debugging, or click "Open file".';
410+
const importNewInstructions =
411+
'Drag and drop a modified (new) AOT snapshot or size analysis file for debugging, or click "Open file".';
412+
expect(find.richText(importOldInstructions), findsOneWidget);
413+
expect(find.richText(importNewInstructions), findsOneWidget);
398414
expect(find.text('No File Selected'), findsNWidgets(2));
399415
});
400416
});

0 commit comments

Comments
 (0)