From 2cdd51900cb95c9cda92c9dd3d1a4ee7dd9beb31 Mon Sep 17 00:00:00 2001 From: Michael Thomsen Date: Tue, 30 Mar 2021 10:16:05 +0200 Subject: [PATCH] Enable null safety by default in templates (#78619) --- .../ios_host_app/flutterapp/lib/main | 9 +- .../ios_host_app/flutterapp/lib/marquee | 8 +- .../lib/src/commands/create.dart | 9 +- .../templates/app/lib/main.dart.tmpl | 6 +- .../templates/app/test/widget_test.dart.tmpl | 2 +- .../module/common/lib/main.dart.tmpl | 6 +- .../module/common/test/widget_test.dart.tmpl | 2 +- .../plugin/lib/projectName.dart.tmpl | 4 +- .../plugin/lib/projectName_web.dart.tmpl | 1 - .../templates/plugin/pubspec.yaml.tmpl | 2 +- .../commands.shard/permeable/create_test.dart | 19 ---- .../generated_plugin_registrant_test.dart | 2 +- .../test/integration.shard/migrate_test.dart | 106 ------------------ 13 files changed, 25 insertions(+), 151 deletions(-) delete mode 100644 packages/flutter_tools/test/integration.shard/migrate_test.dart diff --git a/dev/integration_tests/ios_host_app/flutterapp/lib/main b/dev/integration_tests/ios_host_app/flutterapp/lib/main index e169b03a0d5..d770b1cad10 100644 --- a/dev/integration_tests/ios_host_app/flutterapp/lib/main +++ b/dev/integration_tests/ios_host_app/flutterapp/lib/main @@ -38,13 +38,14 @@ void main() { run(ui.window.defaultRouteName); } -Future run(String name) async { +Future run(String? name) async { // The platform-specific component will call [setInitialRoute] on the Flutter // view (or view controller for iOS) to set [ui.window.defaultRouteName]. // We then dispatch based on the route names to show different Flutter // widgets. // Since we don't really care about Flutter-side navigation in this app, we're // not using a regular routes map. + name ??= ''; switch (name) { case greenMarqueeRouteName: runApp(Marquee(color: Colors.green[400])); @@ -62,7 +63,7 @@ Future run(String name) async { } class FlutterView extends StatelessWidget { - const FlutterView({@required this.initialRoute}); + const FlutterView({required this.initialRoute}); @override Widget build(BuildContext context) { @@ -76,7 +77,7 @@ class FlutterView extends StatelessWidget { } class MyHomePage extends StatefulWidget { - const MyHomePage({this.initialRoute}); + const MyHomePage({required this.initialRoute}); @override _MyHomePageState createState() => _MyHomePageState(); @@ -131,7 +132,7 @@ class _MyHomePageState extends State { /// Callback for messages sent by the platform-specific component. /// /// Increments our internal counter. - Future _handlePlatformIncrement(String message) async { + Future _handlePlatformIncrement(String? message) async { // Normally we'd dispatch based on the value of [message], but in this // sample, there is only one message that is sent to us. _incrementCounter(); diff --git a/dev/integration_tests/ios_host_app/flutterapp/lib/marquee b/dev/integration_tests/ios_host_app/flutterapp/lib/marquee index ad81d9c83bb..71375e4ad01 100644 --- a/dev/integration_tests/ios_host_app/flutterapp/lib/marquee +++ b/dev/integration_tests/ios_host_app/flutterapp/lib/marquee @@ -7,7 +7,7 @@ import 'package:flutter/animation.dart'; import 'package:flutter/services.dart'; class _MarqueeText extends AnimatedWidget { - const _MarqueeText({Key key, Animation animation}) + const _MarqueeText({Key? key, required Animation animation}) : super(key: key, listenable: animation); @override @@ -26,15 +26,15 @@ class _MarqueeText extends AnimatedWidget { class Marquee extends StatefulWidget { const Marquee({this.color}); - final Color color; + final Color? color; @override State createState() => MarqueeState(); } class MarqueeState extends State with SingleTickerProviderStateMixin { - AnimationController controller; - Animation animation; + late AnimationController controller; + late Animation animation; @override void initState() { diff --git a/packages/flutter_tools/lib/src/commands/create.dart b/packages/flutter_tools/lib/src/commands/create.dart index 2c482aba13a..83b6ac6f442 100644 --- a/packages/flutter_tools/lib/src/commands/create.dart +++ b/packages/flutter_tools/lib/src/commands/create.dart @@ -242,8 +242,8 @@ class CreateCommand extends CreateBase { macos: featureFlags.isMacOSEnabled && platforms.contains('macos'), windows: featureFlags.isWindowsEnabled && platforms.contains('windows'), windowsUwp: featureFlags.isWindowsUwpEnabled && platforms.contains('winuwp'), - // Enable null-safety for sample code, which is - unlike our regular templates - already migrated. - dartSdkVersionBounds: sampleCode != null ? '">=2.12.0-0 <3.0.0"' : '">=2.7.0 <3.0.0"' + // Enable null safety everywhere. + dartSdkVersionBounds: '">=2.12.0 <3.0.0"' ); final String relativeDirPath = globals.fs.path.relative(projectDirPath); @@ -325,11 +325,6 @@ In order to run your $application, type: \$ cd $relativeAppPath \$ flutter run -To enable null safety, type: - - \$ cd $relativeAppPath - \$ dart migrate --apply-changes - Your $application code is in $relativeAppMain. '''); // Show warning if any selected platform is not enabled diff --git a/packages/flutter_tools/templates/app/lib/main.dart.tmpl b/packages/flutter_tools/templates/app/lib/main.dart.tmpl index ee04c860914..225e9ca0990 100644 --- a/packages/flutter_tools/templates/app/lib/main.dart.tmpl +++ b/packages/flutter_tools/templates/app/lib/main.dart.tmpl @@ -35,7 +35,7 @@ class MyApp extends StatelessWidget { } class MyHomePage extends StatefulWidget { - MyHomePage({Key key, this.title}) : super(key: key); + MyHomePage({Key? key, required this.title}) : super(key: key); // This widget is the home page of your application. It is stateful, meaning // that it has a State object (defined below) that contains fields that affect @@ -138,8 +138,10 @@ class _MyAppState extends State { Future initPlatformState() async { String platformVersion; // Platform messages may fail, so we use a try/catch PlatformException. + // We also handle the message potentially returning null. try { - platformVersion = await {{pluginDartClass}}.platformVersion; + platformVersion = + await {{pluginDartClass}}.platformVersion ?? 'Unknown platform version'; } on PlatformException { platformVersion = 'Failed to get platform version.'; } diff --git a/packages/flutter_tools/templates/app/test/widget_test.dart.tmpl b/packages/flutter_tools/templates/app/test/widget_test.dart.tmpl index 76ccd69ab87..6cd94fc7769 100644 --- a/packages/flutter_tools/templates/app/test/widget_test.dart.tmpl +++ b/packages/flutter_tools/templates/app/test/widget_test.dart.tmpl @@ -40,7 +40,7 @@ void main() { expect( find.byWidgetPredicate( (Widget widget) => widget is Text && - widget.data.startsWith('Running on:'), + widget.data!.startsWith('Running on:'), ), findsOneWidget, ); diff --git a/packages/flutter_tools/templates/module/common/lib/main.dart.tmpl b/packages/flutter_tools/templates/module/common/lib/main.dart.tmpl index c814ee91106..bba5115f449 100644 --- a/packages/flutter_tools/templates/module/common/lib/main.dart.tmpl +++ b/packages/flutter_tools/templates/module/common/lib/main.dart.tmpl @@ -32,7 +32,7 @@ class MyApp extends StatelessWidget { } class MyHomePage extends StatefulWidget { - MyHomePage({Key key, this.title}) : super(key: key); + MyHomePage({Key? key, required this.title}) : super(key: key); // This widget is the home page of your application. It is stateful, meaning // that it has a State object (defined below) that contains fields that affect @@ -135,8 +135,10 @@ class _MyAppState extends State { Future initPlatformState() async { String platformVersion; // Platform messages may fail, so we use a try/catch PlatformException. + // We also handle the message potentially returning null. try { - platformVersion = await {{pluginDartClass}}.platformVersion; + platformVersion = + await {{pluginDartClass}}.platformVersion ?? 'Unknown platform version'; } on PlatformException { platformVersion = 'Failed to get platform version.'; } diff --git a/packages/flutter_tools/templates/module/common/test/widget_test.dart.tmpl b/packages/flutter_tools/templates/module/common/test/widget_test.dart.tmpl index 76ccd69ab87..6cd94fc7769 100644 --- a/packages/flutter_tools/templates/module/common/test/widget_test.dart.tmpl +++ b/packages/flutter_tools/templates/module/common/test/widget_test.dart.tmpl @@ -40,7 +40,7 @@ void main() { expect( find.byWidgetPredicate( (Widget widget) => widget is Text && - widget.data.startsWith('Running on:'), + widget.data!.startsWith('Running on:'), ), findsOneWidget, ); diff --git a/packages/flutter_tools/templates/plugin/lib/projectName.dart.tmpl b/packages/flutter_tools/templates/plugin/lib/projectName.dart.tmpl index 5ccb0c482e6..f2512112227 100644 --- a/packages/flutter_tools/templates/plugin/lib/projectName.dart.tmpl +++ b/packages/flutter_tools/templates/plugin/lib/projectName.dart.tmpl @@ -13,8 +13,8 @@ class {{pluginDartClass}} { static const MethodChannel _channel = const MethodChannel('{{projectName}}'); - static Future get platformVersion async { - final String version = await _channel.invokeMethod('getPlatformVersion'); + static Future get platformVersion async { + final String? version = await _channel.invokeMethod('getPlatformVersion'); return version; } } diff --git a/packages/flutter_tools/templates/plugin/lib/projectName_web.dart.tmpl b/packages/flutter_tools/templates/plugin/lib/projectName_web.dart.tmpl index 21135203ae6..de8221c9928 100644 --- a/packages/flutter_tools/templates/plugin/lib/projectName_web.dart.tmpl +++ b/packages/flutter_tools/templates/plugin/lib/projectName_web.dart.tmpl @@ -28,7 +28,6 @@ class {{pluginDartClass}}Web { switch (call.method) { case 'getPlatformVersion': return getPlatformVersion(); - break; default: throw PlatformException( code: 'Unimplemented', diff --git a/packages/flutter_tools/templates/plugin/pubspec.yaml.tmpl b/packages/flutter_tools/templates/plugin/pubspec.yaml.tmpl index e1b53e68727..c45b539cbe7 100644 --- a/packages/flutter_tools/templates/plugin/pubspec.yaml.tmpl +++ b/packages/flutter_tools/templates/plugin/pubspec.yaml.tmpl @@ -4,7 +4,7 @@ version: 0.0.1 homepage: environment: - sdk: ">=2.7.0 <3.0.0" + sdk: {{dartSdkVersionBounds}} flutter: ">=1.20.0" dependencies: diff --git a/packages/flutter_tools/test/commands.shard/permeable/create_test.dart b/packages/flutter_tools/test/commands.shard/permeable/create_test.dart index 002701f4427..ea6052798b8 100755 --- a/packages/flutter_tools/test/commands.shard/permeable/create_test.dart +++ b/packages/flutter_tools/test/commands.shard/permeable/create_test.dart @@ -2450,25 +2450,6 @@ void main() { FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true, isAndroidEnabled: false, isIOSEnabled: false), Logger: () => logger, }); - - testUsingContext('flutter create prints note about null safety', () async { - await _createProject( - projectDir, - [], - [], - ); - expect(logger.statusText, contains('dart migrate --apply-changes')); - }, overrides: { - Pub: () => Pub( - fileSystem: globals.fs, - logger: globals.logger, - processManager: globals.processManager, - usage: globals.flutterUsage, - botDetector: globals.botDetector, - platform: globals.platform, - ), - Logger: () => logger, - }); } Future _createProject( diff --git a/packages/flutter_tools/test/integration.shard/generated_plugin_registrant_test.dart b/packages/flutter_tools/test/integration.shard/generated_plugin_registrant_test.dart index 5cf1218b485..67ca031128f 100644 --- a/packages/flutter_tools/test/integration.shard/generated_plugin_registrant_test.dart +++ b/packages/flutter_tools/test/integration.shard/generated_plugin_registrant_test.dart @@ -47,7 +47,7 @@ void main() { // We need to add a dependency with web support to trigger // the generated_plugin_registrant generation. await _addDependency(projectDir, 'shared_preferences', - version: '^0.5.12+4'); + version: '^2.0.0'); await _analyzeProject(projectDir); expect( diff --git a/packages/flutter_tools/test/integration.shard/migrate_test.dart b/packages/flutter_tools/test/integration.shard/migrate_test.dart deleted file mode 100644 index 49f94dcc5bc..00000000000 --- a/packages/flutter_tools/test/integration.shard/migrate_test.dart +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright 2014 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// @dart = 2.8 - -import 'package:flutter_tools/src/base/file_system.dart'; -import 'package:flutter_tools/src/base/io.dart'; - -import '../src/common.dart'; -import 'test_utils.dart'; - -void main() { - /// Verifies that `dart migrate` will run successfully on the default `flutter create` - /// template. - group('dart migrate', () { - testWithoutContext('dart migrate succeeds on flutter create template', () async { - Directory tempDir; - try { - tempDir = await _createProject(tempDir); - await _migrate(tempDir); - await _analyze(tempDir); - } finally { - tempDir?.deleteSync(recursive: true); - } - }); - - /// Verifies that `dart migrate` will run successfully on the module template - /// used by `flutter create --template=module`. - testWithoutContext('dart migrate succeeds on module template', () async { - Directory tempDir; - try { - tempDir = await _createProject(tempDir, ['--template=module']); - await _migrate(tempDir); - await _analyze(tempDir); - } finally { - tempDir?.deleteSync(recursive: true); - } - }); - - /// Verifies that `dart migrate` will run successfully on the module template - /// used by `flutter create --template=plugin`. - testWithoutContext('dart migrate succeeds on plugin template', () async { - Directory tempDir; - try { - tempDir = await _createProject(tempDir, ['--template=plugin']); - await _migrate(tempDir); - await _analyze(tempDir); - } finally { - tempDir?.deleteSync(recursive: true); - } - }); - - /// Verifies that `dart migrate` will run successfully on the module template - /// used by `flutter create --template=package`. - testWithoutContext('dart migrate succeeds on package template', () async { - Directory tempDir; - try { - tempDir = await _createProject(tempDir, ['--template=package']); - await _migrate(tempDir); - await _analyze(tempDir); - } finally { - tempDir?.deleteSync(recursive: true); - } - }); - }, timeout: const Timeout(Duration(seconds: 90))); -} - -Future _createProject(Directory tempDir, [List extraAgs]) async { - tempDir = createResolvedTempDirectorySync('dart_migrate_test.'); - final ProcessResult createResult = await processManager.run([ - _flutterBin, - 'create', - if (extraAgs != null) - ...extraAgs, - 'foo', - ], workingDirectory: tempDir.path); - if (createResult.exitCode != 0) { - fail('flutter create did not work: ${createResult.stdout}${createResult.stderr}'); - } - return tempDir; -} - -Future _migrate(Directory tempDir) async { - final ProcessResult migrateResult = await processManager.run([ - _dartBin, - 'migrate', - '--apply-changes', - ], workingDirectory: fileSystem.path.join(tempDir.path, 'foo')); - if (migrateResult.exitCode != 0) { - fail('dart migrate did not work: ${migrateResult.stdout}${migrateResult.stderr}'); - } -} - -Future _analyze(Directory tempDir) async { - final ProcessResult analyzeResult = await processManager.run([ - _flutterBin, - 'analyze', - ], workingDirectory: fileSystem.path.join(tempDir.path, 'foo')); - if (analyzeResult.exitCode != 0) { - fail('flutter analyze had errors: ${analyzeResult.stdout}${analyzeResult.stderr}'); - } -} - -String get _flutterBin => fileSystem.path.join(getFlutterRoot(), 'bin', platform.isWindows ? 'flutter.bat' : 'flutter'); -String get _dartBin => fileSystem.path.join(getFlutterRoot(), 'bin', platform.isWindows ? 'dart.bat' : 'dart');