Revert "Migrate core devicelab framework to null safety. (#85993)" (#86269)

This reverts commit 2175e64e4fdb387c5f01242c9a82779d3423e531.
diff --git a/dev/devicelab/lib/command/test.dart b/dev/devicelab/lib/command/test.dart
index 9e35fdd..feab3ca 100644
--- a/dev/devicelab/lib/command/test.dart
+++ b/dev/devicelab/lib/command/test.dart
@@ -2,6 +2,8 @@
 // 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:args/command_runner.dart';
 
 import 'package:flutter_devicelab/framework/runner.dart';
@@ -62,19 +64,19 @@
 
   @override
   Future<void> run() async {
-    final List<String> taskArgsRaw = argResults!['task-args'] as List<String>;
+    final List<String> taskArgsRaw = argResults['task-args'] as List<String>;
     // Prepend '--' to convert args to options when passed to task
     final List<String> taskArgs = taskArgsRaw.map((String taskArg) => '--$taskArg').toList();
     print(taskArgs);
     await runTasks(
-      <String>[argResults!['task'] as String],
-      deviceId: argResults!['device-id'] as String?,
-      gitBranch: argResults!['git-branch'] as String?,
-      localEngine: argResults!['local-engine'] as String?,
-      localEngineSrcPath: argResults!['local-engine-src-path'] as String?,
-      luciBuilder: argResults!['luci-builder'] as String?,
-      resultsPath: argResults!['results-file'] as String?,
-      silent: (argResults!['silent'] as bool?) ?? false,
+      <String>[argResults['task'] as String],
+      deviceId: argResults['device-id'] as String,
+      gitBranch: argResults['git-branch'] as String,
+      localEngine: argResults['local-engine'] as String,
+      localEngineSrcPath: argResults['local-engine-src-path'] as String,
+      luciBuilder: argResults['luci-builder'] as String,
+      resultsPath: argResults['results-file'] as String,
+      silent: argResults['silent'] as bool,
       taskArgs: taskArgs,
     );
   }
diff --git a/dev/devicelab/lib/command/upload_metrics.dart b/dev/devicelab/lib/command/upload_metrics.dart
index 89742cb..f44da32 100644
--- a/dev/devicelab/lib/command/upload_metrics.dart
+++ b/dev/devicelab/lib/command/upload_metrics.dart
@@ -2,6 +2,8 @@
 // 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:args/command_runner.dart';
 
 import '../framework/cocoon.dart';
@@ -23,8 +25,8 @@
 
   @override
   Future<void> run() async {
-    final String resultsPath = argResults!['results-file'] as String;
-    final String? serviceAccountTokenFile = argResults!['service-account-token-file'] as String?;
+    final String resultsPath = argResults['results-file'] as String;
+    final String serviceAccountTokenFile = argResults['service-account-token-file'] as String;
 
     final Cocoon cocoon = Cocoon(serviceAccountTokenPath: serviceAccountTokenFile);
     return cocoon.sendResultsPath(resultsPath);
diff --git a/dev/devicelab/lib/framework/ab.dart b/dev/devicelab/lib/framework/ab.dart
index 4f214f9..af3c737 100644
--- a/dev/devicelab/lib/framework/ab.dart
+++ b/dev/devicelab/lib/framework/ab.dart
@@ -2,7 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// @dart = 2.8
+
 import 'dart:math' as math;
+import 'package:meta/meta.dart';
 
 import 'task_result.dart';
 
@@ -40,8 +43,8 @@
   final String localEngine;
   final String taskName;
   final DateTime runStart;
-  DateTime? _runEnd;
-  DateTime? get runEnd => _runEnd;
+  DateTime _runEnd;
+  DateTime get runEnd => _runEnd;
 
   final Map<String, List<double>> _aResults;
   final Map<String, List<double>> _bResults;
@@ -88,15 +91,15 @@
     kLocalEngineKeyName:       localEngine,
     kTaskNameKeyName:          taskName,
     kRunStartKeyName:          runStart.toIso8601String(),
-    kRunEndKeyName:            runEnd!.toIso8601String(),
+    kRunEndKeyName:            runEnd.toIso8601String(),
     kAResultsKeyName:          _aResults,
     kBResultsKeyName:          _bResults,
   };
 
-  static void updateColumnLengths(List<int> lengths, List<String?> results) {
+  static void updateColumnLengths(List<int> lengths, List<String> results) {
     for (int column = 0; column < lengths.length; column++) {
       if (results[column] != null) {
-        lengths[column] = math.max(lengths[column], results[column]?.length ?? 0);
+        lengths[column] = math.max(lengths[column], results[column].length);
       }
     }
   }
@@ -104,10 +107,10 @@
   static void formatResult(StringBuffer buffer,
                            List<int> lengths,
                            List<FieldJustification> aligns,
-                           List<String?> values) {
+                           List<String> values) {
     for (int column = 0; column < lengths.length; column++) {
       final int len = lengths[column];
-      String? value = values[column];
+      String value = values[column];
       if (value == null) {
         value = ''.padRight(len);
       } else {
@@ -139,9 +142,9 @@
     final Map<String, _ScoreSummary> summariesA = _summarize(_aResults);
     final Map<String, _ScoreSummary> summariesB = _summarize(_bResults);
 
-    final List<List<String?>> tableRows = <List<String?>>[
+    final List<List<String>> tableRows = <List<String>>[
       for (final String scoreKey in <String>{...summariesA.keys, ...summariesB.keys})
-        <String?>[
+        <String>[
           scoreKey,
           summariesA[scoreKey]?.averageString, summariesA[scoreKey]?.noiseString,
           summariesB[scoreKey]?.averageString, summariesB[scoreKey]?.noiseString,
@@ -164,7 +167,7 @@
 
     final List<int> lengths = List<int>.filled(6, 0);
     updateColumnLengths(lengths, titles);
-    for (final List<String?> row in tableRows) {
+    for (final List<String> row in tableRows) {
       updateColumnLengths(lengths, row);
     }
 
@@ -174,7 +177,7 @@
           FieldJustification.CENTER,
           ...alignments.skip(1),
         ], titles);
-    for (final List<String?> row in tableRows) {
+    for (final List<String> row in tableRows) {
       formatResult(buffer, lengths, alignments, row);
     }
 
@@ -189,7 +192,7 @@
       buffer.writeln('$scoreKey:');
       buffer.write('  A:\t');
       if (_aResults.containsKey(scoreKey)) {
-        for (final double score in _aResults[scoreKey]!) {
+        for (final double score in _aResults[scoreKey]) {
           buffer.write('${score.toStringAsFixed(2)}\t');
         }
       } else {
@@ -199,7 +202,7 @@
 
       buffer.write('  B:\t');
       if (_bResults.containsKey(scoreKey)) {
-        for (final double score in _bResults[scoreKey]!) {
+        for (final double score in _bResults[scoreKey]) {
           buffer.write('${score.toStringAsFixed(2)}\t');
         }
       } else {
@@ -229,8 +232,8 @@
     );
 
     for (final String scoreKey in _allScoreKeys) {
-      final _ScoreSummary? summaryA = summariesA[scoreKey];
-      final _ScoreSummary? summaryB = summariesB[scoreKey];
+      final _ScoreSummary summaryA = summariesA[scoreKey];
+      final _ScoreSummary summaryB = summariesB[scoreKey];
       buffer.write('$scoreKey\t');
 
       if (summaryA != null) {
@@ -258,8 +261,8 @@
 
 class _ScoreSummary {
   _ScoreSummary({
-    required this.average,
-    required this.noise,
+    @required this.average,
+    @required this.noise,
   });
 
   /// Average (arithmetic mean) of a series of values collected by a benchmark.
@@ -272,14 +275,14 @@
   String get averageString => average.toStringAsFixed(2);
   String get noiseString => '(${_ratioToPercent(noise)})';
 
-  String improvementOver(_ScoreSummary? other) {
+  String improvementOver(_ScoreSummary other) {
     return other == null ? '' : '${(average / other.average).toStringAsFixed(2)}x';
   }
 }
 
 void _addResult(TaskResult result, Map<String, List<double>> results) {
-  for (final String scoreKey in result.benchmarkScoreKeys ?? <String>[]) {
-    final double score = (result.data![scoreKey] as num).toDouble();
+  for (final String scoreKey in result.benchmarkScoreKeys) {
+    final double score = (result.data[scoreKey] as num).toDouble();
     results.putIfAbsent(scoreKey, () => <double>[]).add(score);
   }
 }
diff --git a/dev/devicelab/lib/framework/apk_utils.dart b/dev/devicelab/lib/framework/apk_utils.dart
index 2baec6c..2def05b 100644
--- a/dev/devicelab/lib/framework/apk_utils.dart
+++ b/dev/devicelab/lib/framework/apk_utils.dart
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// @dart = 2.8
+
 import 'dart:io';
 
 import 'package:path/path.dart' as path;
@@ -104,7 +106,7 @@
 
 /// The Android home directory.
 String get _androidHome {
-  final String? androidHome = Platform.environment['ANDROID_HOME'] ??
+  final String androidHome = Platform.environment['ANDROID_HOME'] ??
       Platform.environment['ANDROID_SDK_ROOT'];
   if (androidHome == null || androidHome.isEmpty) {
     throw Exception('Environment variable `ANDROID_SDK_ROOT` is not set.');
@@ -116,9 +118,9 @@
 Future<String> _evalApkAnalyzer(
   List<String> args, {
   bool printStdout = false,
-  String? workingDirectory,
+  String workingDirectory,
 }) async {
-  final String? javaHome = await findJavaHome();
+  final String javaHome = await findJavaHome();
   if (javaHome == null || javaHome.isEmpty) {
     throw Exception('No JAVA_HOME set.');
   }
@@ -257,7 +259,7 @@
   String get androidPath => path.join(rootPath, 'android');
   String get iosPath => path.join(rootPath, 'ios');
 
-  Future<void> addCustomBuildType(String name, {required String initWith}) async {
+  Future<void> addCustomBuildType(String name, {String initWith}) async {
     final File buildScript = File(
       path.join(androidPath, 'app', 'build.gradle'),
     );
@@ -274,7 +276,7 @@
     ''');
   }
 
-  Future<void> addGlobalBuildType(String name, {required String initWith}) async {
+  Future<void> addGlobalBuildType(String name, {String initWith}) async {
     final File buildScript = File(
       path.join(androidPath, 'build.gradle'),
     );
@@ -358,11 +360,11 @@
     pubspec.writeAsStringSync(newContents);
   }
 
-  Future<void> runGradleTask(String task, {List<String>? options}) async {
+  Future<void> runGradleTask(String task, {List<String> options}) async {
     return _runGradleTask(workingDirectory: androidPath, task: task, options: options);
   }
 
-  Future<ProcessResult> resultOfGradleTask(String task, {List<String>? options}) {
+  Future<ProcessResult> resultOfGradleTask(String task, {List<String> options}) {
     return _resultOfGradleTask(workingDirectory: androidPath, task: task, options: options);
   }
 
@@ -414,11 +416,7 @@
   String get rootPath => path.join(parent.path, name);
 }
 
-Future<void> _runGradleTask({
-  required String workingDirectory,
-  required String task,
-  List<String>? options,
-}) async {
+Future<void> _runGradleTask({String workingDirectory, String task, List<String> options}) async {
   final ProcessResult result = await _resultOfGradleTask(
       workingDirectory: workingDirectory,
       task: task,
@@ -433,13 +431,10 @@
     throw 'Gradle exited with error';
 }
 
-Future<ProcessResult> _resultOfGradleTask({
-  required String workingDirectory,
-  required String task,
-  List<String>? options,
-}) async {
+Future<ProcessResult> _resultOfGradleTask({String workingDirectory, String task,
+    List<String> options}) async {
   section('Find Java');
-  final String? javaHome = await findJavaHome();
+  final String javaHome = await findJavaHome();
 
   if (javaHome == null)
     throw TaskResult.failure('Could not find Java');
@@ -470,7 +465,7 @@
 }
 
 /// Returns [null] if target matches [expectedTarget], otherwise returns an error message.
-String? validateSnapshotDependency(FlutterProject project, String expectedTarget) {
+String validateSnapshotDependency(FlutterProject project, String expectedTarget) {
   final File snapshotBlob = File(
       path.join(project.rootPath, 'build', 'app', 'intermediates',
           'flutter', 'debug', 'flutter_build.d'));
diff --git a/dev/devicelab/lib/framework/browser.dart b/dev/devicelab/lib/framework/browser.dart
index 3b65e83..9d2f544 100644
--- a/dev/devicelab/lib/framework/browser.dart
+++ b/dev/devicelab/lib/framework/browser.dart
@@ -2,12 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// @dart = 2.8
+
 import 'dart:async';
 import 'dart:convert' show json, utf8, LineSplitter, JsonEncoder;
 import 'dart:io' as io;
 import 'dart:math' as math;
 
 import 'package:flutter_devicelab/common.dart';
+import 'package:meta/meta.dart';
 import 'package:path/path.dart' as path;
 import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart';
 
@@ -29,10 +32,10 @@
   });
 
   /// If not null passed as `--user-data-dir`.
-  final String? userDataDirectory;
+  final String userDataDirectory;
 
   /// If not null launches a Chrome tab at this URL.
-  final String? url;
+  final String url;
 
   /// The width of the Chrome window.
   ///
@@ -46,14 +49,14 @@
 
   /// Launches code in "headless" mode, which allows running Chrome in
   /// environments without a display, such as LUCI and Cirrus.
-  final bool? headless;
+  final bool headless;
 
   /// The port Chrome will use for its debugging protocol.
   ///
   /// If null, Chrome is launched without debugging. When running in headless
   /// mode without a debug port, Chrome quits immediately. For most tests it is
   /// typical to set [headless] to true and set a non-null debug port.
-  final int? debugPort;
+  final int debugPort;
 }
 
 /// A function called when the Chrome process encounters an error.
@@ -76,7 +79,7 @@
   /// The [onError] callback is called with an error message when the Chrome
   /// process encounters an error. In particular, [onError] is called when the
   /// Chrome process exits prematurely, i.e. before [stop] is called.
-  static Future<Chrome> launch(ChromeOptions options, { String? workingDirectory, required ChromeErrorCallback onError }) async {
+  static Future<Chrome> launch(ChromeOptions options, { String workingDirectory, @required ChromeErrorCallback onError }) async {
     if (!io.Platform.isWindows) {
       final io.ProcessResult versionResult = io.Process.runSync(_findSystemChromeExecutable(), const <String>['--version']);
       print('Launching ${versionResult.stdout}');
@@ -89,10 +92,10 @@
       if (options.userDataDirectory != null)
         '--user-data-dir=${options.userDataDirectory}',
       if (options.url != null)
-        options.url!,
+        options.url,
       if (io.Platform.environment['CHROME_NO_SANDBOX'] == 'true')
         '--no-sandbox',
-      if (options.headless == true)
+      if (options.headless)
         '--headless',
       if (withDebugging)
         '--remote-debugging-port=${options.debugPort}',
@@ -113,9 +116,9 @@
       workingDirectory: workingDirectory,
     );
 
-    WipConnection? debugConnection;
+    WipConnection debugConnection;
     if (withDebugging) {
-      debugConnection = await _connectToChromeDebugPort(chromeProcess, options.debugPort!);
+      debugConnection = await _connectToChromeDebugPort(chromeProcess, options.debugPort);
     }
 
     return Chrome._(chromeProcess, onError, debugConnection);
@@ -123,12 +126,12 @@
 
   final io.Process _chromeProcess;
   final ChromeErrorCallback _onError;
-  final WipConnection? _debugConnection;
+  final WipConnection _debugConnection;
   bool _isStopped = false;
 
-  Completer<void> ?_tracingCompleter;
-  StreamSubscription<WipEvent>? _tracingSubscription;
-  List<Map<String, dynamic>>? _tracingData;
+  Completer<void> _tracingCompleter;
+  StreamSubscription<WipEvent> _tracingSubscription;
+  List<Map<String, dynamic>> _tracingData;
 
   /// Starts recording a performance trace.
   ///
@@ -148,24 +151,24 @@
 
     // Subscribe to tracing events prior to calling "Tracing.start". Otherwise,
     // we'll miss tracing data.
-    _tracingSubscription = _debugConnection?.onNotification.listen((WipEvent event) {
+    _tracingSubscription = _debugConnection.onNotification.listen((WipEvent event) {
       // We receive data as a sequence of "Tracing.dataCollected" followed by
       // "Tracing.tracingComplete" at the end. Until "Tracing.tracingComplete"
       // is received, the data may be incomplete.
       if (event.method == 'Tracing.tracingComplete') {
-        _tracingCompleter!.complete();
-        _tracingSubscription!.cancel();
+        _tracingCompleter.complete();
+        _tracingSubscription.cancel();
         _tracingSubscription = null;
       } else if (event.method == 'Tracing.dataCollected') {
-        final dynamic value = event.params?['value'];
+        final dynamic value = event.params['value'];
         if (value is! List) {
           throw FormatException('"Tracing.dataCollected" returned malformed data. '
               'Expected a List but got: ${value.runtimeType}');
         }
-        _tracingData?.addAll((event.params?['value'] as List<dynamic>).cast<Map<String, dynamic>>());
+        _tracingData.addAll((event.params['value'] as List<dynamic>).cast<Map<String, dynamic>>());
       }
     });
-    await _debugConnection?.sendCommand('Tracing.start', <String, dynamic>{
+    await _debugConnection.sendCommand('Tracing.start', <String, dynamic>{
       // The choice of categories is as follows:
       //
       // blink:
@@ -187,23 +190,22 @@
   /// Stops a performance tracing session started by [beginRecordingPerformance].
   ///
   /// Returns all the collected tracing data unfiltered.
-  Future<List<Map<String, dynamic>>?> endRecordingPerformance() async {
-    await _debugConnection!.sendCommand('Tracing.end');
-    await _tracingCompleter!.future;
-    final List<Map<String, dynamic>>? data = _tracingData;
+  Future<List<Map<String, dynamic>>> endRecordingPerformance() async {
+    await _debugConnection.sendCommand('Tracing.end');
+    await _tracingCompleter.future;
+    final List<Map<String, dynamic>> data = _tracingData;
     _tracingCompleter = null;
     _tracingData = null;
     return data;
   }
 
   Future<void> reloadPage({bool ignoreCache = false}) async {
-    await _debugConnection?.page.reload(ignoreCache: ignoreCache);
+    await _debugConnection.page.reload(ignoreCache: ignoreCache);
   }
 
   /// Stops the Chrome process.
   void stop() {
     _isStopped = true;
-    _tracingSubscription?.cancel();
     _chromeProcess.kill();
   }
 }
@@ -212,7 +214,7 @@
   // On some environments, such as the Dart HHH tester, Chrome resides in a
   // non-standard location and is provided via the following environment
   // variable.
-  final String? envExecutable = io.Platform.environment['CHROME_EXECUTABLE'];
+  final String envExecutable = io.Platform.environment['CHROME_EXECUTABLE'];
   if (envExecutable != null) {
     return envExecutable;
   }
@@ -230,12 +232,15 @@
     return '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome';
   } else if (io.Platform.isWindows) {
     const String kWindowsExecutable = r'Google\Chrome\Application\chrome.exe';
-    final List<String> kWindowsPrefixes = <String?>[
+    final List<String> kWindowsPrefixes = <String>[
       io.Platform.environment['LOCALAPPDATA'],
       io.Platform.environment['PROGRAMFILES'],
       io.Platform.environment['PROGRAMFILES(X86)'],
-    ].whereType<String>().toList();
+    ];
     final String windowsPrefix = kWindowsPrefixes.firstWhere((String prefix) {
+      if (prefix == null) {
+        return false;
+      }
       final String expectedPath = path.join(prefix, kWindowsExecutable);
       return io.File(expectedPath).existsSync();
     }, orElse: () => '.');
@@ -264,7 +269,7 @@
   final io.HttpClient client = io.HttpClient();
   final io.HttpClientRequest request = await client.getUrl(base.resolve('/json/list'));
   final io.HttpClientResponse response = await request.close();
-  final List<dynamic>? jsonObject = await json.fuse(utf8).decoder.bind(response).single as List<dynamic>?;
+  final List<dynamic> jsonObject = await json.fuse(utf8).decoder.bind(response).single as List<dynamic>;
   if (jsonObject == null || jsonObject.isEmpty) {
     return base;
   }
@@ -274,17 +279,17 @@
 /// Summarizes a Blink trace down to a few interesting values.
 class BlinkTraceSummary {
   BlinkTraceSummary._({
-    required this.averageBeginFrameTime,
-    required this.averageUpdateLifecyclePhasesTime,
+    @required this.averageBeginFrameTime,
+    @required this.averageUpdateLifecyclePhasesTime,
   }) : averageTotalUIFrameTime = averageBeginFrameTime + averageUpdateLifecyclePhasesTime;
 
-  static BlinkTraceSummary? fromJson(List<Map<String, dynamic>> traceJson) {
+  static BlinkTraceSummary fromJson(List<Map<String, dynamic>> traceJson) {
     try {
       // Convert raw JSON data to BlinkTraceEvent objects sorted by timestamp.
       List<BlinkTraceEvent> events = traceJson
         .map<BlinkTraceEvent>(BlinkTraceEvent.fromJson)
         .toList()
-        ..sort((BlinkTraceEvent a, BlinkTraceEvent b) => a.ts! - b.ts!);
+        ..sort((BlinkTraceEvent a, BlinkTraceEvent b) => a.ts - b.ts);
 
       Exception noMeasuredFramesFound() => Exception(
         'No measured frames found in benchmark tracing data. This likely '
@@ -311,7 +316,7 @@
         return null;
       }
 
-      final int tabPid = firstMeasuredFrameEvent.pid!;
+      final int tabPid = firstMeasuredFrameEvent.pid;
 
       // Filter out data from unrelated processes
       events = events.where((BlinkTraceEvent element) => element.pid == tabPid).toList();
@@ -347,8 +352,8 @@
 
       // Compute averages and summarize.
       return BlinkTraceSummary._(
-        averageBeginFrameTime: _computeAverageDuration(frames.map((BlinkFrame frame) => frame.beginFrame).whereType<BlinkTraceEvent>().toList()),
-        averageUpdateLifecyclePhasesTime: _computeAverageDuration(frames.map((BlinkFrame frame) => frame.updateAllLifecyclePhases).whereType<BlinkTraceEvent>().toList()),
+        averageBeginFrameTime: _computeAverageDuration(frames.map((BlinkFrame frame) => frame.beginFrame).toList()),
+        averageUpdateLifecyclePhasesTime: _computeAverageDuration(frames.map((BlinkFrame frame) => frame.updateAllLifecyclePhases).toList()),
       );
     } catch (_, __) {
       final io.File traceFile = io.File('./chrome-trace.json');
@@ -387,16 +392,16 @@
 /// Contains events pertaining to a single frame in the Blink trace data.
 class BlinkFrame {
   /// Corresponds to 'WebViewImpl::beginFrame' event.
-  BlinkTraceEvent? beginFrame;
+  BlinkTraceEvent beginFrame;
 
   /// Corresponds to 'WebViewImpl::updateAllLifecyclePhases' event.
-  BlinkTraceEvent? updateAllLifecyclePhases;
+  BlinkTraceEvent updateAllLifecyclePhases;
 
   /// Corresponds to 'measured_frame' begin event.
-  BlinkTraceEvent? beginMeasuredFrame;
+  BlinkTraceEvent beginMeasuredFrame;
 
   /// Corresponds to 'measured_frame' end event.
-  BlinkTraceEvent? endMeasuredFrame;
+  BlinkTraceEvent endMeasuredFrame;
 }
 
 /// Takes a list of events that have non-null [BlinkTraceEvent.tdur] computes
@@ -409,7 +414,7 @@
       if (event.tdur == null) {
         throw FormatException('Trace event lacks "tdur" field: $event');
       }
-      return previousValue + event.tdur!;
+      return previousValue + event.tdur;
     });
   final int sampleCount = math.min(events.length, _kMeasuredSampleCount);
   return Duration(microseconds: sum ~/ sampleCount);
@@ -421,15 +426,15 @@
 ///  * https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview
 class BlinkTraceEvent {
   BlinkTraceEvent._({
-    required this.args,
-    required this.cat,
-    required this.name,
-    required this.ph,
-    this.pid,
-    this.tid,
-    this.ts,
-    this.tts,
-    this.tdur,
+    @required this.args,
+    @required this.cat,
+    @required this.name,
+    @required this.ph,
+    @required this.pid,
+    @required this.tid,
+    @required this.ts,
+    @required this.tts,
+    @required this.tdur,
   });
 
   /// Parses an event from its JSON representation.
@@ -483,19 +488,19 @@
   final String ph;
 
   /// Process ID of the process that emitted the event.
-  final int? pid;
+  final int pid;
 
   /// Thread ID of the thread that emitted the event.
-  final int? tid;
+  final int tid;
 
   /// Timestamp in microseconds using tracer clock.
-  final int? ts;
+  final int ts;
 
   /// Timestamp in microseconds using thread clock.
-  final int? tts;
+  final int tts;
 
   /// Event duration in microseconds.
-  final int? tdur;
+  final int tdur;
 
   /// A "begin frame" event contains all of the scripting time of an animation
   /// frame (JavaScript, WebAssembly), plus a negligible amount of internal
@@ -551,7 +556,7 @@
 /// validation and conversion is needed.
 ///
 /// Returns null if the value is null.
-int? _readInt(Map<String, dynamic> json, String key) {
+int _readInt(Map<String, dynamic> json, String key) {
   final num jsonValue = json[key] as num;
 
   if (jsonValue == null) {
@@ -573,10 +578,10 @@
 ///     Inconsistency detected by ld.so: ../elf/dl-tls.c: 493: _dl_allocate_tls_init: Assertion `listp->slotinfo[cnt].gen <= GL(dl_tls_generation)' failed!
 const String _kGlibcError = 'Inconsistency detected by ld.so';
 
-Future<io.Process> _spawnChromiumProcess(String executable, List<String> args, { String? workingDirectory }) async {
+Future<io.Process> _spawnChromiumProcess(String executable, List<String> args, { String workingDirectory }) async {
   // Keep attempting to launch the browser until one of:
   // - Chrome launched successfully, in which case we just return from the loop.
-  // - The tool detected an unretryable Chrome error, in which case we throw ToolExit.
+  // - The tool detected an unretriable Chrome error, in which case we throw ToolExit.
   while (true) {
     final io.Process process = await io.Process.start(executable, args, workingDirectory: workingDirectory);
 
@@ -606,7 +611,7 @@
             'Encountered glibc bug https://sourceware.org/bugzilla/show_bug.cgi?id=19329. '
             'Will try launching browser again.',
           );
-          return '';
+          return null;
         }
         print('Failed to launch browser. Command used to launch it: ${args.join(' ')}');
         throw Exception(
@@ -625,7 +630,7 @@
     // launching more processes.
     unawaited(process.exitCode.timeout(const Duration(seconds: 1), onTimeout: () {
       process.kill();
-      return 0;
+      return null;
     }));
   }
 }
diff --git a/dev/devicelab/lib/framework/cocoon.dart b/dev/devicelab/lib/framework/cocoon.dart
index bdca7db..1f09875 100644
--- a/dev/devicelab/lib/framework/cocoon.dart
+++ b/dev/devicelab/lib/framework/cocoon.dart
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// @dart = 2.8
+
 import 'dart:async';
 import 'dart:convert' show Encoding, json;
 import 'dart:io';
@@ -18,12 +20,12 @@
 typedef ProcessRunSync = ProcessResult Function(
   String,
   List<String>, {
-  Map<String, String>? environment,
+  Map<String, String> environment,
   bool includeParentEnvironment,
   bool runInShell,
-  Encoding? stderrEncoding,
-  Encoding? stdoutEncoding,
-  String? workingDirectory,
+  Encoding stderrEncoding,
+  Encoding stdoutEncoding,
+  String workingDirectory,
 });
 
 /// Class for test runner to interact with Flutter's infrastructure service, Cocoon.
@@ -32,8 +34,8 @@
 /// To retrieve these results, the test runner needs to send results back so the database can be updated.
 class Cocoon {
   Cocoon({
-    String? serviceAccountTokenPath,
-    @visibleForTesting Client? httpClient,
+    String serviceAccountTokenPath,
+    @visibleForTesting Client httpClient,
     @visibleForTesting this.fs = const LocalFileSystem(),
     @visibleForTesting this.processRunSync = Process.runSync,
     @visibleForTesting this.requestRetryLimit = 5,
@@ -56,7 +58,7 @@
   final int requestRetryLimit;
 
   String get commitSha => _commitSha ?? _readCommitSha();
-  String? _commitSha;
+  String _commitSha;
 
   /// Parse the local repo for the current running commit.
   String _readCommitSha() {
@@ -83,9 +85,9 @@
   /// Send [TaskResult] to Cocoon.
   // TODO(chillers): Remove when sendResultsPath is used in prod. https://github.com/flutter/flutter/issues/72457
   Future<void> sendTaskResult({
-    required String builderName,
-    required TaskResult result,
-    required String gitBranch,
+    @required String builderName,
+    @required TaskResult result,
+    @required String gitBranch,
   }) async {
     assert(builderName != null);
     assert(gitBranch != null);
@@ -107,10 +109,10 @@
 
   /// Write the given parameters into an update task request and store the JSON in [resultsPath].
   Future<void> writeTaskResultToFile({
-    required String builderName,
-    required String gitBranch,
-    required TaskResult result,
-    required String resultsPath,
+    @required String builderName,
+    @required String gitBranch,
+    @required TaskResult result,
+    @required String resultsPath,
   }) async {
     assert(builderName != null);
     assert(gitBranch != null);
@@ -132,9 +134,9 @@
   }
 
   Map<String, dynamic> _constructUpdateRequest({
-    String? builderName,
-    required TaskResult result,
-    required String gitBranch,
+    @required String builderName,
+    @required TaskResult result,
+    @required String gitBranch,
   }) {
     final Map<String, dynamic> updateRequest = <String, dynamic>{
       'CommitBranch': gitBranch,
@@ -149,12 +151,12 @@
 
     final List<String> validScoreKeys = <String>[];
     if (result.benchmarkScoreKeys != null) {
-      for (final String scoreKey in result.benchmarkScoreKeys!) {
-        final Object score = result.data![scoreKey] as Object;
+      for (final String scoreKey in result.benchmarkScoreKeys) {
+        final Object score = result.data[scoreKey];
         if (score is num) {
           // Convert all metrics to double, which provide plenty of precision
           // without having to add support for multiple numeric types in Cocoon.
-          result.data![scoreKey] = score.toDouble();
+          result.data[scoreKey] = score.toDouble();
           validScoreKeys.add(scoreKey);
         }
       }
@@ -193,15 +195,15 @@
 class AuthenticatedCocoonClient extends BaseClient {
   AuthenticatedCocoonClient(
     this._serviceAccountTokenPath, {
-    @visibleForTesting Client? httpClient,
-    @visibleForTesting FileSystem? filesystem,
+    @visibleForTesting Client httpClient,
+    @visibleForTesting FileSystem filesystem,
   })  : _delegate = httpClient ?? Client(),
         _fs = filesystem ?? const LocalFileSystem();
 
   /// Authentication token to have the ability to upload and record test results.
   ///
   /// This is intended to only be passed on automated runs on LUCI post-submit.
-  final String? _serviceAccountTokenPath;
+  final String _serviceAccountTokenPath;
 
   /// Underlying [HttpClient] to send requests to.
   final Client _delegate;
@@ -211,7 +213,7 @@
 
   /// Value contained in the service account token file that can be used in http requests.
   String get serviceAccountToken => _serviceAccountToken ?? _readServiceAccountTokenFile();
-  String? _serviceAccountToken;
+  String _serviceAccountToken;
 
   /// Get [serviceAccountToken] from the given service account file.
   String _readServiceAccountTokenFile() {
diff --git a/dev/devicelab/lib/framework/devices.dart b/dev/devicelab/lib/framework/devices.dart
index a4945bb..4800311 100644
--- a/dev/devicelab/lib/framework/devices.dart
+++ b/dev/devicelab/lib/framework/devices.dart
@@ -2,12 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// @dart = 2.8
+
 import 'dart:async';
 import 'dart:convert';
 import 'dart:io';
 import 'dart:math' as math;
 
 import 'package:flutter_devicelab/common.dart';
+import 'package:meta/meta.dart';
 import 'package:path/path.dart' as path;
 
 import 'utils.dart';
@@ -34,8 +37,8 @@
 }
 
 /// Return the item is in idList if find a match, otherwise return null
-String? _findMatchId(List<String> idList, String idPattern) {
-  String? candidate;
+String _findMatchId(List<String> idList, String idPattern) {
+  String candidate;
   idPattern = idPattern.toLowerCase();
   for(final String id in idList) {
     if (id.toLowerCase() == idPattern) {
@@ -181,46 +184,47 @@
 }
 
 class AndroidDeviceDiscovery implements DeviceDiscovery {
-  factory AndroidDeviceDiscovery({AndroidCPU? cpu}) {
+  factory AndroidDeviceDiscovery({AndroidCPU cpu}) {
     return _instance ??= AndroidDeviceDiscovery._(cpu);
   }
 
   AndroidDeviceDiscovery._(this.cpu);
 
-  final AndroidCPU? cpu;
+  final AndroidCPU cpu;
 
   // Parses information about a device. Example:
   //
   // 015d172c98400a03       device usb:340787200X product:nakasi model:Nexus_7 device:grouper
   static final RegExp _kDeviceRegex = RegExp(r'^(\S+)\s+(\S+)(.*)');
 
-  static AndroidDeviceDiscovery? _instance;
+  static AndroidDeviceDiscovery _instance;
 
-  AndroidDevice? _workingDevice;
+  AndroidDevice _workingDevice;
 
   @override
   Future<AndroidDevice> get workingDevice async {
     if (_workingDevice == null) {
       if (Platform.environment.containsKey(DeviceIdEnvName)) {
-        final String deviceId = Platform.environment[DeviceIdEnvName]!;
+        final String deviceId = Platform.environment[DeviceIdEnvName];
         await chooseWorkingDeviceById(deviceId);
-        return _workingDevice!;
+        return _workingDevice;
       }
       await chooseWorkingDevice();
     }
 
-    return _workingDevice!;
+    return _workingDevice;
   }
 
   Future<bool> _matchesCPURequirement(AndroidDevice device) async {
+    if (cpu == null)
+      return true;
     switch (cpu) {
-      case null:
-        return true;
       case AndroidCPU.arm64:
         return device.isArm64();
       case AndroidCPU.arm:
         return device.isArm();
     }
+    return true;
   }
 
   /// Picks a random Android device out of connected devices and sets it as
@@ -255,11 +259,11 @@
 
   @override
   Future<void> chooseWorkingDeviceById(String deviceId) async {
-    final String? matchedId = _findMatchId(await discoverDevices(), deviceId);
+    final String matchedId = _findMatchId(await discoverDevices(), deviceId);
     if (matchedId != null) {
       _workingDevice = AndroidDevice(deviceId: matchedId);
       if (cpu != null) {
-        if (!await _matchesCPURequirement(_workingDevice!)) {
+        if (!await _matchesCPURequirement(_workingDevice)) {
           throw DeviceException('The selected device $matchedId does not match the cpu requirement');
         }
       }
@@ -286,10 +290,10 @@
         continue;
 
       if (_kDeviceRegex.hasMatch(line)) {
-        final Match match = _kDeviceRegex.firstMatch(line)!;
+        final Match match = _kDeviceRegex.firstMatch(line);
 
-        final String deviceID = match[1]!;
-        final String deviceState = match[2]!;
+        final String deviceID = match[1];
+        final String deviceState = match[2];
 
         if (!const <String>['unauthorized', 'offline'].contains(deviceState)) {
           results.add(deviceID);
@@ -338,9 +342,9 @@
 
   FuchsiaDeviceDiscovery._();
 
-  static FuchsiaDeviceDiscovery? _instance;
+  static FuchsiaDeviceDiscovery _instance;
 
-  FuchsiaDevice? _workingDevice;
+  FuchsiaDevice _workingDevice;
 
   String get _ffx {
     final String ffx = path.join(getArtifactPath(), 'fuchsia', 'tools','x64', 'ffx');
@@ -354,13 +358,13 @@
   Future<FuchsiaDevice> get workingDevice async {
     if (_workingDevice == null) {
       if (Platform.environment.containsKey(DeviceIdEnvName)) {
-        final String deviceId = Platform.environment[DeviceIdEnvName]!;
+        final String deviceId = Platform.environment[DeviceIdEnvName];
         await chooseWorkingDeviceById(deviceId);
-        return _workingDevice!;
+        return _workingDevice;
       }
       await chooseWorkingDevice();
     }
-    return _workingDevice!;
+    return _workingDevice;
   }
 
   /// Picks the first connected Fuchsia device.
@@ -379,8 +383,8 @@
 
   @override
   Future<void> chooseWorkingDeviceById(String deviceId) async {
-    final String? matchedId = _findMatchId(await discoverDevices(), deviceId);
-    if (matchedId != null) {
+    final String matchedId = _findMatchId(await discoverDevices(), deviceId);
+    if (deviceId != null) {
       _workingDevice = FuchsiaDevice(deviceId: matchedId);
       print('Choose device by ID: $matchedId');
       return;
@@ -438,7 +442,7 @@
 }
 
 class AndroidDevice extends Device {
-  AndroidDevice({required this.deviceId}) {
+  AndroidDevice({@required this.deviceId}) {
     _updateDeviceInfo();
   }
 
@@ -536,19 +540,19 @@
   }
 
   /// Executes [command] on `adb shell` and returns its exit code.
-  Future<void> shellExec(String command, List<String> arguments, { Map<String, String>? environment, bool silent = false }) async {
+  Future<void> shellExec(String command, List<String> arguments, { Map<String, String> environment, bool silent = false }) async {
     await adb(<String>['shell', command, ...arguments], environment: environment, silent: silent);
   }
 
   /// Executes [command] on `adb shell` and returns its standard output as a [String].
-  Future<String> shellEval(String command, List<String> arguments, { Map<String, String>? environment, bool silent = false }) {
+  Future<String> shellEval(String command, List<String> arguments, { Map<String, String> environment, bool silent = false }) {
     return adb(<String>['shell', command, ...arguments], environment: environment, silent: silent);
   }
 
   /// Runs `adb` with the given [arguments], selecting this device.
   Future<String> adb(
       List<String> arguments, {
-      Map<String, String>? environment,
+      Map<String, String> environment,
       bool silent = false,
     }) {
     return eval(
@@ -564,10 +568,10 @@
   @override
   Future<Map<String, dynamic>> getMemoryStats(String packageName) async {
     final String meminfo = await shellEval('dumpsys', <String>['meminfo', packageName]);
-    final Match? match = RegExp(r'TOTAL\s+(\d+)').firstMatch(meminfo);
+    final Match match = RegExp(r'TOTAL\s+(\d+)').firstMatch(meminfo);
     assert(match != null, 'could not parse dumpsys meminfo output');
     return <String, dynamic>{
-      'total_kb': int.parse(match!.group(1)!),
+      'total_kb': int.parse(match.group(1)),
     };
   }
 
@@ -575,7 +579,7 @@
   bool get canStreamLogs => true;
 
   bool _abortedLogging/*!*/ = false;
-  Process? _loggingProcess;
+  Process/*?*/ _loggingProcess;
 
   @override
   Future<void> startLoggingToSink(IOSink sink, {bool clear = true}) async {
@@ -592,17 +596,17 @@
       // to view the whole log, or just run logcat alongside this.
       <String>['-s', deviceId, 'logcat', 'ActivityManager:I', 'flutter:V', '*:F'],
     );
-    _loggingProcess!.stdout
+    _loggingProcess.stdout
       .transform<String>(const Utf8Decoder(allowMalformed: true))
       .listen((String line) {
         sink.write(line);
       });
-    _loggingProcess!.stderr
+    _loggingProcess.stderr
       .transform<String>(const Utf8Decoder(allowMalformed: true))
       .listen((String line) {
         sink.write(line);
       });
-    unawaited(_loggingProcess!.exitCode.then<void>((int exitCode) {
+    unawaited(_loggingProcess.exitCode.then<void>((int exitCode) {
       if (!_abortedLogging) {
         sink.writeln('adb logcat failed with exit code $exitCode.\n');
       }
@@ -613,8 +617,8 @@
   Future<void> stopLoggingToSink() async {
     if (_loggingProcess != null) {
       _abortedLogging = true;
-      _loggingProcess!.kill();
-      await _loggingProcess!.exitCode;
+      _loggingProcess.kill();
+      await _loggingProcess.exitCode;
     }
   }
 
@@ -625,7 +629,7 @@
     final Completer<void> processDone = Completer<void>();
     final Completer<void> abort = Completer<void>();
     bool aborted = false;
-    late final StreamController<String> stream;
+    StreamController<String> stream;
     stream = StreamController<String>(
       onListen: () async {
         await adb(<String>['logcat', '--clear']);
@@ -709,22 +713,22 @@
 
   IosDeviceDiscovery._();
 
-  static IosDeviceDiscovery? _instance;
+  static IosDeviceDiscovery _instance;
 
-  IosDevice? _workingDevice;
+  IosDevice _workingDevice;
 
   @override
   Future<IosDevice> get workingDevice async {
     if (_workingDevice == null) {
       if (Platform.environment.containsKey(DeviceIdEnvName)) {
-        final String deviceId = Platform.environment[DeviceIdEnvName]!;
+        final String deviceId = Platform.environment[DeviceIdEnvName];
         await chooseWorkingDeviceById(deviceId);
-        return _workingDevice!;
+        return _workingDevice;
       }
       await chooseWorkingDevice();
     }
 
-    return _workingDevice!;
+    return _workingDevice;
   }
 
   /// Picks a random iOS device out of connected devices and sets it as
@@ -745,7 +749,7 @@
 
   @override
   Future<void> chooseWorkingDeviceById(String deviceId) async {
-    final String? matchedId = _findMatchId(await discoverDevices(), deviceId);
+    final String matchedId = _findMatchId(await discoverDevices(), deviceId);
     if (matchedId != null) {
       _workingDevice = IosDevice(deviceId: matchedId);
       print('Choose device by ID: $matchedId');
@@ -820,7 +824,7 @@
 
 /// iOS device.
 class IosDevice extends Device {
-  IosDevice({ required this.deviceId });
+  IosDevice({ @required this.deviceId });
 
   @override
   final String deviceId;
@@ -842,8 +846,8 @@
   @override
   bool get canStreamLogs => true;
 
-  bool _abortedLogging = false;
-  Process? _loggingProcess;
+  bool _abortedLogging/*!*/ = false;
+  Process/*?*/ _loggingProcess;
 
   @override
   Future<void> startLoggingToSink(IOSink sink, {bool clear = true}) async {
@@ -855,17 +859,17 @@
         'DYLD_LIBRARY_PATH': dyldLibraryPath,
       },
     );
-    _loggingProcess!.stdout
+    _loggingProcess.stdout
       .transform<String>(const Utf8Decoder(allowMalformed: true))
       .listen((String line) {
         sink.write(line);
       });
-    _loggingProcess!.stderr
+    _loggingProcess.stderr
       .transform<String>(const Utf8Decoder(allowMalformed: true))
       .listen((String line) {
         sink.write(line);
       });
-    unawaited(_loggingProcess!.exitCode.then<void>((int exitCode) {
+    unawaited(_loggingProcess.exitCode.then<void>((int exitCode) {
       if (!_abortedLogging) {
         sink.writeln('idevicesyslog failed with exit code $exitCode.\n');
       }
@@ -876,8 +880,8 @@
   Future<void> stopLoggingToSink() async {
     if (_loggingProcess != null) {
       _abortedLogging = true;
-      _loggingProcess!.kill();
-      await _loggingProcess!.exitCode;
+      _loggingProcess.kill();
+      await _loggingProcess.exitCode;
     }
   }
 
@@ -930,7 +934,7 @@
 
 /// Fuchsia device.
 class FuchsiaDevice extends Device {
-  const FuchsiaDevice({ required this.deviceId });
+  const FuchsiaDevice({ @required this.deviceId });
 
   @override
   final String deviceId;
@@ -978,7 +982,7 @@
 
 /// Path to the `adb` executable.
 String get adbPath {
-  final String? androidHome = Platform.environment['ANDROID_HOME'] ?? Platform.environment['ANDROID_SDK_ROOT'];
+  final String androidHome = Platform.environment['ANDROID_HOME'] ?? Platform.environment['ANDROID_SDK_ROOT'];
 
   if (androidHome == null) {
     throw const DeviceException(
@@ -997,7 +1001,7 @@
 }
 
 class FakeDevice extends Device {
-  const FakeDevice({ required this.deviceId });
+  const FakeDevice({ @required this.deviceId });
 
   @override
   final String deviceId;
@@ -1051,22 +1055,22 @@
 
   FakeDeviceDiscovery._();
 
-  static FakeDeviceDiscovery? _instance;
+  static FakeDeviceDiscovery _instance;
 
-  FakeDevice? _workingDevice;
+  FakeDevice _workingDevice;
 
   @override
   Future<FakeDevice> get workingDevice async {
     if (_workingDevice == null) {
       if (Platform.environment.containsKey(DeviceIdEnvName)) {
-        final String deviceId = Platform.environment[DeviceIdEnvName]!;
+        final String deviceId = Platform.environment[DeviceIdEnvName];
         await chooseWorkingDeviceById(deviceId);
-        return _workingDevice!;
+        return _workingDevice;
       }
       await chooseWorkingDevice();
     }
 
-    return _workingDevice!;
+    return _workingDevice;
   }
 
   /// The Fake is only available for by ID device discovery.
@@ -1077,7 +1081,7 @@
 
   @override
   Future<void> chooseWorkingDeviceById(String deviceId) async {
-    final String? matchedId = _findMatchId(await discoverDevices(), deviceId);
+    final String matchedId = _findMatchId(await discoverDevices(), deviceId);
     if (matchedId != null) {
       _workingDevice = FakeDevice(deviceId: matchedId);
       print('Choose device by ID: $matchedId');
diff --git a/dev/devicelab/lib/framework/framework.dart b/dev/devicelab/lib/framework/framework.dart
index a304102..e73e373 100644
--- a/dev/devicelab/lib/framework/framework.dart
+++ b/dev/devicelab/lib/framework/framework.dart
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// @dart = 2.8
+
 import 'dart:async';
 import 'dart:convert';
 import 'dart:developer';
@@ -62,8 +64,8 @@
   _TaskRunner(this.task) {
     registerExtension('ext.cocoonRunTask',
         (String method, Map<String, String> parameters) async {
-      final Duration? taskTimeout = parameters.containsKey('timeoutInMinutes')
-        ? Duration(minutes: int.parse(parameters['timeoutInMinutes']!))
+      final Duration taskTimeout = parameters.containsKey('timeoutInMinutes')
+        ? Duration(minutes: int.parse(parameters['timeoutInMinutes']))
         : null;
       // This is only expected to be passed in unit test runs so they do not
       // kill the Dart process that is running them and waste time running config.
@@ -80,7 +82,7 @@
 
   final TaskFunction task;
 
-  Future<Device?> _getWorkingDeviceIfAvailable() async {
+  Future<Device/*?*/> _getWorkingDeviceIfAvailable() async {
     try {
       return await devices.workingDevice;
     } on DeviceException {
@@ -89,8 +91,8 @@
   }
 
   // TODO(ianh): workaround for https://github.com/dart-lang/sdk/issues/23797
-  RawReceivePort? _keepAlivePort;
-  Timer? _startTaskTimeout;
+  RawReceivePort _keepAlivePort;
+  Timer _startTaskTimeout;
   bool _taskStarted = false;
 
   final Completer<TaskResult> _completer = Completer<TaskResult>();
@@ -100,7 +102,7 @@
   /// Signals that this task runner finished running the task.
   Future<TaskResult> get whenDone => _completer.future;
 
-  Future<TaskResult> run(Duration? taskTimeout, {
+  Future<TaskResult> run(Duration taskTimeout, {
     bool runFlutterConfig = true,
     bool runProcessCleanup = true,
   }) async {
@@ -108,7 +110,7 @@
       _taskStarted = true;
       print('Running task with a timeout of $taskTimeout.');
       final String exe = Platform.isWindows ? '.exe' : '';
-      late Set<RunningProcessInfo> beforeRunningDartInstances;
+      Set<RunningProcessInfo> beforeRunningDartInstances;
       if (runProcessCleanup) {
         section('Checking running Dart$exe processes');
         beforeRunningDartInstances = await getRunningProcesses(
@@ -134,7 +136,7 @@
           '--enable-windows-desktop',
           '--enable-linux-desktop',
           '--enable-web',
-          if (localEngine != null) ...<String>['--local-engine', localEngine!],
+          if (localEngine != null) ...<String>['--local-engine', localEngine],
         ], canFail: true);
         if (configResult != 0) {
           print('Failed to enable configuration, tasks may not run.');
@@ -143,12 +145,12 @@
         print('Skipping enabling configs for macOS, Linux, Windows, and Web');
       }
 
-      final Device? device = await _getWorkingDeviceIfAvailable();
-      late TaskResult result;
-      IOSink? sink;
+      final Device/*?*/ device = await _getWorkingDeviceIfAvailable();
+      /*late*/ TaskResult result;
+      IOSink/*?*/ sink;
       try {
         if (device != null && device.canStreamLogs && hostAgent.dumpDirectory != null) {
-          sink = File(path.join(hostAgent.dumpDirectory!.path, '${device.deviceId}.log')).openWrite();
+          sink = File(path.join(hostAgent.dumpDirectory.path, '${device.deviceId}.log')).openWrite();
           await device.startLoggingToSink(sink);
         }
 
@@ -210,7 +212,7 @@
       final File rebootFile = _rebootFile();
       int runCount;
       if (rebootFile.existsSync()) {
-        runCount = int.tryParse(rebootFile.readAsStringSync().trim()) ?? 0;
+        runCount = int.tryParse(rebootFile.readAsStringSync().trim());
       } else {
         runCount = 0;
       }
@@ -281,10 +283,10 @@
 
 File _rebootFile() {
   if (Platform.isLinux || Platform.isMacOS) {
-    return File(path.join(Platform.environment['HOME']!, '.reboot-count'));
+    return File(path.join(Platform.environment['HOME'], '.reboot-count'));
   }
   if (!Platform.isWindows) {
     throw StateError('Unexpected platform ${Platform.operatingSystem}');
   }
-  return File(path.join(Platform.environment['USERPROFILE']!, '.reboot-count'));
+  return File(path.join(Platform.environment['USERPROFILE'], '.reboot-count'));
 }
diff --git a/dev/devicelab/lib/framework/ios.dart b/dev/devicelab/lib/framework/ios.dart
index 3e03bd9..6598011 100644
--- a/dev/devicelab/lib/framework/ios.dart
+++ b/dev/devicelab/lib/framework/ios.dart
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// @dart = 2.8
+
 import 'dart:convert';
 
 import 'utils.dart';
@@ -40,10 +42,17 @@
   final List<String> lines = LineSplitter.split(loadCommands).toList();
   lines.asMap().forEach((int index, String line) {
     if (line.contains('segname __LLVM') && lines.length - index - 1 > 3) {
-      emptyBitcodeMarkerFound |= lines
+      final String emptyBitcodeMarker = lines
         .skip(index - 1)
         .take(4)
-        .any((String line) => line.contains(' size 0x0000000000000001'));
+        .firstWhere(
+          (String line) => line.contains(' size 0x0000000000000001'),
+          orElse: () => null,
+      );
+      if (emptyBitcodeMarker != null) {
+        emptyBitcodeMarkerFound = true;
+        return;
+      }
     }
   });
   return !emptyBitcodeMarkerFound;
@@ -70,16 +79,16 @@
     workingDirectory: flutterDirectory.path,
   );
 
-  String? iOSSimRuntime;
+  String iOSSimRuntime;
 
   final RegExp iOSRuntimePattern = RegExp(r'iOS .*\) - (.*)');
 
   for (final String runtime in LineSplitter.split(availableRuntimes)) {
     // These seem to be in order, so allow matching multiple lines so it grabs
     // the last (hopefully latest) one.
-    final RegExpMatch? iOSRuntimeMatch = iOSRuntimePattern.firstMatch(runtime);
+    final RegExpMatch iOSRuntimeMatch = iOSRuntimePattern.firstMatch(runtime);
     if (iOSRuntimeMatch != null) {
-      iOSSimRuntime = iOSRuntimeMatch.group(1)!.trim();
+      iOSSimRuntime = iOSRuntimeMatch.group(1).trim();
       continue;
     }
   }
diff --git a/dev/devicelab/lib/framework/manifest.dart b/dev/devicelab/lib/framework/manifest.dart
index a52ab10..836c8ab 100644
--- a/dev/devicelab/lib/framework/manifest.dart
+++ b/dev/devicelab/lib/framework/manifest.dart
@@ -2,14 +2,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// @dart = 2.8
+
 import 'dart:io';
 
+import 'package:meta/meta.dart';
 import 'package:yaml/yaml.dart';
 
 import 'utils.dart';
 
 /// Loads manifest data from `manifest.yaml` file or from [yaml], if present.
-Manifest loadTaskManifest([ String? yaml ]) {
+Manifest loadTaskManifest([ String yaml ]) {
   final dynamic manifestYaml = yaml == null
     ? loadYaml(file('manifest.yaml').readAsStringSync())
     : loadYamlNode(yaml);
@@ -29,13 +32,13 @@
 /// A CI task.
 class ManifestTask {
   ManifestTask._({
-    required this.name,
-    required this.description,
-    required this.stage,
-    required this.requiredAgentCapabilities,
-    required this.isFlaky,
-    required this.timeoutInMinutes,
-    required this.onLuci,
+    @required this.name,
+    @required this.description,
+    @required this.stage,
+    @required this.requiredAgentCapabilities,
+    @required this.isFlaky,
+    @required this.timeoutInMinutes,
+    @required this.onLuci,
   }) {
     final String taskName = 'task "$name"';
     _checkIsNotBlank(name, 'Task name', taskName);
@@ -145,9 +148,9 @@
     // ignore: avoid_dynamic_calls
     stage: taskYaml['stage'] as String,
     requiredAgentCapabilities: capabilities as List<String>,
-    isFlaky: isFlaky as bool,
+    isFlaky: isFlaky as bool ?? false,
     timeoutInMinutes: timeoutInMinutes as int,
-    onLuci: onLuci as bool,
+    onLuci: onLuci as bool ?? false,
   );
 }
 
@@ -158,7 +161,7 @@
     final dynamic capability = capabilities[i];
     _checkType(capability is String, capability, 'required_agent_capabilities[$i]', 'string');
   }
-  return capabilitiesYaml.cast<String>();
+  return (capabilitiesYaml as List<dynamic>).cast<String>();
 }
 
 void _checkType(bool isValid, dynamic value, String variableName, String typeName) {
diff --git a/dev/devicelab/lib/framework/runner.dart b/dev/devicelab/lib/framework/runner.dart
index bbfc959..7a84e9f 100644
--- a/dev/devicelab/lib/framework/runner.dart
+++ b/dev/devicelab/lib/framework/runner.dart
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// @dart = 2.8
+
 import 'dart:async';
 import 'dart:convert';
 import 'dart:io';
@@ -19,13 +21,13 @@
   List<String> taskNames, {
   bool exitOnFirstTestFailure = false,
   bool silent = false,
-  String? deviceId,
-  String? gitBranch,
-  String? localEngine,
-  String? localEngineSrcPath,
-  String? luciBuilder,
-  String? resultsPath,
-  List<String>? taskArgs,
+  String deviceId,
+  String gitBranch,
+  String localEngine,
+  String localEngineSrcPath,
+  String luciBuilder,
+  String resultsPath,
+  List<String> taskArgs,
 }) async {
   for (final String taskName in taskNames) {
     section('Running task "$taskName"');
@@ -42,10 +44,10 @@
     print(const JsonEncoder.withIndent('  ').convert(result));
     section('Finished task "$taskName"');
 
-    if (resultsPath != null && gitBranch != null) {
+    if (resultsPath != null) {
       final Cocoon cocoon = Cocoon();
       await cocoon.writeTaskResultToFile(
-        builderName: luciBuilder!,
+        builderName: luciBuilder,
         gitBranch: gitBranch,
         result: result,
         resultsPath: resultsPath,
@@ -74,11 +76,11 @@
 Future<TaskResult> runTask(
   String taskName, {
   bool silent = false,
-  String? localEngine,
-  String? localEngineSrcPath,
-  String? deviceId,
-  List<String> ?taskArgs,
-  @visibleForTesting Map<String, String>? isolateParams,
+  String localEngine,
+  String localEngineSrcPath,
+  String deviceId,
+  List<String> taskArgs,
+  @visibleForTesting Map<String, String> isolateParams,
 }) async {
   final String taskExecutable = 'bin/tasks/$taskName.dart';
 
@@ -115,7 +117,7 @@
       .transform<String>(const LineSplitter())
       .listen((String line) {
     if (!uri.isCompleted) {
-      final Uri? serviceUri = parseServiceUri(line, prefix: 'Observatory listening on ');
+      final Uri serviceUri = parseServiceUri(line, prefix: 'Observatory listening on ');
       if (serviceUri != null)
         uri.complete(serviceUri);
     }
@@ -137,7 +139,7 @@
       'ext.cocoonRunTask',
       args: isolateParams,
       isolateId: result.isolate.id,
-    )).json!;
+    )).json;
     final TaskResult taskResult = TaskResult.fromJson(taskResultJson);
     await runner.exitCode;
     return taskResult;
@@ -166,13 +168,13 @@
       // Look up the isolate.
       final VmService client = await vmServiceConnectUri(url);
       VM vm = await client.getVM();
-      while (vm.isolates!.isEmpty) {
+      while (vm.isolates.isEmpty) {
         await Future<void>.delayed(const Duration(seconds: 1));
         vm = await client.getVM();
       }
-      final IsolateRef isolate = vm.isolates!.first;
+      final IsolateRef isolate = vm.isolates.first;
       final Response response = await client.callServiceExtension('ext.cocoonRunnerReady', isolateId: isolate.id);
-      if (response.json!['response'] != 'ready')
+      if (response.json['response'] != 'ready')
         throw 'not ready yet';
       return ConnectionResult(client, isolate);
     } catch (error) {
@@ -191,7 +193,7 @@
 }
 
 /// The cocoon client sends an invalid VM service response, we need to intercept it.
-Future<VmService> vmServiceConnectUri(String wsUri, {Log? log}) async {
+Future<VmService> vmServiceConnectUri(String wsUri, {Log log}) async {
   final WebSocket socket = await WebSocket.connect(wsUri);
   final StreamController<dynamic> controller = StreamController<dynamic>();
   final Completer<dynamic> streamClosedCompleter = Completer<dynamic>();
@@ -205,7 +207,7 @@
         controller.add(data);
       }
     },
-    onError: (Object err, StackTrace stackTrace) => controller.addError(err, stackTrace),
+    onError: (dynamic err, StackTrace stackTrace) => controller.addError(err, stackTrace),
     onDone: () => streamClosedCompleter.complete(),
   );
 
diff --git a/dev/devicelab/lib/framework/running_processes.dart b/dev/devicelab/lib/framework/running_processes.dart
index 07a5e52..b07f1c1 100644
--- a/dev/devicelab/lib/framework/running_processes.dart
+++ b/dev/devicelab/lib/framework/running_processes.dart
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// @dart = 2.8
+
 import 'dart:io';
 
 import 'package:meta/meta.dart';
@@ -34,7 +36,7 @@
   }
 }
 
-Future<bool> killProcess(String pid, {ProcessManager? processManager}) async {
+Future<bool> killProcess(String pid, {ProcessManager processManager}) async {
   assert(pid != null, 'Must specify a pid to kill');
   processManager ??= const LocalProcessManager();
   ProcessResult result;
@@ -56,8 +58,8 @@
 }
 
 Stream<RunningProcessInfo> getRunningProcesses({
-  String? processName,
-  ProcessManager? processManager,
+  String processName,
+  ProcessManager processManager,
 }) {
   processManager ??= const LocalProcessManager();
   if (Platform.isWindows) {
@@ -67,7 +69,7 @@
 }
 
 @visibleForTesting
-Stream<RunningProcessInfo> windowsRunningProcesses(String? processName) async* {
+Stream<RunningProcessInfo> windowsRunningProcesses(String processName) async* {
   // PowerShell script to get the command line arguments and create time of
   // a process.
   // See: https://docs.microsoft.com/en-us/windows/desktop/cimwin32prov/win32-process
@@ -105,8 +107,8 @@
 
   const int processIdHeaderSize = 'ProcessId'.length;
   const int creationDateHeaderStart = processIdHeaderSize + 1;
-  late int creationDateHeaderEnd;
-  late int commandLineHeaderStart;
+  int creationDateHeaderEnd;
+  int commandLineHeaderStart;
   bool inTableBody = false;
   for (final String line in output.split('\n')) {
     if (line.startsWith('ProcessId')) {
@@ -158,7 +160,7 @@
 
 @visibleForTesting
 Stream<RunningProcessInfo> posixRunningProcesses(
-  String? processName,
+  String processName,
   ProcessManager processManager,
 ) async* {
   // Cirrus is missing this in Linux for some reason.
@@ -192,7 +194,7 @@
 @visibleForTesting
 Iterable<RunningProcessInfo> processPsOutput(
   String output,
-  String? processName,
+  String processName,
 ) sync* {
   if (output == null) {
     return;
@@ -233,7 +235,7 @@
     final String rawTime = line.substring(0, 24);
 
     final String year = rawTime.substring(20, 24);
-    final String month = months[rawTime.substring(4, 7)]!;
+    final String month = months[rawTime.substring(4, 7)];
     final String day = rawTime.substring(8, 10).replaceFirst(' ', '0');
     final String time = rawTime.substring(11, 19);
 
diff --git a/dev/devicelab/lib/framework/task_result.dart b/dev/devicelab/lib/framework/task_result.dart
index 3fa821d..be6e4e4 100644
--- a/dev/devicelab/lib/framework/task_result.dart
+++ b/dev/devicelab/lib/framework/task_result.dart
@@ -41,7 +41,7 @@
     List<String> detailFiles = const <String>[],
   }) {
     return TaskResult.success(
-      json.decode(file.readAsStringSync()) as Map<String, dynamic>?,
+      json.decode(file.readAsStringSync()) as Map<String, dynamic>,
       benchmarkScoreKeys: benchmarkScoreKeys,
       detailFiles: detailFiles,
     );
@@ -53,14 +53,14 @@
     if (success) {
       final List<String> benchmarkScoreKeys = (json['benchmarkScoreKeys'] as List<dynamic>? ?? <String>[]).cast<String>();
       final List<String> detailFiles = (json['detailFiles'] as List<dynamic>? ?? <String>[]).cast<String>();
-      return TaskResult.success(json['data'] as Map<String, dynamic>?,
+      return TaskResult.success(json['data'] as Map<String, dynamic>,
         benchmarkScoreKeys: benchmarkScoreKeys,
         detailFiles: detailFiles,
-        message: json['reason'] as String?,
+        message: json['reason'] as String,
       );
     }
 
-    return TaskResult.failure(json['reason'] as String?);
+    return TaskResult.failure(json['reason'] as String);
   }
 
   /// Constructs an unsuccessful result.
@@ -88,7 +88,7 @@
   bool get failed => !succeeded;
 
   /// Explains the result in a human-readable format.
-  final String? message;
+  final String message;
 
   /// Serializes this task result to JSON format.
   ///
@@ -124,7 +124,7 @@
   }
 
   @override
-  String toString() => message ?? '';
+  String toString() => message;
 }
 
 class TaskResultCheckProcesses extends TaskResult {
diff --git a/dev/devicelab/lib/framework/utils.dart b/dev/devicelab/lib/framework/utils.dart
index 7983e27..aeb501a 100644
--- a/dev/devicelab/lib/framework/utils.dart
+++ b/dev/devicelab/lib/framework/utils.dart
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// @dart = 2.8
+
 import 'dart:async';
 import 'dart:convert';
 import 'dart:io';
@@ -9,6 +11,7 @@
 
 import 'package:flutter_devicelab/common.dart';
 import 'package:flutter_devicelab/framework/devices.dart';
+import 'package:meta/meta.dart';
 import 'package:path/path.dart' as path;
 import 'package:process/process.dart';
 import 'package:stack_trace/stack_trace.dart';
@@ -20,7 +23,7 @@
 String cwd = Directory.current.path;
 
 /// The local engine to use for [flutter] and [evalFlutter], if any.
-String? get localEngine {
+String get localEngine {
   // Use two distinct `defaultValue`s to determine whether a 'localEngine'
   // declaration exists in the environment.
   const bool isDefined =
@@ -31,7 +34,7 @@
 
 /// The local engine source path to use if a local engine is used for [flutter]
 /// and [evalFlutter].
-String? get localEngineSrcPath {
+String get localEngineSrcPath {
   // Use two distinct `defaultValue`s to determine whether a
   // 'localEngineSrcPath' declaration exists in the environment.
   const bool isDefined =
@@ -67,18 +70,18 @@
   HealthCheckResult.failure(this.details) : succeeded = false;
   HealthCheckResult.error(dynamic error, dynamic stackTrace)
       : succeeded = false,
-        details = 'ERROR: $error${stackTrace != null ? '\n$stackTrace' : ''}';
+        details = 'ERROR: $error${'\n$stackTrace' ?? ''}';
 
   final bool succeeded;
-  final String? details;
+  final String details;
 
   @override
   String toString() {
     final StringBuffer buf = StringBuffer(succeeded ? 'succeeded' : 'failed');
-    if (details != null && details!.trim().isNotEmpty) {
+    if (details != null && details.trim().isNotEmpty) {
       buf.writeln();
       // Indent details by 4 spaces
-      for (final String line in details!.trim().split('\n')) {
+      for (final String line in details.trim().split('\n')) {
         buf.writeln('    $line');
       }
     }
@@ -124,7 +127,7 @@
 
 File file(String path) => File(path);
 
-void copy(File sourceFile, Directory targetDirectory, {String? name}) {
+void copy(File sourceFile, Directory targetDirectory, {String name}) {
   final File target = file(
       path.join(targetDirectory.path, name ?? path.basename(sourceFile.path)));
   target.writeAsBytesSync(sourceFile.readAsBytesSync());
@@ -151,7 +154,7 @@
 }
 
 FileSystemEntity move(FileSystemEntity whatToMove,
-    {required Directory to, String? name}) {
+    {Directory to, String name}) {
   return whatToMove
       .renameSync(path.join(to.path, name ?? path.basename(whatToMove.path)));
 }
@@ -222,9 +225,9 @@
   return version.replaceAll('"', "'");
 }
 
-Future<String?> getCurrentFlutterRepoCommit() {
+Future<String> getCurrentFlutterRepoCommit() {
   if (!dir('${flutterDirectory.path}/.git').existsSync()) {
-    return Future<String?>.value(null);
+    return Future<String>.value(null);
   }
 
   return inDirectory<String>(flutterDirectory, () {
@@ -272,10 +275,10 @@
 /// returned in the form of a [Future] that completes to a [Process] object.
 Future<Process> startProcess(
   String executable,
-  List<String>? arguments, {
-  Map<String, String>? environment,
+  List<String> arguments, {
+  Map<String, String> environment,
   bool isBot = true, // set to false to pretend not to be on a bot (e.g. to test user-facing outputs)
-  String? workingDirectory,
+  String workingDirectory,
 }) async {
   assert(isBot != null);
   final String command = '$executable ${arguments?.join(" ") ?? ""}';
@@ -285,7 +288,7 @@
   newEnvironment['LANG'] = 'en_US.UTF-8';
   print('\nExecuting: $command in $finalWorkingDirectory with environment $newEnvironment');
   final Process process = await _processManager.start(
-    <String>[executable, ...?arguments],
+    <String>[executable, ...arguments],
     environment: newEnvironment,
     workingDirectory: finalWorkingDirectory,
   );
@@ -321,9 +324,9 @@
 Future<int> exec(
   String executable,
   List<String> arguments, {
-  Map<String, String>? environment,
+  Map<String, String> environment,
   bool canFail = false, // as in, whether failures are ok. False means that they are fatal.
-  String? workingDirectory,
+  String workingDirectory,
 }) async {
   return _execute(
     executable,
@@ -337,11 +340,11 @@
 Future<int> _execute(
   String executable,
   List<String> arguments, {
-  Map<String, String>? environment,
+  Map<String, String> environment,
   bool canFail = false, // as in, whether failures are ok. False means that they are fatal.
-  String? workingDirectory,
-  StringBuffer? output, // if not null, the stdout will be written here
-  StringBuffer? stderr, // if not null, the stderr will be written here
+  String workingDirectory,
+  StringBuffer output, // if not null, the stdout will be written here
+  StringBuffer stderr, // if not null, the stderr will be written here
   bool printStdout = true,
   bool printStderr = true,
 }) async {
@@ -373,8 +376,8 @@
 /// Returns a future that completes when both out and error streams a closed.
 Future<void> forwardStandardStreams(
   Process process, {
-  StringBuffer? output,
-  StringBuffer? stderr,
+  StringBuffer output,
+  StringBuffer stderr,
   bool printStdout = true,
   bool printStderr = true,
   }) {
@@ -411,10 +414,10 @@
 Future<String> eval(
   String executable,
   List<String> arguments, {
-  Map<String, String>? environment,
+  Map<String, String> environment,
   bool canFail = false, // as in, whether failures are ok. False means that they are fatal.
-  String? workingDirectory,
-  StringBuffer? stderr, // if not null, the stderr will be written here
+  String workingDirectory,
+  StringBuffer stderr, // if not null, the stderr will be written here
   bool printStdout = true,
   bool printStderr = true,
 }) async {
@@ -454,10 +457,10 @@
 
     if (command == 'drive' && hostAgent.dumpDirectory != null) ...<String>[
       '--screenshot',
-      hostAgent.dumpDirectory!.path,
+      hostAgent.dumpDirectory.path,
     ],
-    if (localEngine != null) ...<String>['--local-engine', localEngine!],
-    if (localEngineSrcPath != null) ...<String>['--local-engine-src-path', localEngineSrcPath!],
+    if (localEngine != null) ...<String>['--local-engine', localEngine],
+    if (localEngineSrcPath != null) ...<String>['--local-engine-src-path', localEngineSrcPath],
     ...options,
   ];
 }
@@ -467,7 +470,7 @@
 Future<int> flutter(String command, {
   List<String> options = const <String>[],
   bool canFail = false, // as in, whether failures are ok. False means that they are fatal.
-  Map<String, String>? environment,
+  Map<String, String> environment = const <String, String>{},
 }) {
   final List<String> args = flutterCommandArgs(command, options);
   return exec(path.join(flutterDirectory.path, 'bin', 'flutter'), args,
@@ -490,8 +493,8 @@
 Future<String> evalFlutter(String command, {
   List<String> options = const <String>[],
   bool canFail = false, // as in, whether failures are ok. False means that they are fatal.
-  Map<String, String>? environment,
-  StringBuffer? stderr, // if not null, the stderr will be written here.
+  Map<String, String> environment,
+  StringBuffer stderr, // if not null, the stderr will be written here.
 }) {
   final List<String> args = flutterCommandArgs(command, options);
   return eval(path.join(flutterDirectory.path, 'bin', 'flutter'), args,
@@ -518,7 +521,7 @@
 
 /// Returns a future that completes with a path suitable for JAVA_HOME
 /// or with null, if Java cannot be found.
-Future<String?> findJavaHome() async {
+Future<String> findJavaHome() async {
   if (_javaHome == null) {
     final Iterable<String> hits = grep(
       'Java binary at: ',
@@ -534,7 +537,7 @@
   }
   return _javaHome;
 }
-String? _javaHome;
+String _javaHome;
 
 Future<T> inDirectory<T>(dynamic directory, Future<T> Function() action) async {
   final String previousCwd = cwd;
@@ -565,12 +568,12 @@
 Directory get flutterDirectory => Directory.current.parent.parent;
 
 String requireEnvVar(String name) {
-  final String? value = Platform.environment[name];
+  final String value = Platform.environment[name];
 
   if (value == null)
     fail('$name environment variable is missing. Quitting.');
 
-  return value!;
+  return value;
 }
 
 T requireConfigProperty<T>(Map<String, dynamic> map, String propertyName) {
@@ -634,7 +637,7 @@
 }
 
 /// Splits [from] into lines and selects those that contain [pattern].
-Iterable<String> grep(Pattern pattern, {required String from}) {
+Iterable<String> grep(Pattern pattern, {@required String from}) {
   return from.split('\n').where((String line) {
     return line.contains(pattern);
   });
@@ -672,8 +675,8 @@
 ///
 /// The `prefix`, if specified, is a regular expression pattern and must not contain groups.
 /// `prefix` defaults to the RegExp: `An Observatory debugger .* is available at: `.
-int? parseServicePort(String line, {
-  Pattern? prefix,
+int parseServicePort(String line, {
+  Pattern prefix,
 }) {
   prefix ??= _obsRegExp;
   final Iterable<Match> matchesIter = prefix.allMatches(line);
@@ -683,15 +686,15 @@
   final Match prefixMatch = matchesIter.first;
   final List<Match> matches =
     _obsPortRegExp.allMatches(line, prefixMatch.end).toList();
-  return matches.isEmpty ? null : int.parse(matches[0].group(2)!);
+  return matches.isEmpty ? null : int.parse(matches[0].group(2));
 }
 
 /// Tries to extract a URL from the string.
 ///
 /// The `prefix`, if specified, is a regular expression pattern and must not contain groups.
 /// `prefix` defaults to the RegExp: `An Observatory debugger .* is available at: `.
-Uri? parseServiceUri(String line, {
-  Pattern? prefix,
+Uri parseServiceUri(String line, {
+  Pattern prefix,
 }) {
   prefix ??= _obsRegExp;
   final Iterable<Match> matchesIter = prefix.allMatches(line);
@@ -701,7 +704,7 @@
   final Match prefixMatch = matchesIter.first;
   final List<Match> matches =
     _obsUriRegExp.allMatches(line, prefixMatch.end).toList();
-  return matches.isEmpty ? null : Uri.parse(matches[0].group(0)!);
+  return matches.isEmpty ? null : Uri.parse(matches[0].group(0));
 }
 
 /// Checks that the file exists, otherwise throws a [FileSystemException].
@@ -768,7 +771,7 @@
 ///
 /// Removes the directory [path], then clones the git repository
 /// specified by [repo] to the directory [path].
-Future<int> gitClone({required String path, required String repo}) async {
+Future<int> gitClone({String path, String repo}) async {
   rmTree(Directory(path));
 
   await Directory(path).create(recursive: true);
@@ -789,7 +792,7 @@
 /// Waits a constant duration of [delayDuration] between every retry attempt.
 Future<T> retry<T>(
   FutureOr<T> Function() fn, {
-  FutureOr<bool> Function(Exception)? retryIf,
+  FutureOr<bool> Function(Exception) retryIf,
   int maxAttempts = 5,
   Duration delayDuration = const Duration(seconds: 3),
 }) async {
diff --git a/dev/devicelab/lib/microbenchmarks.dart b/dev/devicelab/lib/microbenchmarks.dart
index 008cc60..4a0ebf2 100644
--- a/dev/devicelab/lib/microbenchmarks.dart
+++ b/dev/devicelab/lib/microbenchmarks.dart
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// @dart = 2.8
+
 import 'dart:async';
 import 'dart:convert';
 import 'dart:io';
diff --git a/dev/devicelab/lib/tasks/analysis.dart b/dev/devicelab/lib/tasks/analysis.dart
index 30fa74e..9bcd1a5 100644
--- a/dev/devicelab/lib/tasks/analysis.dart
+++ b/dev/devicelab/lib/tasks/analysis.dart
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// @dart = 2.8
+
 import 'dart:io';
 
 import 'package:path/path.dart' as path;
diff --git a/dev/devicelab/lib/tasks/build_test_task.dart b/dev/devicelab/lib/tasks/build_test_task.dart
index 1484007..81bdf3f 100644
--- a/dev/devicelab/lib/tasks/build_test_task.dart
+++ b/dev/devicelab/lib/tasks/build_test_task.dart
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// @dart = 2.8
+
 import 'dart:io';
 
 import 'package:args/args.dart';
@@ -16,9 +18,10 @@
 abstract class BuildTestTask {
   BuildTestTask(this.args, {this.workingDirectory, this.runFlutterClean = true,}) {
     final ArgResults argResults = argParser.parse(args);
-    applicationBinaryPath = argResults[kApplicationBinaryPathOption] as String?;
+    applicationBinaryPath = argResults[kApplicationBinaryPathOption] as String;
     buildOnly = argResults[kBuildOnlyFlag] as bool;
     testOnly = argResults[kTestOnlyFlag] as bool;
+
   }
 
   static const String kApplicationBinaryPathOption = 'application-binary-path';
@@ -45,10 +48,10 @@
   /// Path to a built application to use in [test].
   ///
   /// If not given, will default to child's expected location.
-  String? applicationBinaryPath;
+  String applicationBinaryPath;
 
   /// Where the test artifacts are stored, such as performance results.
-  final Directory? workingDirectory;
+  final Directory workingDirectory;
 
   /// Run Flutter build to create [applicationBinaryPath].
   Future<void> build() async {
@@ -90,7 +93,7 @@
   ///
   /// Tasks can override to support default values. Otherwise, it will default
   /// to needing to be passed as an argument in the test runner.
-  String? getApplicationBinaryPath() => applicationBinaryPath;
+  String getApplicationBinaryPath() => applicationBinaryPath;
 
   /// Run this task.
   ///
diff --git a/dev/devicelab/lib/tasks/dart_plugin_registry_tests.dart b/dev/devicelab/lib/tasks/dart_plugin_registry_tests.dart
index c3ee1a8..253cff5 100644
--- a/dev/devicelab/lib/tasks/dart_plugin_registry_tests.dart
+++ b/dev/devicelab/lib/tasks/dart_plugin_registry_tests.dart
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// @dart = 2.8
+
 import 'dart:async';
 import 'dart:convert';
 import 'dart:io';
@@ -13,8 +15,8 @@
 import 'package:path/path.dart' as path;
 
 TaskFunction dartPluginRegistryTest({
-  String? deviceIdOverride,
-  Map<String, String>? environment,
+  String deviceIdOverride,
+  Map<String, String> environment,
 }) {
   final Directory tempDir = Directory.systemTemp
       .createTempSync('flutter_devicelab_dart_plugin_test.');
diff --git a/dev/devicelab/lib/tasks/gallery.dart b/dev/devicelab/lib/tasks/gallery.dart
index 08d0851..0be7bd4 100644
--- a/dev/devicelab/lib/tasks/gallery.dart
+++ b/dev/devicelab/lib/tasks/gallery.dart
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// @dart = 2.8
+
 import 'dart:convert';
 import 'dart:io';
 import 'dart:math' as math;
@@ -53,9 +55,9 @@
   final bool needFullTimeline;
   final String testFile;
   final String timelineSummaryFile;
-  final String? timelineTraceFile;
-  final String? transitionDurationFile;
-  final String? driverFile;
+  final String timelineTraceFile;
+  final String transitionDurationFile;
+  final String driverFile;
 
   Future<TaskResult> call() async {
     final Device device = await devices.workingDevice;
@@ -63,7 +65,7 @@
     final String deviceId = device.deviceId;
     final Directory galleryDirectory = dir('${flutterDirectory.path}/dev/integration_tests/flutter_gallery');
     await inDirectory<void>(galleryDirectory, () async {
-      String? applicationBinaryPath;
+      String applicationBinaryPath;
       if (deviceOperatingSystem == DeviceOperatingSystem.android) {
         section('BUILDING APPLICATION');
         await flutter(
diff --git a/dev/devicelab/lib/tasks/hot_mode_tests.dart b/dev/devicelab/lib/tasks/hot_mode_tests.dart
index 21fb730..ca9eb43 100644
--- a/dev/devicelab/lib/tasks/hot_mode_tests.dart
+++ b/dev/devicelab/lib/tasks/hot_mode_tests.dart
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// @dart = 2.8
+
 import 'dart:async';
 import 'dart:convert';
 import 'dart:io';
@@ -18,7 +20,7 @@
 const String kSourceLine = 'fontSize: (orientation == Orientation.portrait) ? 32.0 : 24.0';
 const String kReplacementLine = 'fontSize: (orientation == Orientation.portrait) ? 34.0 : 24.0';
 
-TaskFunction createHotModeTest({String? deviceIdOverride, Map<String, String>? environment}) {
+TaskFunction createHotModeTest({String deviceIdOverride, Map<String, String> environment}) {
   // This file is modified during the test and needs to be restored at the end.
   final File flutterFrameworkSource = file(path.join(
     flutterDirectory.path, 'packages/flutter/lib/src/widgets/framework.dart',
@@ -33,13 +35,13 @@
     final File benchmarkFile = file(path.join(_editedFlutterGalleryDir.path, 'hot_benchmark.json'));
     rm(benchmarkFile);
     final List<String> options = <String>[
-      '--hot', '-d', deviceIdOverride!, '--benchmark', '--resident',  '--no-android-gradle-daemon', '--no-publish-port', '--verbose',
+      '--hot', '-d', deviceIdOverride, '--benchmark', '--resident',  '--no-android-gradle-daemon', '--no-publish-port', '--verbose',
     ];
     int hotReloadCount = 0;
-    late Map<String, dynamic> smallReloadData;
-    late Map<String, dynamic> mediumReloadData;
-    late Map<String, dynamic> largeReloadData;
-    late Map<String, dynamic> freshRestartReloadsData;
+    Map<String, dynamic> smallReloadData;
+    Map<String, dynamic> mediumReloadData;
+    Map<String, dynamic> largeReloadData;
+    Map<String, dynamic> freshRestartReloadsData;
 
 
     await inDirectory<void>(flutterDirectory, () async {
@@ -215,7 +217,7 @@
 
 Future<Map<String, Object>> captureReloadData(
   List<String> options,
-  Map<String, String>? environment,
+  Map<String, String> environment,
   File benchmarkFile,
   void Function(String, Process) onLine,
 ) async {
@@ -245,7 +247,7 @@
 
   await Future.wait<void>(<Future<void>>[stdoutDone.future, stderrDone.future]);
   await process.exitCode;
-  final Map<String, Object> result = json.decode(benchmarkFile.readAsStringSync()) as Map<String, Object>;
+  final Map<String, dynamic> result = json.decode(benchmarkFile.readAsStringSync()) as Map<String, dynamic>;
   benchmarkFile.deleteSync();
   return result;
 }
diff --git a/dev/devicelab/lib/tasks/integration_tests.dart b/dev/devicelab/lib/tasks/integration_tests.dart
index 7a8f783..7408538 100644
--- a/dev/devicelab/lib/tasks/integration_tests.dart
+++ b/dev/devicelab/lib/tasks/integration_tests.dart
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// @dart = 2.8
+
 import '../framework/devices.dart';
 import '../framework/framework.dart';
 import '../framework/task_result.dart';
diff --git a/dev/devicelab/lib/tasks/microbenchmarks.dart b/dev/devicelab/lib/tasks/microbenchmarks.dart
index 957ab4c..91a6a0a 100644
--- a/dev/devicelab/lib/tasks/microbenchmarks.dart
+++ b/dev/devicelab/lib/tasks/microbenchmarks.dart
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// @dart = 2.8
+
 import 'dart:async';
 import 'dart:io';
 
diff --git a/dev/devicelab/lib/tasks/new_gallery.dart b/dev/devicelab/lib/tasks/new_gallery.dart
index ee53732..afe69a1 100644
--- a/dev/devicelab/lib/tasks/new_gallery.dart
+++ b/dev/devicelab/lib/tasks/new_gallery.dart
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// @dart = 2.8
+
 import 'dart:io';
 
 import 'package:flutter_devicelab/tasks/perf_tests.dart';
diff --git a/dev/devicelab/lib/tasks/perf_tests.dart b/dev/devicelab/lib/tasks/perf_tests.dart
index 7fd1840..e7235ed 100644
--- a/dev/devicelab/lib/tasks/perf_tests.dart
+++ b/dev/devicelab/lib/tasks/perf_tests.dart
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// @dart = 2.8
+
 import 'dart:async';
 import 'dart:convert' show LineSplitter, json, utf8;
 import 'dart:io';
@@ -514,7 +516,7 @@
       final List<Map<String, dynamic>> results = <Map<String, dynamic>>[];
 
       section('Building application');
-      String? applicationBinaryPath;
+      String applicationBinaryPath;
       switch (deviceOperatingSystem) {
         case DeviceOperatingSystem.android:
           await flutter('build', options: <String>[
@@ -584,7 +586,7 @@
                 '-d',
                 device.deviceId,
                 '--out',
-                hostAgent.dumpDirectory!
+                hostAgent.dumpDirectory
                     .childFile('screenshot_startup_failure_$currentFailures.png')
                     .path,
               ],
@@ -628,7 +630,7 @@
       final Device device = await devices.workingDevice;
 
       section('Building application');
-      String? applicationBinaryPath;
+      String applicationBinaryPath;
       switch (deviceOperatingSystem) {
         case DeviceOperatingSystem.android:
           await flutter('build', options: <String>[
@@ -731,7 +733,7 @@
     this.needsFullTimeline = true,
     this.benchmarkScoreKeys,
     this.dartDefine = '',
-    String? resultFilename,
+    String resultFilename,
   }): _resultFilename = resultFilename;
 
   const PerfTest.e2e(
@@ -751,12 +753,12 @@
   /// The main entry-point file of the application, as run on the device.
   final String testTarget;
   // The prefix name of the filename such as `<timelineFileName>.timeline_summary.json`.
-  final String? timelineFileName;
+  final String timelineFileName;
   String get traceFilename => '$timelineFileName.timeline';
   String get resultFilename => _resultFilename ?? '$timelineFileName.timeline_summary';
-  final String? _resultFilename;
+  final String _resultFilename;
   /// The test file to run on the host.
-  final String? testDriver;
+  final String testDriver;
   /// Whether to collect CPU and GPU metrics.
   final bool measureCpuGpu;
   /// Whether to collect memory metrics.
@@ -786,7 +788,7 @@
   ///   if (measureCpuGpu) 'average_gpu_usage',
   /// ]
   /// ```
-  final List<String>? benchmarkScoreKeys;
+  final List<String> benchmarkScoreKeys;
 
   /// Additional flags for `--dart-define` to control the test
   final String dartDefine;
@@ -798,8 +800,8 @@
   @protected
   Future<TaskResult> internalRun({
       bool cacheSkSL = false,
-      String? existingApp,
-      String? writeSkslFileName,
+      String existingApp,
+      String writeSkslFileName,
   }) {
     return inDirectory<TaskResult>(testDirectory, () async {
       final Device device = await devices.workingDevice;
@@ -816,7 +818,7 @@
           '--trace-startup', // Enables "endless" timeline event buffering.
         '-t', testTarget,
         if (testDriver != null)
-          ...<String>['--driver', testDriver!],
+          ...<String>['--driver', testDriver],
         if (existingApp != null)
           ...<String>['--use-existing-app', existingApp],
         if (writeSkslFileName != null)
@@ -888,9 +890,9 @@
     String testTarget,
     String timelineFileName, {
     bool measureCpuGpu = false,
-    String? testDriver,
+    String testDriver,
     bool needsFullTimeline = true,
-    List<String>? benchmarkScoreKeys,
+    List<String> benchmarkScoreKeys,
   }) : super(
     testDirectory,
     testTarget,
@@ -962,7 +964,7 @@
     );
   }
 
-  Future<String> _runApp({String? appBinary, bool cacheSkSL = false, String? skslPath}) async {
+  Future<String> _runApp({String appBinary, bool cacheSkSL = false, String skslPath}) async {
     if (File(_vmserviceFileName).existsSync()) {
       File(_vmserviceFileName).deleteSync();
     }
@@ -1025,9 +1027,9 @@
     });
   }
 
-  late String _flutterPath;
-  late Device _device;
-  late Process _runProcess;
+  String _flutterPath;
+  Device _device;
+  Process _runProcess;
 
   static const String _kVmserviceOutFileName = 'vmservice.out';
 }
@@ -1071,16 +1073,12 @@
   ///
   /// Run a single web compile test for the app under [directory], and store
   /// its metrics with prefix [metric].
-  static Future<Map<String, int>> runSingleBuildTest({
-    required String directory,
-    required String metric,
-    bool measureBuildTime = false,
-  }) {
+  static Future<Map<String, int>> runSingleBuildTest({String directory, String metric, bool measureBuildTime = false}) {
     return inDirectory<Map<String, int>>(directory, () async {
       final Map<String, int> metrics = <String, int>{};
 
       await flutter('packages', options: <String>['get']);
-      final Stopwatch? watch = measureBuildTime ? Stopwatch() : null;
+      final Stopwatch watch = measureBuildTime ? Stopwatch() : null;
       watch?.start();
       await evalFlutter('build', options: <String>[
         'web',
@@ -1093,7 +1091,7 @@
       metrics.addAll(await getSize(outputFileName, metric: metric));
 
       if (measureBuildTime) {
-        metrics['${metric}_dart2js_millis'] = watch!.elapsedMilliseconds;
+        metrics['${metric}_dart2js_millis'] = watch.elapsedMilliseconds;
       }
 
       return metrics;
@@ -1101,7 +1099,7 @@
   }
 
   /// Obtains the size and gzipped size of a file given by [fileName].
-  static Future<Map<String, int>> getSize(String fileName, {required String metric}) async {
+  static Future<Map<String, int>> getSize(String fileName, {String metric}) async {
     final Map<String, int> sizeMetrics = <String, int>{};
 
     final ProcessResult result = await Process.run('du', <String>['-k', fileName]);
@@ -1170,12 +1168,10 @@
         await flutter('build', options: options);
         watch.stop();
         final Directory appBuildDirectory = dir(path.join(cwd, 'build/ios/Release-iphoneos'));
-        final Directory? appBundle = appBuildDirectory
+        final Directory appBundle = appBuildDirectory
             .listSync()
-            .whereType<Directory?>()
-            .singleWhere((Directory? directory) =>
-              directory != null && path.extension(directory.path) == '.app',
-              orElse: () => null);
+            .whereType<Directory>()
+            .singleWhere((Directory directory) => path.extension(directory.path) == '.app', orElse: () => null);
         if (appBundle == null) {
           throw 'Failed to find app bundle in ${appBuildDirectory.path}';
         }
@@ -1230,8 +1226,8 @@
   }
 
   static Future<Map<String, dynamic>> _compileDebug({
-    required bool clean,
-    required String metricKey,
+    @required bool clean,
+    @required String metricKey,
   }) async {
     if (clean) {
       await flutter('clean');
@@ -1294,9 +1290,9 @@
       fileToMetadata[entry.path] = entry;
     }
 
-    final _UnzipListEntry libflutter = fileToMetadata['lib/armeabi-v7a/libflutter.so']!;
-    final _UnzipListEntry libapp = fileToMetadata['lib/armeabi-v7a/libapp.so']!;
-    final _UnzipListEntry license = fileToMetadata['assets/flutter_assets/NOTICES.Z']!;
+    final _UnzipListEntry libflutter = fileToMetadata['lib/armeabi-v7a/libflutter.so'];
+    final _UnzipListEntry libapp = fileToMetadata['lib/armeabi-v7a/libapp.so'];
+    final _UnzipListEntry license = fileToMetadata['assets/flutter_assets/NOTICES.Z'];
 
     return <String, dynamic>{
       'libflutter_uncompressed_bytes': libflutter.uncompressedSize,
@@ -1319,9 +1315,9 @@
 
   /// Completes when the log line specified in the last call to
   /// [prepareForNextMessage] is seen by `adb logcat`.
-  Future<void>? get receivedNextMessage => _receivedNextMessage?.future;
-  Completer<void>? _receivedNextMessage;
-  String? _nextMessage;
+  Future<void> get receivedNextMessage => _receivedNextMessage?.future;
+  Completer<void> _receivedNextMessage;
+  String _nextMessage;
 
   /// Prepares the [receivedNextMessage] future such that it will complete
   /// when `adb logcat` sees a log line with the given `message`.
@@ -1332,8 +1328,8 @@
 
   int get iterationCount => 10;
 
-  Device? get device => _device;
-  Device? _device;
+  Device get device => _device;
+  Device _device;
 
   Future<TaskResult> run() {
     return inDirectory<TaskResult>(project, () async {
@@ -1341,13 +1337,13 @@
       // device.getMemoryStats, etc, aren't implemented for iOS.
 
       _device = await devices.workingDevice;
-      await device!.unlock();
+      await device.unlock();
       await flutter('packages', options: <String>['get']);
 
-      final StreamSubscription<String> adb = device!.logcat.listen(
+      final StreamSubscription<String> adb = device.logcat.listen(
         (String data) {
           if (data.contains('==== MEMORY BENCHMARK ==== $_nextMessage ===='))
-            _receivedNextMessage?.complete();
+            _receivedNextMessage.complete();
         },
       );
 
@@ -1360,12 +1356,12 @@
         assert(_endMemory.length == iteration + 1);
         assert(_diffMemory.length == iteration + 1);
         print('terminating...');
-        await device!.stop(package);
+        await device.stop(package);
         await Future<void>.delayed(const Duration(milliseconds: 10));
       }
 
       await adb.cancel();
-      await flutter('install', options: <String>['--uninstall-only', '-d', device!.deviceId]);
+      await flutter('install', options: <String>['--uninstall-only', '-d', device.deviceId]);
 
       final ListStatistics startMemoryStatistics = ListStatistics(_startMemory);
       final ListStatistics endMemoryStatistics = ListStatistics(_endMemory);
@@ -1396,7 +1392,7 @@
       '--verbose',
       '--release',
       '--no-resident',
-      '-d', device!.deviceId,
+      '-d', device.deviceId,
       test,
     ]);
     print('awaiting "ready" message...');
@@ -1415,7 +1411,7 @@
 
     prepareForNextMessage('DONE');
     print('tapping device...');
-    await device!.tap(100, 100);
+    await device.tap(100, 100);
     print('awaiting "done" message...');
     await receivedNextMessage;
 
@@ -1426,23 +1422,23 @@
   final List<int> _endMemory = <int>[];
   final List<int> _diffMemory = <int>[];
 
-  Map<String, dynamic>? _startMemoryUsage;
+  Map<String, dynamic> _startMemoryUsage;
 
   @protected
   Future<void> recordStart() async {
     assert(_startMemoryUsage == null);
     print('snapshotting memory usage...');
-    _startMemoryUsage = await device!.getMemoryStats(package);
+    _startMemoryUsage = await device.getMemoryStats(package);
   }
 
   @protected
   Future<void> recordEnd() async {
     assert(_startMemoryUsage != null);
     print('snapshotting memory usage...');
-    final Map<String, dynamic> endMemoryUsage = await device!.getMemoryStats(package);
-    _startMemory.add(_startMemoryUsage!['total_kb'] as int);
+    final Map<String, dynamic> endMemoryUsage = await device.getMemoryStats(package);
+    _startMemory.add(_startMemoryUsage['total_kb'] as int);
     _endMemory.add(endMemoryUsage['total_kb'] as int);
-    _diffMemory.add((endMemoryUsage['total_kb'] as int) - (_startMemoryUsage!['total_kb'] as int));
+    _diffMemory.add((endMemoryUsage['total_kb'] as int) - (_startMemoryUsage['total_kb'] as int));
   }
 }
 
@@ -1491,7 +1487,7 @@
     });
   }
 
-  late Device _device;
+  Device _device;
 
   static const String _kJsonFileName = 'devtools_memory.json';
 }
@@ -1509,6 +1505,7 @@
     case ReportedDurationTestFlavor.release:
       return 'release';
   }
+  throw ArgumentError('Unexpected value for enum $flavor');
 }
 
 class ReportedDurationTest {
@@ -1524,8 +1521,8 @@
 
   int get iterationCount => 10;
 
-  Device? get device => _device;
-  Device? _device;
+  Device get device => _device;
+  Device _device;
 
   Future<TaskResult> run() {
     return inDirectory<TaskResult>(project, () async {
@@ -1533,13 +1530,13 @@
       // device.getMemoryStats, etc, aren't implemented for iOS.
 
       _device = await devices.workingDevice;
-      await device!.unlock();
+      await device.unlock();
       await flutter('packages', options: <String>['get']);
 
-      final StreamSubscription<String> adb = device!.logcat.listen(
+      final StreamSubscription<String> adb = device.logcat.listen(
         (String data) {
           if (durationPattern.hasMatch(data))
-            durationCompleter.complete(int.parse(durationPattern.firstMatch(data)!.group(1)!));
+            durationCompleter.complete(int.parse(durationPattern.firstMatch(data).group(1)));
         },
       );
       print('launching $project$test on device...');
@@ -1549,13 +1546,13 @@
         '--no-fast-start',
         '--${_reportedDurationTestToString(flavor)}',
         '--no-resident',
-        '-d', device!.deviceId,
+        '-d', device.deviceId,
         test,
       ]);
 
       final int duration = await durationCompleter.future;
       print('terminating...');
-      await device!.stop(package);
+      await device.stop(package);
       await adb.cancel();
 
       _device = null;
@@ -1610,9 +1607,9 @@
   }
 
   _UnzipListEntry._({
-    required this.uncompressedSize,
-    required this.compressedSize,
-    required this.path,
+    @required this.uncompressedSize,
+    @required this.compressedSize,
+    @required this.path,
   }) : assert(uncompressedSize != null),
        assert(compressedSize != null),
        assert(compressedSize <= uncompressedSize),
@@ -1636,7 +1633,7 @@
   throw StateError('Did not find vmservice out file after 400 seconds');
 }
 
-String? _findIosAppInBuildDirectory(String searchDirectory) {
+String _findIosAppInBuildDirectory(String searchDirectory) {
   for (final FileSystemEntity entity in Directory(searchDirectory).listSync()) {
     if (entity.path.endsWith('.app')) {
       return entity.path;
diff --git a/dev/devicelab/lib/tasks/platform_channels_benchmarks.dart b/dev/devicelab/lib/tasks/platform_channels_benchmarks.dart
index 767e888..361d050 100644
--- a/dev/devicelab/lib/tasks/platform_channels_benchmarks.dart
+++ b/dev/devicelab/lib/tasks/platform_channels_benchmarks.dart
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// @dart = 2.8
+
 import 'dart:io' show Process, Directory;
 
 import 'package:flutter_devicelab/framework/devices.dart' as adb;
diff --git a/dev/devicelab/lib/tasks/plugin_tests.dart b/dev/devicelab/lib/tasks/plugin_tests.dart
index 445b40f..47875f8 100644
--- a/dev/devicelab/lib/tasks/plugin_tests.dart
+++ b/dev/devicelab/lib/tasks/plugin_tests.dart
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// @dart = 2.8
+
 import 'dart:io';
 
 import 'package:flutter_devicelab/framework/framework.dart';
@@ -29,8 +31,8 @@
 
   final String buildTarget;
   final List<String> options;
-  final Map<String, String>? pluginCreateEnvironment;
-  final Map<String, String>? appCreateEnvironment;
+  final Map<String, String> pluginCreateEnvironment;
+  final Map<String, String> appCreateEnvironment;
 
   Future<TaskResult> call() async {
     final Directory tempDir =
@@ -75,7 +77,7 @@
 
   String get rootPath => path.join(parent.path, name);
 
-  Future<void> addPlugin(String plugin, {String? pluginPath}) async {
+  Future<void> addPlugin(String plugin, {String pluginPath}) async {
     final File pubspec = File(path.join(rootPath, 'pubspec.yaml'));
     String content = await pubspec.readAsString();
     final String dependency =
@@ -98,9 +100,9 @@
       List<String> options,
       String target,
       {
-        required String name,
-        required String template,
-        Map<String, String>? environment,
+        String name,
+        String template,
+        Map<String, String> environment,
       }) async {
     await inDirectory(directory, () async {
       await flutter(
diff --git a/dev/devicelab/lib/tasks/web_benchmarks.dart b/dev/devicelab/lib/tasks/web_benchmarks.dart
index cdafead..77af15d 100644
--- a/dev/devicelab/lib/tasks/web_benchmarks.dart
+++ b/dev/devicelab/lib/tasks/web_benchmarks.dart
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// @dart = 2.8
+
 import 'dart:async';
 import 'dart:convert' show json;
 import 'dart:io' as io;
@@ -11,6 +13,7 @@
 import 'package:flutter_devicelab/framework/task_result.dart';
 import 'package:flutter_devicelab/framework/utils.dart';
 import 'package:logging/logging.dart';
+import 'package:meta/meta.dart';
 import 'package:path/path.dart' as path;
 import 'package:shelf/shelf.dart';
 import 'package:shelf/shelf_io.dart' as shelf_io;
@@ -20,7 +23,7 @@
 const int benchmarkServerPort = 9999;
 const int chromeDebugPort = 10000;
 
-Future<TaskResult> runWebBenchmark({ required bool useCanvasKit }) async {
+Future<TaskResult> runWebBenchmark({ @required bool useCanvasKit }) async {
   // Reduce logging level. Otherwise, package:webkit_inspection_protocol is way too spammy.
   Logger.root.level = Level.INFO;
   final String macrobenchmarksDirectory = path.join(flutterDirectory.path, 'dev', 'benchmarks', 'macrobenchmarks');
@@ -35,17 +38,17 @@
     ]);
     final Completer<List<Map<String, dynamic>>> profileData = Completer<List<Map<String, dynamic>>>();
     final List<Map<String, dynamic>> collectedProfiles = <Map<String, dynamic>>[];
-    List<String>? benchmarks;
-    late Iterator<String> benchmarkIterator;
+    List<String> benchmarks;
+    Iterator<String> benchmarkIterator;
 
     // This future fixes a race condition between the web-page loading and
     // asking to run a benchmark, and us connecting to Chrome's DevTools port.
     // Sometime one wins. Other times, the other wins.
-    Future<Chrome>? whenChromeIsReady;
-    Chrome? chrome;
-    late io.HttpServer server;
+    Future<Chrome> whenChromeIsReady;
+    Chrome chrome;
+    io.HttpServer server;
     Cascade cascade = Cascade();
-    List<Map<String, dynamic>>? latestPerformanceTrace;
+    List<Map<String, dynamic>> latestPerformanceTrace;
     cascade = cascade.add((Request request) async {
       try {
         chrome ??= await whenChromeIsReady;
@@ -63,7 +66,7 @@
 
           // Trace data is null when the benchmark is not frame-based, such as RawRecorder.
           if (latestPerformanceTrace != null) {
-            final BlinkTraceSummary traceSummary = BlinkTraceSummary.fromJson(latestPerformanceTrace!)!;
+            final BlinkTraceSummary traceSummary = BlinkTraceSummary.fromJson(latestPerformanceTrace);
             profile['totalUiFrame.average'] = traceSummary.averageTotalUIFrameTime.inMicroseconds;
             profile['scoreKeys'] ??= <dynamic>[]; // using dynamic for consistency with JSON
             (profile['scoreKeys'] as List<dynamic>).add('totalUiFrame.average');
@@ -73,10 +76,10 @@
           return Response.ok('Profile received');
         } else if (request.requestedUri.path.endsWith('/start-performance-tracing')) {
           latestPerformanceTrace = null;
-          await chrome!.beginRecordingPerformance(request.requestedUri.queryParameters['label']!);
+          await chrome.beginRecordingPerformance(request.requestedUri.queryParameters['label']);
           return Response.ok('Started performance tracing');
         } else if (request.requestedUri.path.endsWith('/stop-performance-tracing')) {
-          latestPerformanceTrace = await chrome!.endRecordingPerformance();
+          latestPerformanceTrace = await chrome.endRecordingPerformance();
           return Response.ok('Stopped performance tracing');
         } else if (request.requestedUri.path.endsWith('/on-error')) {
           final Map<String, dynamic> errorDetails = json.decode(await request.readAsString()) as Map<String, dynamic>;
@@ -87,7 +90,7 @@
         } else if (request.requestedUri.path.endsWith('/next-benchmark')) {
           if (benchmarks == null) {
             benchmarks = (json.decode(await request.readAsString()) as List<dynamic>).cast<String>();
-            benchmarkIterator = benchmarks!.iterator;
+            benchmarkIterator = benchmarks.iterator;
           }
           if (benchmarkIterator.moveNext()) {
             final String nextBenchmark = benchmarkIterator.current;
@@ -183,7 +186,7 @@
       }
       return TaskResult.success(taskResult, benchmarkScoreKeys: benchmarkScoreKeys);
     } finally {
-      unawaited(server.close());
+      unawaited(server?.close());
       chrome?.stop();
     }
   });
diff --git a/dev/devicelab/lib/tasks/web_dev_mode_tests.dart b/dev/devicelab/lib/tasks/web_dev_mode_tests.dart
index 22aa5ad..6164328 100644
--- a/dev/devicelab/lib/tasks/web_dev_mode_tests.dart
+++ b/dev/devicelab/lib/tasks/web_dev_mode_tests.dart
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// @dart = 2.8
+
 import 'dart:async';
 import 'dart:convert';
 import 'dart:io';