mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
Enable null safety by default in templates (#78619)
This commit is contained in:
parent
e93590474d
commit
2cdd51900c
@ -38,13 +38,14 @@ void main() {
|
|||||||
run(ui.window.defaultRouteName);
|
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
|
// The platform-specific component will call [setInitialRoute] on the Flutter
|
||||||
// view (or view controller for iOS) to set [ui.window.defaultRouteName].
|
// view (or view controller for iOS) to set [ui.window.defaultRouteName].
|
||||||
// We then dispatch based on the route names to show different Flutter
|
// We then dispatch based on the route names to show different Flutter
|
||||||
// widgets.
|
// widgets.
|
||||||
// Since we don't really care about Flutter-side navigation in this app, we're
|
// Since we don't really care about Flutter-side navigation in this app, we're
|
||||||
// not using a regular routes map.
|
// not using a regular routes map.
|
||||||
|
name ??= '';
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case greenMarqueeRouteName:
|
case greenMarqueeRouteName:
|
||||||
runApp(Marquee(color: Colors.green[400]));
|
runApp(Marquee(color: Colors.green[400]));
|
||||||
@ -62,7 +63,7 @@ Future<String> run(String name) async {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class FlutterView extends StatelessWidget {
|
class FlutterView extends StatelessWidget {
|
||||||
const FlutterView({@required this.initialRoute});
|
const FlutterView({required this.initialRoute});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -76,7 +77,7 @@ class FlutterView extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class MyHomePage extends StatefulWidget {
|
class MyHomePage extends StatefulWidget {
|
||||||
const MyHomePage({this.initialRoute});
|
const MyHomePage({required this.initialRoute});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_MyHomePageState createState() => _MyHomePageState();
|
_MyHomePageState createState() => _MyHomePageState();
|
||||||
@ -131,7 +132,7 @@ class _MyHomePageState extends State<MyHomePage> {
|
|||||||
/// Callback for messages sent by the platform-specific component.
|
/// Callback for messages sent by the platform-specific component.
|
||||||
///
|
///
|
||||||
/// Increments our internal counter.
|
/// 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
|
// Normally we'd dispatch based on the value of [message], but in this
|
||||||
// sample, there is only one message that is sent to us.
|
// sample, there is only one message that is sent to us.
|
||||||
_incrementCounter();
|
_incrementCounter();
|
||||||
|
@ -7,7 +7,7 @@ import 'package:flutter/animation.dart';
|
|||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
class _MarqueeText extends AnimatedWidget {
|
class _MarqueeText extends AnimatedWidget {
|
||||||
const _MarqueeText({Key key, Animation<double> animation})
|
const _MarqueeText({Key? key, required Animation<double> animation})
|
||||||
: super(key: key, listenable: animation);
|
: super(key: key, listenable: animation);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -26,15 +26,15 @@ class _MarqueeText extends AnimatedWidget {
|
|||||||
class Marquee extends StatefulWidget {
|
class Marquee extends StatefulWidget {
|
||||||
const Marquee({this.color});
|
const Marquee({this.color});
|
||||||
|
|
||||||
final Color color;
|
final Color? color;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<StatefulWidget> createState() => MarqueeState();
|
State<StatefulWidget> createState() => MarqueeState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class MarqueeState extends State<Marquee> with SingleTickerProviderStateMixin {
|
class MarqueeState extends State<Marquee> with SingleTickerProviderStateMixin {
|
||||||
AnimationController controller;
|
late AnimationController controller;
|
||||||
Animation<double> animation;
|
late Animation<double> animation;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
@ -242,8 +242,8 @@ class CreateCommand extends CreateBase {
|
|||||||
macos: featureFlags.isMacOSEnabled && platforms.contains('macos'),
|
macos: featureFlags.isMacOSEnabled && platforms.contains('macos'),
|
||||||
windows: featureFlags.isWindowsEnabled && platforms.contains('windows'),
|
windows: featureFlags.isWindowsEnabled && platforms.contains('windows'),
|
||||||
windowsUwp: featureFlags.isWindowsUwpEnabled && platforms.contains('winuwp'),
|
windowsUwp: featureFlags.isWindowsUwpEnabled && platforms.contains('winuwp'),
|
||||||
// Enable null-safety for sample code, which is - unlike our regular templates - already migrated.
|
// Enable null safety everywhere.
|
||||||
dartSdkVersionBounds: sampleCode != null ? '">=2.12.0-0 <3.0.0"' : '">=2.7.0 <3.0.0"'
|
dartSdkVersionBounds: '">=2.12.0 <3.0.0"'
|
||||||
);
|
);
|
||||||
|
|
||||||
final String relativeDirPath = globals.fs.path.relative(projectDirPath);
|
final String relativeDirPath = globals.fs.path.relative(projectDirPath);
|
||||||
@ -325,11 +325,6 @@ In order to run your $application, type:
|
|||||||
\$ cd $relativeAppPath
|
\$ cd $relativeAppPath
|
||||||
\$ flutter run
|
\$ flutter run
|
||||||
|
|
||||||
To enable null safety, type:
|
|
||||||
|
|
||||||
\$ cd $relativeAppPath
|
|
||||||
\$ dart migrate --apply-changes
|
|
||||||
|
|
||||||
Your $application code is in $relativeAppMain.
|
Your $application code is in $relativeAppMain.
|
||||||
''');
|
''');
|
||||||
// Show warning if any selected platform is not enabled
|
// Show warning if any selected platform is not enabled
|
||||||
|
@ -35,7 +35,7 @@ class MyApp extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class MyHomePage extends StatefulWidget {
|
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
|
// 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
|
// 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 {
|
Future<void> initPlatformState() async {
|
||||||
String platformVersion;
|
String platformVersion;
|
||||||
// Platform messages may fail, so we use a try/catch PlatformException.
|
// Platform messages may fail, so we use a try/catch PlatformException.
|
||||||
|
// We also handle the message potentially returning null.
|
||||||
try {
|
try {
|
||||||
platformVersion = await {{pluginDartClass}}.platformVersion;
|
platformVersion =
|
||||||
|
await {{pluginDartClass}}.platformVersion ?? 'Unknown platform version';
|
||||||
} on PlatformException {
|
} on PlatformException {
|
||||||
platformVersion = 'Failed to get platform version.';
|
platformVersion = 'Failed to get platform version.';
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ void main() {
|
|||||||
expect(
|
expect(
|
||||||
find.byWidgetPredicate(
|
find.byWidgetPredicate(
|
||||||
(Widget widget) => widget is Text &&
|
(Widget widget) => widget is Text &&
|
||||||
widget.data.startsWith('Running on:'),
|
widget.data!.startsWith('Running on:'),
|
||||||
),
|
),
|
||||||
findsOneWidget,
|
findsOneWidget,
|
||||||
);
|
);
|
||||||
|
@ -32,7 +32,7 @@ class MyApp extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class MyHomePage extends StatefulWidget {
|
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
|
// 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
|
// 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 {
|
Future<void> initPlatformState() async {
|
||||||
String platformVersion;
|
String platformVersion;
|
||||||
// Platform messages may fail, so we use a try/catch PlatformException.
|
// Platform messages may fail, so we use a try/catch PlatformException.
|
||||||
|
// We also handle the message potentially returning null.
|
||||||
try {
|
try {
|
||||||
platformVersion = await {{pluginDartClass}}.platformVersion;
|
platformVersion =
|
||||||
|
await {{pluginDartClass}}.platformVersion ?? 'Unknown platform version';
|
||||||
} on PlatformException {
|
} on PlatformException {
|
||||||
platformVersion = 'Failed to get platform version.';
|
platformVersion = 'Failed to get platform version.';
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ void main() {
|
|||||||
expect(
|
expect(
|
||||||
find.byWidgetPredicate(
|
find.byWidgetPredicate(
|
||||||
(Widget widget) => widget is Text &&
|
(Widget widget) => widget is Text &&
|
||||||
widget.data.startsWith('Running on:'),
|
widget.data!.startsWith('Running on:'),
|
||||||
),
|
),
|
||||||
findsOneWidget,
|
findsOneWidget,
|
||||||
);
|
);
|
||||||
|
@ -13,8 +13,8 @@ class {{pluginDartClass}} {
|
|||||||
static const MethodChannel _channel =
|
static const MethodChannel _channel =
|
||||||
const MethodChannel('{{projectName}}');
|
const MethodChannel('{{projectName}}');
|
||||||
|
|
||||||
static Future<String> get platformVersion async {
|
static Future<String?> get platformVersion async {
|
||||||
final String version = await _channel.invokeMethod('getPlatformVersion');
|
final String? version = await _channel.invokeMethod('getPlatformVersion');
|
||||||
return version;
|
return version;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,6 @@ class {{pluginDartClass}}Web {
|
|||||||
switch (call.method) {
|
switch (call.method) {
|
||||||
case 'getPlatformVersion':
|
case 'getPlatformVersion':
|
||||||
return getPlatformVersion();
|
return getPlatformVersion();
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
throw PlatformException(
|
throw PlatformException(
|
||||||
code: 'Unimplemented',
|
code: 'Unimplemented',
|
||||||
|
@ -4,7 +4,7 @@ version: 0.0.1
|
|||||||
homepage:
|
homepage:
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.7.0 <3.0.0"
|
sdk: {{dartSdkVersionBounds}}
|
||||||
flutter: ">=1.20.0"
|
flutter: ">=1.20.0"
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -2450,25 +2450,6 @@ void main() {
|
|||||||
FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true, isAndroidEnabled: false, isIOSEnabled: false),
|
FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true, isAndroidEnabled: false, isIOSEnabled: false),
|
||||||
Logger: () => logger,
|
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(
|
Future<void> _createProject(
|
||||||
|
@ -47,7 +47,7 @@ void main() {
|
|||||||
// We need to add a dependency with web support to trigger
|
// We need to add a dependency with web support to trigger
|
||||||
// the generated_plugin_registrant generation.
|
// the generated_plugin_registrant generation.
|
||||||
await _addDependency(projectDir, 'shared_preferences',
|
await _addDependency(projectDir, 'shared_preferences',
|
||||||
version: '^0.5.12+4');
|
version: '^2.0.0');
|
||||||
await _analyzeProject(projectDir);
|
await _analyzeProject(projectDir);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
|
@ -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, <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');
|
|
Loading…
Reference in New Issue
Block a user