Unverified Commit 2cdd5190 authored by Michael Thomsen's avatar Michael Thomsen Committed by GitHub

Enable null safety by default in templates (#78619)

parent e9359047
......@@ -38,13 +38,14 @@ void main() {
run(ui.window.defaultRouteName);
}
Future<String> run(String name) async {
Future<String> 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<String> 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<MyHomePage> {
/// Callback for messages sent by the platform-specific component.
///
/// Increments our internal counter.
Future<String> _handlePlatformIncrement(String message) async {
Future<String> _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();
......
......@@ -7,7 +7,7 @@ import 'package:flutter/animation.dart';
import 'package:flutter/services.dart';
class _MarqueeText extends AnimatedWidget {
const _MarqueeText({Key key, Animation<double> animation})
const _MarqueeText({Key? key, required Animation<double> 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<StatefulWidget> createState() => MarqueeState();
}
class MarqueeState extends State<Marquee> with SingleTickerProviderStateMixin {
AnimationController controller;
Animation<double> animation;
late AnimationController controller;
late Animation<double> animation;
@override
void initState() {
......
......@@ -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
......
......@@ -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<MyApp> {
Future<void> 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.';
}
......
......@@ -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,
);
......
......@@ -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<MyApp> {
Future<void> 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.';
}
......
......@@ -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,
);
......
......@@ -13,8 +13,8 @@ class {{pluginDartClass}} {
static const MethodChannel _channel =
const MethodChannel('{{projectName}}');
static Future<String> get platformVersion async {
final String version = await _channel.invokeMethod('getPlatformVersion');
static Future<String?> get platformVersion async {
final String? version = await _channel.invokeMethod('getPlatformVersion');
return version;
}
}
......@@ -28,7 +28,6 @@ class {{pluginDartClass}}Web {
switch (call.method) {
case 'getPlatformVersion':
return getPlatformVersion();
break;
default:
throw PlatformException(
code: 'Unimplemented',
......
......@@ -4,7 +4,7 @@ version: 0.0.1
homepage:
environment:
sdk: ">=2.7.0 <3.0.0"
sdk: {{dartSdkVersionBounds}}
flutter: ">=1.20.0"
dependencies:
......
......@@ -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,
<String>[],
<String>[],
);
expect(logger.statusText, contains('dart migrate --apply-changes'));
}, overrides: <Type, Generator>{
Pub: () => Pub(
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
usage: globals.flutterUsage,
botDetector: globals.botDetector,
platform: globals.platform,
),
Logger: () => logger,
});
}
Future<void> _createProject(
......
......@@ -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(
......
// 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, <String>['--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, <String>['--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, <String>['--template=package']);
await _migrate(tempDir);
await _analyze(tempDir);
} finally {
tempDir?.deleteSync(recursive: true);
}
});
}, timeout: const Timeout(Duration(seconds: 90)));
}
Future<Directory> _createProject(Directory tempDir, [List<String> extraAgs]) async {
tempDir = createResolvedTempDirectorySync('dart_migrate_test.');
final ProcessResult createResult = await processManager.run(<String>[
_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<void> _migrate(Directory tempDir) async {
final ProcessResult migrateResult = await processManager.run(<String>[
_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<void> _analyze(Directory tempDir) async {
final ProcessResult analyzeResult = await processManager.run(<String>[
_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');
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment