diff --git a/packages/flutter_tools/lib/src/commands/widget_preview.dart b/packages/flutter_tools/lib/src/commands/widget_preview.dart index 30bcbabcdd4..36d9f3e77c9 100644 --- a/packages/flutter_tools/lib/src/commands/widget_preview.dart +++ b/packages/flutter_tools/lib/src/commands/widget_preview.dart @@ -318,8 +318,9 @@ final class WidgetPreviewStartCommand extends WidgetPreviewSubCommandBase with C // after we generate the scaffold project as invoking the getter triggers // lazy initialization of the preview scaffold's FlutterManifest before // the scaffold project's pubspec has been generated. + final FlutterProject widgetPreviewScaffoldProject = rootProject.widgetPreviewScaffoldProject; _previewCodeGenerator = PreviewCodeGenerator( - widgetPreviewScaffoldProject: rootProject.widgetPreviewScaffoldProject, + widgetPreviewScaffoldProject: widgetPreviewScaffoldProject, fs: fs, ); @@ -333,6 +334,12 @@ final class WidgetPreviewStartCommand extends WidgetPreviewSubCommandBase with C await _previewPubspecBuilder.populatePreviewPubspec(rootProject: rootProject); } + if (!widgetPreviewScaffoldProject.dartTool.existsSync()) { + await _previewPubspecBuilder.generatePackageConfig( + widgetPreviewScaffoldProject: widgetPreviewScaffoldProject, + ); + } + shutdownHooks.addShutdownHook(() async { await _widgetPreviewApp?.exitApp(); await _previewDetector.dispose(); @@ -343,7 +350,7 @@ final class WidgetPreviewStartCommand extends WidgetPreviewSubCommandBase with C await configureDtd(); final int result = await runPreviewEnvironment( - widgetPreviewScaffoldProject: rootProject.widgetPreviewScaffoldProject, + widgetPreviewScaffoldProject: widgetPreviewScaffoldProject, ); if (result != 0) { throwToolExit('Failed to launch the widget previewer.', exitCode: result); diff --git a/packages/flutter_tools/lib/src/widget_preview/preview_pubspec_builder.dart b/packages/flutter_tools/lib/src/widget_preview/preview_pubspec_builder.dart index 3e8d3689857..75394ac51c9 100644 --- a/packages/flutter_tools/lib/src/widget_preview/preview_pubspec_builder.dart +++ b/packages/flutter_tools/lib/src/widget_preview/preview_pubspec_builder.dart @@ -91,6 +91,8 @@ class PreviewPubspecBuilder { ); } + PubOutputMode get _outputMode => verbose ? PubOutputMode.all : PubOutputMode.failuresOnly; + Future populatePreviewPubspec({ required FlutterProject rootProject, String? updatedPubspecPath, @@ -126,7 +128,6 @@ class PreviewPubspecBuilder { }), }; - final PubOutputMode outputMode = verbose ? PubOutputMode.all : PubOutputMode.failuresOnly; await pub.interactively( [ pubAdd, @@ -146,7 +147,7 @@ class PreviewPubspecBuilder { context: PubContext.pubAdd, command: pubAdd, touchesPackageConfig: true, - outputMode: outputMode, + outputMode: _outputMode, ); // Adds dependencies required by the widget preview scaffolding. @@ -161,18 +162,22 @@ class PreviewPubspecBuilder { context: PubContext.pubAdd, command: pubAdd, touchesPackageConfig: true, - outputMode: outputMode, + outputMode: _outputMode, ); + await generatePackageConfig(widgetPreviewScaffoldProject: widgetPreviewScaffoldProject); + previewManifest.updatePubspecHash(updatedPubspecPath: updatedPubspecPath); + } + + /// Generates `widget_preview_scaffold/.dart_tool/package_config.json`. + Future generatePackageConfig({required FlutterProject widgetPreviewScaffoldProject}) async { // Generate package_config.json. await pub.get( context: PubContext.create, project: widgetPreviewScaffoldProject, offline: offline, - outputMode: outputMode, + outputMode: _outputMode, ); - - previewManifest.updatePubspecHash(updatedPubspecPath: updatedPubspecPath); } void onPubspecChangeDetected(String path) { diff --git a/packages/flutter_tools/test/integration.shard/widget_preview_test.dart b/packages/flutter_tools/test/integration.shard/widget_preview_test.dart index ef75a4da21e..886342fbfb1 100644 --- a/packages/flutter_tools/test/integration.shard/widget_preview_test.dart +++ b/packages/flutter_tools/test/integration.shard/widget_preview_test.dart @@ -7,6 +7,7 @@ import 'dart:convert'; import 'package:dtd/dtd.dart'; import 'package:file/file.dart'; +import 'package:file_testing/file_testing.dart'; import 'package:flutter_tools/src/base/io.dart'; import 'package:flutter_tools/src/base/logger.dart'; import 'package:flutter_tools/src/commands/widget_preview.dart'; @@ -125,6 +126,36 @@ void main() { await runWidgetPreview(expectedMessages: firstLaunchMessagesWebServer, useWebServer: true); }); + testWithoutContext('runs flutter pub get in widget_preview_scaffold if ' + "widget_preview_scaffold/.dart_tool doesn't exist", () async { + // Regression test for https://github.com/flutter/flutter/issues/178660 + // Generate the widget preview scaffold, but don't bother launching it. + processManager.runSync([ + flutterBin, + 'widget-preview', + 'start', + '--no-${WidgetPreviewStartCommand.kLaunchPreviewer}', + ], workingDirectory: tempDir.path); + + // Ensure widget_preview_scaffold/.dart_tool/package_config.json exists. + final Directory widgetPreviewScaffoldDartTool = tempDir + .childDirectory('.dart_tool') + .childDirectory('widget_preview_scaffold') + .childDirectory('.dart_tool'); + expect(widgetPreviewScaffoldDartTool, exists); + expect(widgetPreviewScaffoldDartTool.childFile('package_config.json'), exists); + + // Delete widget_preview_scaffold/.dart_tool/. This simulates an interrupted + // flutter widget-preview start where 'flutter pub get' wasn't run after + // the widget_preview_scaffold project was created. + widgetPreviewScaffoldDartTool.deleteSync(recursive: true); + + // Ensure we don't crash due to the package_config.json lookup pointing to + // the parent project's package_config.json due to + // widget_preview_scaffold/.dart_tool/package_config.json not existing. + await runWidgetPreview(expectedMessages: subsequentLaunchMessagesWeb); + }); + testWithoutContext('does not recreate project on subsequent runs', () async { // The first run of 'flutter widget-preview start' should generate a new preview scaffold await runWidgetPreview(expectedMessages: firstLaunchMessagesWeb);