Skip to content

Commit cc14ef5

Browse files
authored
[ Widget Preview ] Fix crash when widget_preview_scaffold/.dart_tool doesn't exist (flutter#178662)
If the `.dart_tool/widget_preview_scaffold/.dart_tool/` directory wasn't created during the initial run of `flutter widget-preview start` due to the command being interrupted or `pub` being disabled, `flutter widget-preview start` would crash due to the `package_config.json` logic walking up the directory structure looking for the nearest `package_config.json`. This would point to the parent project's `package_config.json`, which would not be compatible with the widget preview scaffold project. Fixes flutter#178660 and flutter#177655
1 parent 4cf8eb9 commit cc14ef5

File tree

3 files changed

+51
-8
lines changed

3 files changed

+51
-8
lines changed

packages/flutter_tools/lib/src/commands/widget_preview.dart

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -318,8 +318,9 @@ final class WidgetPreviewStartCommand extends WidgetPreviewSubCommandBase with C
318318
// after we generate the scaffold project as invoking the getter triggers
319319
// lazy initialization of the preview scaffold's FlutterManifest before
320320
// the scaffold project's pubspec has been generated.
321+
final FlutterProject widgetPreviewScaffoldProject = rootProject.widgetPreviewScaffoldProject;
321322
_previewCodeGenerator = PreviewCodeGenerator(
322-
widgetPreviewScaffoldProject: rootProject.widgetPreviewScaffoldProject,
323+
widgetPreviewScaffoldProject: widgetPreviewScaffoldProject,
323324
fs: fs,
324325
);
325326

@@ -333,6 +334,12 @@ final class WidgetPreviewStartCommand extends WidgetPreviewSubCommandBase with C
333334
await _previewPubspecBuilder.populatePreviewPubspec(rootProject: rootProject);
334335
}
335336

337+
if (!widgetPreviewScaffoldProject.dartTool.existsSync()) {
338+
await _previewPubspecBuilder.generatePackageConfig(
339+
widgetPreviewScaffoldProject: widgetPreviewScaffoldProject,
340+
);
341+
}
342+
336343
shutdownHooks.addShutdownHook(() async {
337344
await _widgetPreviewApp?.exitApp();
338345
await _previewDetector.dispose();
@@ -343,7 +350,7 @@ final class WidgetPreviewStartCommand extends WidgetPreviewSubCommandBase with C
343350

344351
await configureDtd();
345352
final int result = await runPreviewEnvironment(
346-
widgetPreviewScaffoldProject: rootProject.widgetPreviewScaffoldProject,
353+
widgetPreviewScaffoldProject: widgetPreviewScaffoldProject,
347354
);
348355
if (result != 0) {
349356
throwToolExit('Failed to launch the widget previewer.', exitCode: result);

packages/flutter_tools/lib/src/widget_preview/preview_pubspec_builder.dart

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ class PreviewPubspecBuilder {
9191
);
9292
}
9393

94+
PubOutputMode get _outputMode => verbose ? PubOutputMode.all : PubOutputMode.failuresOnly;
95+
9496
Future<void> populatePreviewPubspec({
9597
required FlutterProject rootProject,
9698
String? updatedPubspecPath,
@@ -126,7 +128,6 @@ class PreviewPubspecBuilder {
126128
}),
127129
};
128130

129-
final PubOutputMode outputMode = verbose ? PubOutputMode.all : PubOutputMode.failuresOnly;
130131
await pub.interactively(
131132
<String>[
132133
pubAdd,
@@ -146,7 +147,7 @@ class PreviewPubspecBuilder {
146147
context: PubContext.pubAdd,
147148
command: pubAdd,
148149
touchesPackageConfig: true,
149-
outputMode: outputMode,
150+
outputMode: _outputMode,
150151
);
151152

152153
// Adds dependencies required by the widget preview scaffolding.
@@ -161,18 +162,22 @@ class PreviewPubspecBuilder {
161162
context: PubContext.pubAdd,
162163
command: pubAdd,
163164
touchesPackageConfig: true,
164-
outputMode: outputMode,
165+
outputMode: _outputMode,
165166
);
166167

168+
await generatePackageConfig(widgetPreviewScaffoldProject: widgetPreviewScaffoldProject);
169+
previewManifest.updatePubspecHash(updatedPubspecPath: updatedPubspecPath);
170+
}
171+
172+
/// Generates `widget_preview_scaffold/.dart_tool/package_config.json`.
173+
Future<void> generatePackageConfig({required FlutterProject widgetPreviewScaffoldProject}) async {
167174
// Generate package_config.json.
168175
await pub.get(
169176
context: PubContext.create,
170177
project: widgetPreviewScaffoldProject,
171178
offline: offline,
172-
outputMode: outputMode,
179+
outputMode: _outputMode,
173180
);
174-
175-
previewManifest.updatePubspecHash(updatedPubspecPath: updatedPubspecPath);
176181
}
177182

178183
void onPubspecChangeDetected(String path) {

packages/flutter_tools/test/integration.shard/widget_preview_test.dart

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import 'dart:convert';
77

88
import 'package:dtd/dtd.dart';
99
import 'package:file/file.dart';
10+
import 'package:file_testing/file_testing.dart';
1011
import 'package:flutter_tools/src/base/io.dart';
1112
import 'package:flutter_tools/src/base/logger.dart';
1213
import 'package:flutter_tools/src/commands/widget_preview.dart';
@@ -125,6 +126,36 @@ void main() {
125126
await runWidgetPreview(expectedMessages: firstLaunchMessagesWebServer, useWebServer: true);
126127
});
127128

129+
testWithoutContext('runs flutter pub get in widget_preview_scaffold if '
130+
"widget_preview_scaffold/.dart_tool doesn't exist", () async {
131+
// Regression test for https://github.com/flutter/flutter/issues/178660
132+
// Generate the widget preview scaffold, but don't bother launching it.
133+
processManager.runSync(<String>[
134+
flutterBin,
135+
'widget-preview',
136+
'start',
137+
'--no-${WidgetPreviewStartCommand.kLaunchPreviewer}',
138+
], workingDirectory: tempDir.path);
139+
140+
// Ensure widget_preview_scaffold/.dart_tool/package_config.json exists.
141+
final Directory widgetPreviewScaffoldDartTool = tempDir
142+
.childDirectory('.dart_tool')
143+
.childDirectory('widget_preview_scaffold')
144+
.childDirectory('.dart_tool');
145+
expect(widgetPreviewScaffoldDartTool, exists);
146+
expect(widgetPreviewScaffoldDartTool.childFile('package_config.json'), exists);
147+
148+
// Delete widget_preview_scaffold/.dart_tool/. This simulates an interrupted
149+
// flutter widget-preview start where 'flutter pub get' wasn't run after
150+
// the widget_preview_scaffold project was created.
151+
widgetPreviewScaffoldDartTool.deleteSync(recursive: true);
152+
153+
// Ensure we don't crash due to the package_config.json lookup pointing to
154+
// the parent project's package_config.json due to
155+
// widget_preview_scaffold/.dart_tool/package_config.json not existing.
156+
await runWidgetPreview(expectedMessages: subsequentLaunchMessagesWeb);
157+
});
158+
128159
testWithoutContext('does not recreate project on subsequent runs', () async {
129160
// The first run of 'flutter widget-preview start' should generate a new preview scaffold
130161
await runWidgetPreview(expectedMessages: firstLaunchMessagesWeb);

0 commit comments

Comments
 (0)