Adds list required components when VS is not installed (#40397)

diff --git a/packages/flutter_tools/lib/src/base/user_messages.dart b/packages/flutter_tools/lib/src/base/user_messages.dart
index 8b2f440..2a078bc 100644
--- a/packages/flutter_tools/lib/src/base/user_messages.dart
+++ b/packages/flutter_tools/lib/src/base/user_messages.dart
@@ -190,9 +190,10 @@
       'Visual Studio is missing necessary components. Please re-run the '
       'Visual Studio installer for the "$workload" workload, and include these components:\n'
       '  ${components.join('\n  ')}';
-  String get visualStudioMissing =>
+  String visualStudioMissing(String workload, List<String> components) =>
       'Visual Studio not installed; this is necessary for Windows development.\n'
-      'Download at https://visualstudio.microsoft.com/downloads/.';
+      'Download at https://visualstudio.microsoft.com/downloads/.\n'
+      'Please install the "$workload" workload, including following components:\n  ${components.join('\n  ')}';
   String get visualStudioIsPrerelease => 'The current Visual Studio installation is a pre-release version. It may not be '
       'supported by Flutter yet.';
   String get visualStudioNotLaunchable =>
diff --git a/packages/flutter_tools/lib/src/windows/visual_studio_validator.dart b/packages/flutter_tools/lib/src/windows/visual_studio_validator.dart
index 364e277..5bbe083 100644
--- a/packages/flutter_tools/lib/src/windows/visual_studio_validator.dart
+++ b/packages/flutter_tools/lib/src/windows/visual_studio_validator.dart
@@ -12,6 +12,8 @@
 class VisualStudioValidator extends DoctorValidator {
   const VisualStudioValidator() : super('Visual Studio - develop for Windows');
 
+  int get majorVersion => int.tryParse(visualStudio.fullVersion.split('.')[0]);
+
   @override
   Future<ValidationResult> validate() async {
     final List<ValidationMessage> messages = <ValidationMessage>[];
@@ -46,7 +48,6 @@
         messages.add(ValidationMessage.error(userMessages.visualStudioNotLaunchable));
       } else  if (!visualStudio.hasNecessaryComponents) {
         status = ValidationType.partial;
-        final int majorVersion = int.tryParse(visualStudio.fullVersion.split('.')[0]);
         messages.add(ValidationMessage.error(
             userMessages.visualStudioMissingComponents(
                 visualStudio.workloadDescription,
@@ -57,7 +58,12 @@
       versionInfo = '${visualStudio.displayName} ${visualStudio.displayVersion}';
     } else {
       status = ValidationType.missing;
-      messages.add(ValidationMessage.error(userMessages.visualStudioMissing));
+      messages.add(ValidationMessage.error(
+        userMessages.visualStudioMissing(
+          visualStudio.workloadDescription,
+          visualStudio.necessaryComponentDescriptions(majorVersion)
+        )
+      ));
     }
 
     return ValidationResult(status, messages, statusInfo: versionInfo);
diff --git a/packages/flutter_tools/test/general.shard/windows/visual_studio_validator_test.dart b/packages/flutter_tools/test/general.shard/windows/visual_studio_validator_test.dart
index d632a44..7e4b745 100644
--- a/packages/flutter_tools/test/general.shard/windows/visual_studio_validator_test.dart
+++ b/packages/flutter_tools/test/general.shard/windows/visual_studio_validator_test.dart
@@ -26,6 +26,9 @@
       when(mockVisualStudio.isLaunchable).thenReturn(true);
       when(mockVisualStudio.isRebootRequired).thenReturn(false);
       when(mockVisualStudio.hasNecessaryComponents).thenReturn(true);
+      when(mockVisualStudio.workloadDescription).thenReturn('Desktop development');
+      when(mockVisualStudio.necessaryComponentDescriptions(any)).thenReturn(<String>['A', 'B']);
+      when(mockVisualStudio.fullVersion).thenReturn('15.1');
     });
 
     testUsingContext('Emits a message when Visual Studio is a pre-release version', () async {
@@ -87,9 +90,6 @@
 
     testUsingContext('Emits partial status when Visual Studio is installed without necessary components', () async {
       when(visualStudio.hasNecessaryComponents).thenReturn(false);
-      when(visualStudio.workloadDescription).thenReturn('Desktop development');
-      when(visualStudio.necessaryComponentDescriptions(any)).thenReturn(<String>['A', 'B']);
-      when(visualStudio.fullVersion).thenReturn('15.1');
       const VisualStudioValidator validator = VisualStudioValidator();
       final ValidationResult result = await validator.validate();
       expect(result.type, ValidationType.partial);
@@ -109,7 +109,12 @@
       when(visualStudio.isInstalled).thenReturn(false);
       const VisualStudioValidator validator = VisualStudioValidator();
       final ValidationResult result = await validator.validate();
-      final ValidationMessage expectedMessage = ValidationMessage.error(userMessages.visualStudioMissing);
+      final ValidationMessage expectedMessage = ValidationMessage.error(
+        userMessages.visualStudioMissing(
+          visualStudio.workloadDescription,
+          visualStudio.necessaryComponentDescriptions(validator.majorVersion)
+        )
+      );
       expect(result.messages.contains(expectedMessage), true);
       expect(result.type, ValidationType.missing);
     }, overrides: <Type, Generator>{