Skip to content

Commit 09987dc

Browse files
authored
Migrate create command to null safety (#104484)
1 parent b5adbee commit 09987dc

File tree

4 files changed

+105
-121
lines changed

4 files changed

+105
-121
lines changed

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

Lines changed: 53 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5-
// @dart = 2.8
6-
75
import '../android/gradle_utils.dart' as gradle;
86
import '../base/common.dart';
97
import '../base/context.dart';
@@ -32,8 +30,8 @@ const String kPlatformHelp =
3230

3331
class CreateCommand extends CreateBase {
3432
CreateCommand({
35-
bool verboseHelp = false,
36-
}) : super(verboseHelp: verboseHelp) {
33+
super.verboseHelp = false,
34+
}) {
3735
addPlatformsOptions(customHelp: kPlatformHelp);
3836
argParser.addOption(
3937
'template',
@@ -57,7 +55,6 @@ class CreateCommand extends CreateBase {
5755
flutterProjectTypeToString(FlutterProjectType.module): 'Generate a project to add a Flutter module to an '
5856
'existing Android or iOS application.',
5957
},
60-
defaultsTo: null,
6158
);
6259
argParser.addOption(
6360
'sample',
@@ -66,7 +63,6 @@ class CreateCommand extends CreateBase {
6663
'"--template=app". The value should be the sample ID of the desired sample from the API '
6764
'documentation website (http://docs.flutter.dev/). An example can be found at: '
6865
'https://api.flutter.dev/flutter/widgets/SingleChildScrollView-class.html',
69-
defaultsTo: null,
7066
valueHelp: 'id',
7167
);
7268
argParser.addOption(
@@ -88,7 +84,7 @@ class CreateCommand extends CreateBase {
8884
String get category => FlutterCommandCategory.project;
8985

9086
@override
91-
String get invocation => '${runner.executableName} $name <output directory>';
87+
String get invocation => '${runner?.executableName} $name <output directory>';
9288

9389
@override
9490
Future<CustomDimensions> get usageValues async {
@@ -100,8 +96,7 @@ class CreateCommand extends CreateBase {
10096
}
10197

10298
// Lazy-initialize the net utilities with values from the context.
103-
Net _cachedNet;
104-
Net get _net => _cachedNet ??= Net(
99+
late final Net _net = Net(
105100
httpClientFactory: context.get<HttpClientFactory>(),
106101
logger: globals.logger,
107102
platform: globals.platform,
@@ -112,25 +107,25 @@ class CreateCommand extends CreateBase {
112107
? 'api.flutter.dev'
113108
: 'master-api.flutter.dev';
114109

115-
Future<String> _fetchSampleFromServer(String sampleId) async {
110+
Future<String?> _fetchSampleFromServer(String sampleId) async {
116111
// Sanity check the sampleId
117112
if (sampleId.contains(RegExp(r'[^-\w\.]'))) {
118113
throwToolExit('Sample ID "$sampleId" contains invalid characters. Check the ID in the '
119114
'documentation and try again.');
120115
}
121116

122117
final Uri snippetsUri = Uri.https(_snippetsHost, 'snippets/$sampleId.dart');
123-
final List<int> data = await _net.fetchUrl(snippetsUri);
118+
final List<int>? data = await _net.fetchUrl(snippetsUri);
124119
if (data == null || data.isEmpty) {
125120
return null;
126121
}
127122
return utf8.decode(data);
128123
}
129124

130125
/// Fetches the samples index file from the Flutter docs website.
131-
Future<String> _fetchSamplesIndexFromServer() async {
126+
Future<String?> _fetchSamplesIndexFromServer() async {
132127
final Uri snippetsUri = Uri.https(_snippetsHost, 'snippets/index.json');
133-
final List<int> data = await _net.fetchUrl(snippetsUri, maxAttempts: 2);
128+
final List<int>? data = await _net.fetchUrl(snippetsUri, maxAttempts: 2);
134129
if (data == null || data.isEmpty) {
135130
return null;
136131
}
@@ -145,7 +140,7 @@ class CreateCommand extends CreateBase {
145140
if (outputFile.existsSync()) {
146141
throwToolExit('File "$outputFilePath" already exists', exitCode: 1);
147142
}
148-
final String samplesJson = await _fetchSamplesIndexFromServer();
143+
final String? samplesJson = await _fetchSamplesIndexFromServer();
149144
if (samplesJson == null) {
150145
throwToolExit('Unable to download samples', exitCode: 2);
151146
} else {
@@ -158,11 +153,12 @@ class CreateCommand extends CreateBase {
158153
}
159154

160155
FlutterProjectType _getProjectType(Directory projectDir) {
161-
FlutterProjectType template;
162-
FlutterProjectType detectedProjectType;
156+
FlutterProjectType? template;
157+
FlutterProjectType? detectedProjectType;
163158
final bool metadataExists = projectDir.absolute.childFile('.metadata').existsSync();
164-
if (argResults['template'] != null) {
165-
template = stringToProjectType(stringArgDeprecated('template'));
159+
final String? templateArgument = stringArg('template');
160+
if (templateArgument != null) {
161+
template = stringToProjectType(templateArgument);
166162
}
167163
// If the project directory exists and isn't empty, then try to determine the template
168164
// type from the project directory.
@@ -188,23 +184,25 @@ class CreateCommand extends CreateBase {
188184

189185
@override
190186
Future<FlutterCommandResult> runCommand() async {
191-
if (argResults['list-samples'] != null) {
187+
final String? listSamples = stringArg('list-samples');
188+
if (listSamples != null) {
192189
// _writeSamplesJson can potentially be long-lived.
193-
await _writeSamplesJson(stringArgDeprecated('list-samples'));
190+
await _writeSamplesJson(listSamples);
194191
return FlutterCommandResult.success();
195192
}
196193

197194
validateOutputDirectoryArg();
198195

199-
String sampleCode;
200-
if (argResults['sample'] != null) {
201-
if (argResults['template'] != null &&
202-
stringToProjectType(stringArgDeprecated('template') ?? 'app') != FlutterProjectType.app) {
196+
String? sampleCode;
197+
final String? sampleArgument = stringArg('sample');
198+
if (sampleArgument != null) {
199+
final String? templateArgument = stringArg('template');
200+
if (templateArgument != null && stringToProjectType(templateArgument) != FlutterProjectType.app) {
203201
throwToolExit('Cannot specify --sample with a project type other than '
204202
'"${flutterProjectTypeToString(FlutterProjectType.app)}"');
205203
}
206204
// Fetch the sample from the server.
207-
sampleCode = await _fetchSampleFromServer(stringArgDeprecated('sample'));
205+
sampleCode = await _fetchSampleFromServer(sampleArgument);
208206
}
209207

210208
final FlutterProjectType template = _getProjectType(projectDir);
@@ -215,7 +213,7 @@ class CreateCommand extends CreateBase {
215213

216214
final List<String> platforms = stringsArg('platforms');
217215
// `--platforms` does not support module or package.
218-
if (argResults.wasParsed('platforms') && (generateModule || generatePackage)) {
216+
if (argResults!.wasParsed('platforms') && (generateModule || generatePackage)) {
219217
final String template = generateModule ? 'module' : 'package';
220218
throwToolExit(
221219
'The "--platforms" argument is not supported in $template template.',
@@ -224,18 +222,18 @@ class CreateCommand extends CreateBase {
224222
} else if (platforms == null || platforms.isEmpty) {
225223
throwToolExit('Must specify at least one platform using --platforms',
226224
exitCode: 2);
227-
} else if (generateFfiPlugin && argResults.wasParsed('platforms') && platforms.contains('web')) {
225+
} else if (generateFfiPlugin && argResults!.wasParsed('platforms') && platforms.contains('web')) {
228226
throwToolExit(
229227
'The web platform is not supported in plugin_ffi template.',
230228
exitCode: 2,
231229
);
232-
} else if (generateFfiPlugin && argResults.wasParsed('ios-language')) {
230+
} else if (generateFfiPlugin && argResults!.wasParsed('ios-language')) {
233231
throwToolExit(
234232
'The "ios-language" option is not supported with the plugin_ffi '
235233
'template: the language will always be C or C++.',
236234
exitCode: 2,
237235
);
238-
} else if (generateFfiPlugin && argResults.wasParsed('android-language')) {
236+
} else if (generateFfiPlugin && argResults!.wasParsed('android-language')) {
239237
throwToolExit(
240238
'The "android-language" option is not supported with the plugin_ffi '
241239
'template: the language will always be C or C++.',
@@ -258,7 +256,7 @@ class CreateCommand extends CreateBase {
258256

259257
final String dartSdk = globals.cache.dartSdkBuild;
260258
final bool includeIos = featureFlags.isIOSEnabled && platforms.contains('ios');
261-
String developmentTeam;
259+
String? developmentTeam;
262260
if (includeIos) {
263261
developmentTeam = await getCodeSigningIdentityDevelopmentTeam(
264262
processManager: globals.processManager,
@@ -272,7 +270,7 @@ class CreateCommand extends CreateBase {
272270
// The dart project_name is in snake_case, this variable is the Title Case of the Project Name.
273271
final String titleCaseProjectName = snakeCaseToTitleCase(projectName);
274272

275-
final Map<String, Object> templateContext = createTemplateContext(
273+
final Map<String, Object?> templateContext = createTemplateContext(
276274
organization: organization,
277275
projectName: projectName,
278276
titleCaseProjectName: titleCaseProjectName,
@@ -432,12 +430,12 @@ Your $application code is in $relativeAppMain.
432430

433431
Future<int> _generateModule(
434432
Directory directory,
435-
Map<String, dynamic> templateContext, {
433+
Map<String, Object?> templateContext, {
436434
bool overwrite = false,
437435
bool printStatusWhenWriting = true,
438436
}) async {
439437
int generatedCount = 0;
440-
final String description = argResults.wasParsed('description')
438+
final String? description = argResults!.wasParsed('description')
441439
? stringArgDeprecated('description')
442440
: 'A new Flutter module project.';
443441
templateContext['description'] = description;
@@ -453,7 +451,6 @@ Your $application code is in $relativeAppMain.
453451
context: PubContext.create,
454452
directory: directory.path,
455453
offline: boolArgDeprecated('offline'),
456-
generateSyntheticPackage: false,
457454
);
458455
final FlutterProject project = FlutterProject.fromDirectory(directory);
459456
await project.ensureReadyForPlatformSpecificTooling(
@@ -466,12 +463,12 @@ Your $application code is in $relativeAppMain.
466463

467464
Future<int> _generatePackage(
468465
Directory directory,
469-
Map<String, dynamic> templateContext, {
466+
Map<String, Object?> templateContext, {
470467
bool overwrite = false,
471468
bool printStatusWhenWriting = true,
472469
}) async {
473470
int generatedCount = 0;
474-
final String description = argResults.wasParsed('description')
471+
final String? description = argResults!.wasParsed('description')
475472
? stringArgDeprecated('description')
476473
: 'A new Flutter package project.';
477474
templateContext['description'] = description;
@@ -487,21 +484,20 @@ Your $application code is in $relativeAppMain.
487484
context: PubContext.createPackage,
488485
directory: directory.path,
489486
offline: boolArgDeprecated('offline'),
490-
generateSyntheticPackage: false,
491487
);
492488
}
493489
return generatedCount;
494490
}
495491

496492
Future<int> _generateMethodChannelPlugin(
497493
Directory directory,
498-
Map<String, dynamic> templateContext, {
494+
Map<String, Object?> templateContext, {
499495
bool overwrite = false,
500496
bool printStatusWhenWriting = true,
501-
FlutterProjectType projectType,
497+
required FlutterProjectType projectType,
502498
}) async {
503499
// Plugins only add a platform if it was requested explicitly by the user.
504-
if (!argResults.wasParsed('platforms')) {
500+
if (!argResults!.wasParsed('platforms')) {
505501
for (final String platform in kAllCreatePlatforms) {
506502
templateContext[platform] = false;
507503
}
@@ -517,7 +513,7 @@ Your $application code is in $relativeAppMain.
517513
final bool willAddPlatforms = platformsToAdd.isNotEmpty;
518514
templateContext['no_platforms'] = !willAddPlatforms;
519515
int generatedCount = 0;
520-
final String description = argResults.wasParsed('description')
516+
final String? description = argResults!.wasParsed('description')
521517
? stringArgDeprecated('description')
522518
: 'A new Flutter plugin project.';
523519
templateContext['description'] = description;
@@ -534,7 +530,6 @@ Your $application code is in $relativeAppMain.
534530
context: PubContext.createPlugin,
535531
directory: directory.path,
536532
offline: boolArgDeprecated('offline'),
537-
generateSyntheticPackage: false,
538533
);
539534
}
540535

@@ -545,9 +540,9 @@ Your $application code is in $relativeAppMain.
545540
project: project, requireAndroidSdk: false);
546541
}
547542

548-
final String projectName = templateContext['projectName'] as String;
549-
final String organization = templateContext['organization'] as String;
550-
final String androidPluginIdentifier = templateContext['androidIdentifier'] as String;
543+
final String? projectName = templateContext['projectName'] as String?;
544+
final String organization = templateContext['organization']! as String; // Required to make the context.
545+
final String? androidPluginIdentifier = templateContext['androidIdentifier'] as String?;
551546
final String exampleProjectName = '${projectName}_example';
552547
templateContext['projectName'] = exampleProjectName;
553548
templateContext['androidIdentifier'] = CreateBase.createAndroidIdentifier(organization, exampleProjectName);
@@ -572,13 +567,13 @@ Your $application code is in $relativeAppMain.
572567

573568
Future<int> _generateFfiPlugin(
574569
Directory directory,
575-
Map<String, dynamic> templateContext, {
570+
Map<String, Object?> templateContext, {
576571
bool overwrite = false,
577572
bool printStatusWhenWriting = true,
578-
FlutterProjectType projectType,
573+
required FlutterProjectType projectType,
579574
}) async {
580575
// Plugins only add a platform if it was requested explicitly by the user.
581-
if (!argResults.wasParsed('platforms')) {
576+
if (!argResults!.wasParsed('platforms')) {
582577
for (final String platform in kAllCreatePlatforms) {
583578
templateContext[platform] = false;
584579
}
@@ -596,7 +591,7 @@ Your $application code is in $relativeAppMain.
596591
final bool willAddPlatforms = platformsToAdd.isNotEmpty;
597592
templateContext['no_platforms'] = !willAddPlatforms;
598593
int generatedCount = 0;
599-
final String description = argResults.wasParsed('description')
594+
final String? description = argResults!.wasParsed('description')
600595
? stringArgDeprecated('description')
601596
: 'A new Flutter FFI plugin project.';
602597
templateContext['description'] = description;
@@ -613,7 +608,6 @@ Your $application code is in $relativeAppMain.
613608
context: PubContext.createPlugin,
614609
directory: directory.path,
615610
offline: boolArgDeprecated('offline'),
616-
generateSyntheticPackage: false,
617611
);
618612
}
619613

@@ -623,9 +617,9 @@ Your $application code is in $relativeAppMain.
623617
gradle.updateLocalProperties(project: project, requireAndroidSdk: false);
624618
}
625619

626-
final String projectName = templateContext['projectName'] as String;
627-
final String organization = templateContext['organization'] as String;
628-
final String androidPluginIdentifier = templateContext['androidIdentifier'] as String;
620+
final String? projectName = templateContext['projectName'] as String?;
621+
final String organization = templateContext['organization']! as String; // Required to make the context.
622+
final String? androidPluginIdentifier = templateContext['androidIdentifier'] as String?;
629623
final String exampleProjectName = '${projectName}_example';
630624
templateContext['projectName'] = exampleProjectName;
631625
templateContext['androidIdentifier'] = CreateBase.createAndroidIdentifier(organization, exampleProjectName);
@@ -662,7 +656,7 @@ Your $application code is in $relativeAppMain.
662656
return -files.length;
663657
}
664658

665-
List<String> _getSupportedPlatformsFromTemplateContext(Map<String, dynamic> templateContext) {
659+
List<String> _getSupportedPlatformsFromTemplateContext(Map<String, Object?> templateContext) {
666660
return <String>[
667661
for (String platform in kAllCreatePlatforms)
668662
if (templateContext[platform] == true) platform,
@@ -671,7 +665,7 @@ Your $application code is in $relativeAppMain.
671665

672666
// Returns a list of platforms that are explicitly requested by user via `--platforms`.
673667
List<String> _getUserRequestedPlatforms() {
674-
if (!argResults.wasParsed('platforms')) {
668+
if (!argResults!.wasParsed('platforms')) {
675669
return <String>[];
676670
}
677671
return stringsArg('platforms');
@@ -682,10 +676,11 @@ Your $application code is in $relativeAppMain.
682676
// Determine what platforms are supported based on generated files.
683677
List<String> _getSupportedPlatformsInPlugin(Directory projectDir) {
684678
final String pubspecPath = globals.fs.path.join(projectDir.absolute.path, 'pubspec.yaml');
685-
final FlutterManifest manifest = FlutterManifest.createFromPath(pubspecPath, fileSystem: globals.fs, logger: globals.logger);
686-
final List<String> platforms = manifest.validSupportedPlatforms == null
679+
final FlutterManifest? manifest = FlutterManifest.createFromPath(pubspecPath, fileSystem: globals.fs, logger: globals.logger);
680+
final Map<String, Object?>? validSupportedPlatforms = manifest?.validSupportedPlatforms;
681+
final List<String> platforms = validSupportedPlatforms == null
687682
? <String>[]
688-
: manifest.validSupportedPlatforms.keys.toList();
683+
: validSupportedPlatforms.keys.toList();
689684
return platforms;
690685
}
691686

0 commit comments

Comments
 (0)