Merge branch 'master' into update_nnbd
diff --git a/.cirrus.yml b/.cirrus.yml
index 8c632a0..4762407 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -37,7 +37,7 @@
         # https://github.com/flutter/flutter/issues/42864
         - if [[ "$CHANNEL" -eq "stable" ]]; then find . | grep _web$ | xargs rm -rf; fi
         - flutter channel $CHANNEL
-        - ./script/incremental_build.sh test
+        - ./script/incremental_build.sh test --enable-experiment=non-nullable
     - name: analyze
       script: ./script/incremental_build.sh analyze
     - name: build_all_plugins_apk
@@ -87,13 +87,13 @@
         - echo "$CIRRUS_COMMIT_MESSAGE" > /tmp/cirrus_commit_message.txt
         - export CIRRUS_CHANGE_MESSAGE=""
         - export CIRRUS_COMMIT_MESSAGE=""
-        - ./script/incremental_build.sh build-examples --apk
+        - ./script/incremental_build.sh build-examples --apk --enable-experiment=non-nullable
         - ./script/incremental_build.sh java-test  # must come after apk build
         - if [[ $GCLOUD_FIREBASE_TESTLAB_KEY == ENCRYPTED* ]]; then
         -   echo "This user does not have permission to run Firebase Test Lab tests."
         - else
         -   echo $GCLOUD_FIREBASE_TESTLAB_KEY > ${HOME}/gcloud-service-key.json
-        -   ./script/incremental_build.sh firebase-test-lab --device model=flame,version=29 --device model=starqlteue,version=26
+        -   ./script/incremental_build.sh firebase-test-lab --device model=flame,version=29 --device model=starqlteue,version=26 --enable-experiment=non-nullable
         - fi
         - export CIRRUS_CHANGE_MESSAGE=`cat /tmp/cirrus_change_message.txt`
         - export CIRRUS_COMMIT_MESSAGE=`cat /tmp/cirrus_commit_message.txt`
@@ -178,9 +178,9 @@
         # https://github.com/flutter/flutter/issues/42864
         - if [[ "$CHANNEL" -eq "stable" ]]; then find . | grep _web$ | xargs rm -rf; fi
         - flutter channel $CHANNEL
-        - ./script/incremental_build.sh build-examples --ipa
-        - ./script/incremental_build.sh drive-examples
-        - ./script/incremental_build.sh xctest --target RunnerUITests --skip $PLUGINS_TO_SKIP_XCTESTS
+        - ./script/incremental_build.sh build-examples --ipa --enable-experiment=non-nullable
+        - ./script/incremental_build.sh drive-examples --enable-experiment=non-nullable
+        - ./script/incremental_build.sh xctest --target RunnerUITests --skip $PLUGINS_TO_SKIP_XCTESTS --enable-experiment=non-nullable
 task:
   # don't run on release tags since it creates O(n^2) tasks where n is the number of plugins
   only_if: $CIRRUS_TAG == ''
diff --git a/packages/connectivity/connectivity/CHANGELOG.md b/packages/connectivity/connectivity/CHANGELOG.md
index 8e2802a..cc6897b 100644
--- a/packages/connectivity/connectivity/CHANGELOG.md
+++ b/packages/connectivity/connectivity/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 3.0.0-nullsafety
+
+* Migrate to null safety.
+
 ## 2.0.0
 
 * [Breaking Change] The `getWifiName`, `getWifiBSSID` and `getWifiIP` are removed to [wifi_info_flutter](https://github.com/flutter/plugins/tree/master/packages/wifi_info_flutter)
diff --git a/packages/connectivity/connectivity/analysis_options.yaml b/packages/connectivity/connectivity/analysis_options.yaml
new file mode 100644
index 0000000..3d64bb5
--- /dev/null
+++ b/packages/connectivity/connectivity/analysis_options.yaml
@@ -0,0 +1,4 @@
+include: ../../../analysis_options.yaml
+analyzer:
+  enable-experiment:
+    - non-nullable
diff --git a/packages/connectivity/connectivity/example/lib/main.dart b/packages/connectivity/connectivity/example/lib/main.dart
index e054971..19285ce 100644
--- a/packages/connectivity/connectivity/example/lib/main.dart
+++ b/packages/connectivity/connectivity/example/lib/main.dart
@@ -40,7 +40,7 @@
 }
 
 class MyHomePage extends StatefulWidget {
-  MyHomePage({Key key, this.title}) : super(key: key);
+  MyHomePage({Key? key, required this.title}) : super(key: key);
 
   final String title;
 
@@ -51,7 +51,7 @@
 class _MyHomePageState extends State<MyHomePage> {
   String _connectionStatus = 'Unknown';
   final Connectivity _connectivity = Connectivity();
-  StreamSubscription<ConnectivityResult> _connectivitySubscription;
+  late StreamSubscription<ConnectivityResult> _connectivitySubscription;
 
   @override
   void initState() {
@@ -69,7 +69,7 @@
 
   // Platform messages are asynchronous, so we initialize in an async method.
   Future<void> initConnectivity() async {
-    ConnectivityResult result;
+    ConnectivityResult result = ConnectivityResult.none;
     // Platform messages may fail, so we use a try/catch PlatformException.
     try {
       result = await _connectivity.checkConnectivity();
diff --git a/packages/connectivity/connectivity/example/pubspec.yaml b/packages/connectivity/connectivity/example/pubspec.yaml
index 1d07f7d..976ff6e 100644
--- a/packages/connectivity/connectivity/example/pubspec.yaml
+++ b/packages/connectivity/connectivity/example/pubspec.yaml
@@ -10,10 +10,10 @@
 dev_dependencies:
   flutter_driver:
     sdk: flutter
-  test: any
+  test: ^1.10.0-nullsafety.1
   integration_test:
     path: ../../../integration_test
-  pedantic: ^1.8.0
+  pedantic: ^1.10.0-nullsafety.1
 
 flutter:
   uses-material-design: true
diff --git a/packages/connectivity/connectivity/example/test_driver/integration_test/connectivity_test.dart b/packages/connectivity/connectivity/example/test_driver/integration_test/connectivity_test.dart
index d48deae..3177b66 100644
--- a/packages/connectivity/connectivity/example/test_driver/integration_test/connectivity_test.dart
+++ b/packages/connectivity/connectivity/example/test_driver/integration_test/connectivity_test.dart
@@ -2,6 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// TODO(cyanglaz): Remove once https://github.com/flutter/plugins/pull/3158 is landed.
+// @dart = 2.9
+
 import 'package:integration_test/integration_test.dart';
 import 'package:flutter_test/flutter_test.dart';
 import 'package:connectivity/connectivity.dart';
diff --git a/packages/connectivity/connectivity/integration_test/connectivity_test.dart b/packages/connectivity/connectivity/integration_test/connectivity_test.dart
index d48deae..3177b66 100644
--- a/packages/connectivity/connectivity/integration_test/connectivity_test.dart
+++ b/packages/connectivity/connectivity/integration_test/connectivity_test.dart
@@ -2,6 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// TODO(cyanglaz): Remove once https://github.com/flutter/plugins/pull/3158 is landed.
+// @dart = 2.9
+
 import 'package:integration_test/integration_test.dart';
 import 'package:flutter_test/flutter_test.dart';
 import 'package:connectivity/connectivity.dart';
diff --git a/packages/connectivity/connectivity/lib/connectivity.dart b/packages/connectivity/connectivity/lib/connectivity.dart
index c965536..0f30a93 100644
--- a/packages/connectivity/connectivity/lib/connectivity.dart
+++ b/packages/connectivity/connectivity/lib/connectivity.dart
@@ -22,12 +22,12 @@
     if (_singleton == null) {
       _singleton = Connectivity._();
     }
-    return _singleton;
+    return _singleton!;
   }
 
   Connectivity._();
 
-  static Connectivity _singleton;
+  static Connectivity? _singleton;
 
   static ConnectivityPlatform get _platform => ConnectivityPlatform.instance;
 
diff --git a/packages/connectivity/connectivity/pubspec.yaml b/packages/connectivity/connectivity/pubspec.yaml
index af26f70..063b45e 100644
--- a/packages/connectivity/connectivity/pubspec.yaml
+++ b/packages/connectivity/connectivity/pubspec.yaml
@@ -2,7 +2,7 @@
 description: Flutter plugin for discovering the state of the network (WiFi &
   mobile/cellular) connectivity on Android and iOS.
 homepage: https://github.com/flutter/plugins/tree/master/packages/connectivity/connectivity
-version: 2.0.0
+version: 3.0.0-nullsafety
 
 flutter:
   plugin:
@@ -21,22 +21,24 @@
   flutter:
     sdk: flutter
   meta: ^1.0.5
-  connectivity_platform_interface: ^1.0.2
+  connectivity_platform_interface: 2.0.0-nullsafety
+  #TODO(cyanglaz): re-endorse the below plugins when they have migrated to nnbd.
+  # https://github.com/flutter/flutter/issues/68669
   connectivity_macos: ^0.1.0
-  connectivity_for_web: ^0.3.0
+  # connectivity_for_web: ^0.3.0
 
 dev_dependencies:
   flutter_test:
     sdk: flutter
   flutter_driver:
     sdk: flutter
-  test: any
+  test: ^1.10.0-nullsafety.1
   integration_test:
     path: ../../integration_test
   mockito: ^4.1.1
-  plugin_platform_interface: ^1.0.0
-  pedantic: ^1.8.0
+  plugin_platform_interface: 1.1.0-nullsafety
+  pedantic: ^1.10.0-nullsafety.1
 
 environment:
-  sdk: ">=2.1.0 <3.0.0"
+  sdk: '>=2.10.0-56.0.dev <3.0.0'
   flutter: ">=1.12.13+hotfix.5 <2.0.0"
diff --git a/packages/connectivity/connectivity/test/connectivity_test.dart b/packages/connectivity/connectivity/test/connectivity_test.dart
index b7749ca..e831965 100644
--- a/packages/connectivity/connectivity/test/connectivity_test.dart
+++ b/packages/connectivity/connectivity/test/connectivity_test.dart
@@ -2,8 +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
-
+// TODO(cyanglaz): Remove once Mockito is migrated to null safety.
+// @dart = 2.9
 import 'package:connectivity/connectivity.dart';
 import 'package:connectivity_platform_interface/connectivity_platform_interface.dart';
 import 'package:flutter_test/flutter_test.dart';
diff --git a/packages/connectivity/connectivity_platform_interface/CHANGELOG.md b/packages/connectivity/connectivity_platform_interface/CHANGELOG.md
index e45f8f7..dec2752 100644
--- a/packages/connectivity/connectivity_platform_interface/CHANGELOG.md
+++ b/packages/connectivity/connectivity_platform_interface/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 2.0.0-nullsafety
+
+* Migrate to null safety.
+
 ## 1.0.6
 
 * Update lower bound of dart dependency to 2.1.0.
diff --git a/packages/connectivity/connectivity_platform_interface/analysis_options.yaml b/packages/connectivity/connectivity_platform_interface/analysis_options.yaml
new file mode 100644
index 0000000..3d64bb5
--- /dev/null
+++ b/packages/connectivity/connectivity_platform_interface/analysis_options.yaml
@@ -0,0 +1,4 @@
+include: ../../../analysis_options.yaml
+analyzer:
+  enable-experiment:
+    - non-nullable
diff --git a/packages/connectivity/connectivity_platform_interface/lib/connectivity_platform_interface.dart b/packages/connectivity/connectivity_platform_interface/lib/connectivity_platform_interface.dart
index cfd9cf6..8e9f0fd 100644
--- a/packages/connectivity/connectivity_platform_interface/lib/connectivity_platform_interface.dart
+++ b/packages/connectivity/connectivity_platform_interface/lib/connectivity_platform_interface.dart
@@ -50,17 +50,17 @@
   }
 
   /// Obtains the wifi name (SSID) of the connected network
-  Future<String> getWifiName() {
+  Future<String?> getWifiName() {
     throw UnimplementedError('getWifiName() has not been implemented.');
   }
 
   /// Obtains the wifi BSSID of the connected network.
-  Future<String> getWifiBSSID() {
+  Future<String?> getWifiBSSID() {
     throw UnimplementedError('getWifiBSSID() has not been implemented.');
   }
 
   /// Obtains the IP address of the connected wifi network
-  Future<String> getWifiIP() {
+  Future<String?> getWifiIP() {
     throw UnimplementedError('getWifiIP() has not been implemented.');
   }
 
diff --git a/packages/connectivity/connectivity_platform_interface/lib/src/method_channel_connectivity.dart b/packages/connectivity/connectivity_platform_interface/lib/src/method_channel_connectivity.dart
index 87deaa2..b411b5b 100644
--- a/packages/connectivity/connectivity_platform_interface/lib/src/method_channel_connectivity.dart
+++ b/packages/connectivity/connectivity_platform_interface/lib/src/method_channel_connectivity.dart
@@ -22,29 +22,29 @@
   EventChannel eventChannel =
       EventChannel('plugins.flutter.io/connectivity_status');
 
-  Stream<ConnectivityResult> _onConnectivityChanged;
+  Stream<ConnectivityResult>? _onConnectivityChanged;
 
   /// Fires whenever the connectivity state changes.
   Stream<ConnectivityResult> get onConnectivityChanged {
     if (_onConnectivityChanged == null) {
-      _onConnectivityChanged = eventChannel
-          .receiveBroadcastStream()
-          .map((dynamic result) => result.toString())
-          .map(parseConnectivityResult);
+      _onConnectivityChanged =
+          eventChannel.receiveBroadcastStream().map((dynamic result) {
+        return result != null ? result.toString() : '';
+      }).map(parseConnectivityResult);
     }
-    return _onConnectivityChanged;
+    return _onConnectivityChanged!;
   }
 
   @override
-  Future<ConnectivityResult> checkConnectivity() {
-    return methodChannel
-        .invokeMethod<String>('check')
-        .then(parseConnectivityResult);
+  Future<ConnectivityResult> checkConnectivity() async {
+    final String checkResult =
+        await methodChannel.invokeMethod<String>('check') ?? '';
+    return parseConnectivityResult(checkResult);
   }
 
   @override
-  Future<String> getWifiName() async {
-    String wifiName = await methodChannel.invokeMethod<String>('wifiName');
+  Future<String?> getWifiName() async {
+    String? wifiName = await methodChannel.invokeMethod<String>('wifiName');
     // as Android might return <unknown ssid>, uniforming result
     // our iOS implementation will return null
     if (wifiName == '<unknown ssid>') {
@@ -54,29 +54,31 @@
   }
 
   @override
-  Future<String> getWifiBSSID() {
+  Future<String?> getWifiBSSID() {
     return methodChannel.invokeMethod<String>('wifiBSSID');
   }
 
   @override
-  Future<String> getWifiIP() {
+  Future<String?> getWifiIP() {
     return methodChannel.invokeMethod<String>('wifiIPAddress');
   }
 
   @override
   Future<LocationAuthorizationStatus> requestLocationServiceAuthorization({
     bool requestAlwaysLocationUsage = false,
-  }) {
-    return methodChannel.invokeMethod<String>(
-        'requestLocationServiceAuthorization', <bool>[
-      requestAlwaysLocationUsage
-    ]).then(parseLocationAuthorizationStatus);
+  }) async {
+    final String requestLocationServiceResult = await methodChannel
+            .invokeMethod<String>('requestLocationServiceAuthorization',
+                <bool>[requestAlwaysLocationUsage]) ??
+        '';
+    return parseLocationAuthorizationStatus(requestLocationServiceResult);
   }
 
   @override
-  Future<LocationAuthorizationStatus> getLocationServiceAuthorization() {
-    return methodChannel
-        .invokeMethod<String>('getLocationServiceAuthorization')
-        .then(parseLocationAuthorizationStatus);
+  Future<LocationAuthorizationStatus> getLocationServiceAuthorization() async {
+    final String getLocationServiceResult = await methodChannel
+            .invokeMethod<String>('getLocationServiceAuthorization') ??
+        '';
+    return parseLocationAuthorizationStatus(getLocationServiceResult);
   }
 }
diff --git a/packages/connectivity/connectivity_platform_interface/pubspec.yaml b/packages/connectivity/connectivity_platform_interface/pubspec.yaml
index 5bafcf9..f26107c 100644
--- a/packages/connectivity/connectivity_platform_interface/pubspec.yaml
+++ b/packages/connectivity/connectivity_platform_interface/pubspec.yaml
@@ -3,19 +3,19 @@
 homepage: https://github.com/flutter/plugins/tree/master/packages/connectivity/connectivity_platform_interface
 # NOTE: We strongly prefer non-breaking changes, even at the expense of a
 # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes
-version: 1.0.6
+version: 2.0.0-nullsafety
 
 dependencies:
   flutter:
     sdk: flutter
-  meta: ^1.0.5
-  plugin_platform_interface: ^1.0.1
+  meta: ^1.3.0-nullsafety.3
+  plugin_platform_interface: 1.1.0-nullsafety
 
 dev_dependencies:
   flutter_test:
     sdk: flutter
-  pedantic: ^1.8.0
+  pedantic: ^1.10.0-nullsafety.1
 
 environment:
-  sdk: ">=2.1.0 <3.0.0"
+  sdk: '>=2.10.0-56.0.dev <3.0.0'
   flutter: ">=1.12.13+hotfix.5 <2.0.0"
diff --git a/packages/connectivity/connectivity_platform_interface/test/method_channel_connectivity_test.dart b/packages/connectivity/connectivity_platform_interface/test/method_channel_connectivity_test.dart
index 3d9c405..0c30530 100644
--- a/packages/connectivity/connectivity_platform_interface/test/method_channel_connectivity_test.dart
+++ b/packages/connectivity/connectivity_platform_interface/test/method_channel_connectivity_test.dart
@@ -12,7 +12,7 @@
 
   group('$MethodChannelConnectivity', () {
     final List<MethodCall> log = <MethodCall>[];
-    MethodChannelConnectivity methodChannelConnectivity;
+    late MethodChannelConnectivity methodChannelConnectivity;
 
     setUp(() async {
       methodChannelConnectivity = MethodChannelConnectivity();
@@ -42,7 +42,7 @@
           .setMockMethodCallHandler((MethodCall methodCall) async {
         switch (methodCall.method) {
           case 'listen':
-            await ServicesBinding.instance.defaultBinaryMessenger
+            await ServicesBinding.instance!.defaultBinaryMessenger
                 .handlePlatformMessage(
               methodChannelConnectivity.eventChannel.name,
               methodChannelConnectivity.eventChannel.codec
@@ -64,7 +64,7 @@
     });
 
     test('getWifiName', () async {
-      final String result = await methodChannelConnectivity.getWifiName();
+      final String? result = await methodChannelConnectivity.getWifiName();
       expect(result, '1337wifi');
       expect(
         log,
@@ -78,7 +78,7 @@
     });
 
     test('getWifiBSSID', () async {
-      final String result = await methodChannelConnectivity.getWifiBSSID();
+      final String? result = await methodChannelConnectivity.getWifiBSSID();
       expect(result, 'c0:ff:33:c0:d3:55');
       expect(
         log,
@@ -92,7 +92,7 @@
     });
 
     test('getWifiIP', () async {
-      final String result = await methodChannelConnectivity.getWifiIP();
+      final String? result = await methodChannelConnectivity.getWifiIP();
       expect(result, '127.0.0.1');
       expect(
         log,
diff --git a/packages/device_info/device_info/CHANGELOG.md b/packages/device_info/device_info/CHANGELOG.md
index 836d562..763fd9f 100644
--- a/packages/device_info/device_info/CHANGELOG.md
+++ b/packages/device_info/device_info/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 2.0.0-nullsafety
+
+* Migrate to null safety.
+
 ## 0.4.2+9
 
 * Update android compileSdkVersion to 29.
diff --git a/packages/device_info/device_info/analysis_options.yaml b/packages/device_info/device_info/analysis_options.yaml
new file mode 100644
index 0000000..3d64bb5
--- /dev/null
+++ b/packages/device_info/device_info/analysis_options.yaml
@@ -0,0 +1,4 @@
+include: ../../../analysis_options.yaml
+analyzer:
+  enable-experiment:
+    - non-nullable
diff --git a/packages/device_info/device_info/example/integration_test/device_info_test.dart b/packages/device_info/device_info/example/integration_test/device_info_test.dart
index 2fd1d9a..61c4396 100644
--- a/packages/device_info/device_info/example/integration_test/device_info_test.dart
+++ b/packages/device_info/device_info/example/integration_test/device_info_test.dart
@@ -2,6 +2,9 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+// TODO(cyanglaz): Remove once https://github.com/flutter/plugins/pull/3158 is landed.
+// @dart = 2.9
+
 import 'dart:io';
 import 'package:flutter_test/flutter_test.dart';
 import 'package:device_info/device_info.dart';
diff --git a/packages/device_info/device_info/example/lib/main.dart b/packages/device_info/device_info/example/lib/main.dart
index 1c1064a..3aded93 100644
--- a/packages/device_info/device_info/example/lib/main.dart
+++ b/packages/device_info/device_info/example/lib/main.dart
@@ -36,7 +36,7 @@
   }
 
   Future<void> initPlatformState() async {
-    Map<String, dynamic> deviceData;
+    Map<String, dynamic> deviceData = <String, dynamic>{};
 
     try {
       if (Platform.isAndroid) {
diff --git a/packages/device_info/device_info/example/pubspec.yaml b/packages/device_info/device_info/example/pubspec.yaml
index e22f602..9a9556a 100644
--- a/packages/device_info/device_info/example/pubspec.yaml
+++ b/packages/device_info/device_info/example/pubspec.yaml
@@ -12,7 +12,10 @@
     sdk: flutter
   integration_test:
     path: ../../../integration_test
-  pedantic: ^1.8.0
+  pedantic: ^1.10.0-nullsafety.1
 
 flutter:
   uses-material-design: true
+
+environment:
+  sdk: '>=2.10.0-56.0.dev <3.0.0'
diff --git a/packages/device_info/device_info/example/test_driver/integration_test.dart b/packages/device_info/device_info/example/test_driver/integration_test.dart
index 7a2c213..13327bb 100644
--- a/packages/device_info/device_info/example/test_driver/integration_test.dart
+++ b/packages/device_info/device_info/example/test_driver/integration_test.dart
@@ -2,6 +2,9 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+// TODO(cyanglaz): Remove once https://github.com/flutter/flutter/issues/59879 is fixed.
+// @dart = 2.9
+
 import 'dart:async';
 import 'dart:convert';
 import 'dart:io';
diff --git a/packages/device_info/device_info/lib/device_info.dart b/packages/device_info/device_info/lib/device_info.dart
index f63730c..bccc3d2 100644
--- a/packages/device_info/device_info/lib/device_info.dart
+++ b/packages/device_info/device_info/lib/device_info.dart
@@ -15,7 +15,7 @@
   DeviceInfoPlugin();
 
   /// This information does not change from call to call. Cache it.
-  AndroidDeviceInfo _cachedAndroidDeviceInfo;
+  AndroidDeviceInfo? _cachedAndroidDeviceInfo;
 
   /// Information derived from `android.os.Build`.
   ///
@@ -25,7 +25,7 @@
           await DeviceInfoPlatform.instance.androidInfo();
 
   /// This information does not change from call to call. Cache it.
-  IosDeviceInfo _cachedIosDeviceInfo;
+  IosDeviceInfo? _cachedIosDeviceInfo;
 
   /// Information derived from `UIDevice`.
   ///
diff --git a/packages/device_info/device_info/pubspec.yaml b/packages/device_info/device_info/pubspec.yaml
index bf36bf6..fed08f9 100644
--- a/packages/device_info/device_info/pubspec.yaml
+++ b/packages/device_info/device_info/pubspec.yaml
@@ -5,7 +5,7 @@
 # 0.4.y+z is compatible with 1.0.0, if you land a breaking change bump
 # the version to 2.0.0.
 # See more details: https://github.com/flutter/flutter/wiki/Package-migration-to-1.0.0
-version: 0.4.2+9
+version: 2.0.0-nullsafety
 
 flutter:
   plugin:
@@ -19,14 +19,13 @@
 dependencies:
   flutter:
     sdk: flutter
-  device_info_platform_interface: ^1.0.0
-
+  device_info_platform_interface: ^2.0.0-nullsafety
 dev_dependencies:
-  test: ^1.3.0
+  test: ^1.10.0-nullsafety.1
   flutter_test:
     sdk: flutter
-  pedantic: ^1.8.0
+  pedantic: ^1.10.0-nullsafety.1
 
 environment:
-  sdk: ">=2.1.0<3.0.0"
+  sdk: '>=2.10.0-56.0.dev <3.0.0'
   flutter: ">=1.12.13+hotfix.5 <2.0.0"
diff --git a/packages/device_info/device_info_platform_interface/CHANGELOG.md b/packages/device_info/device_info_platform_interface/CHANGELOG.md
index 8a7eb6c..fe9ded6 100644
--- a/packages/device_info/device_info_platform_interface/CHANGELOG.md
+++ b/packages/device_info/device_info_platform_interface/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 2.0.0-nullsafety
+
+* Migrate to null safety.
+
 ## 1.0.1
 
 - Documentation typo fixed.
diff --git a/packages/device_info/device_info_platform_interface/analysis_options.yaml b/packages/device_info/device_info_platform_interface/analysis_options.yaml
new file mode 100644
index 0000000..32ff54f
--- /dev/null
+++ b/packages/device_info/device_info_platform_interface/analysis_options.yaml
@@ -0,0 +1,4 @@
+include: ../../../analysis_options.yaml
+analyzer:
+  enable-experiment:
+    - non-nullable
\ No newline at end of file
diff --git a/packages/device_info/device_info_platform_interface/lib/model/android_device_info.dart b/packages/device_info/device_info_platform_interface/lib/model/android_device_info.dart
index 5b326cc..ea5839d 100644
--- a/packages/device_info/device_info_platform_interface/lib/model/android_device_info.dart
+++ b/packages/device_info/device_info_platform_interface/lib/model/android_device_info.dart
@@ -8,28 +8,28 @@
 class AndroidDeviceInfo {
   /// Android device Info class.
   AndroidDeviceInfo({
-    this.version,
-    this.board,
-    this.bootloader,
-    this.brand,
-    this.device,
-    this.display,
-    this.fingerprint,
-    this.hardware,
-    this.host,
-    this.id,
-    this.manufacturer,
-    this.model,
-    this.product,
-    List<String> supported32BitAbis,
-    List<String> supported64BitAbis,
-    List<String> supportedAbis,
-    this.tags,
-    this.type,
-    this.isPhysicalDevice,
-    this.androidId,
-    List<String> systemFeatures,
-  })  : supported32BitAbis = List<String>.unmodifiable(supported32BitAbis),
+    required this.version,
+    required this.board,
+    required this.bootloader,
+    required this.brand,
+    required this.device,
+    required this.display,
+    required this.fingerprint,
+    required this.hardware,
+    required this.host,
+    required this.id,
+    required this.manufacturer,
+    required this.model,
+    required this.product,
+    required List<String> supported32BitAbis,
+    required List<String> supported64BitAbis,
+    required List<String> supportedAbis,
+    required this.tags,
+    required this.type,
+    required this.isPhysicalDevice,
+    required this.androidId,
+    required List<String> systemFeatures,
+  })   : supported32BitAbis = List<String>.unmodifiable(supported32BitAbis),
         supported64BitAbis = List<String>.unmodifiable(supported64BitAbis),
         supportedAbis = List<String>.unmodifiable(supportedAbis),
         systemFeatures = List<String>.unmodifiable(systemFeatures);
@@ -115,25 +115,25 @@
     return AndroidDeviceInfo(
       version: AndroidBuildVersion._fromMap(
           map['version']?.cast<String, dynamic>() ?? {}),
-      board: map['board'],
-      bootloader: map['bootloader'],
-      brand: map['brand'],
-      device: map['device'],
-      display: map['display'],
-      fingerprint: map['fingerprint'],
-      hardware: map['hardware'],
-      host: map['host'],
-      id: map['id'],
-      manufacturer: map['manufacturer'],
-      model: map['model'],
-      product: map['product'],
+      board: map['board'] ?? '',
+      bootloader: map['bootloader'] ?? '',
+      brand: map['brand'] ?? '',
+      device: map['device'] ?? '',
+      display: map['display'] ?? '',
+      fingerprint: map['fingerprint'] ?? '',
+      hardware: map['hardware'] ?? '',
+      host: map['host'] ?? '',
+      id: map['id'] ?? '',
+      manufacturer: map['manufacturer'] ?? '',
+      model: map['model'] ?? '',
+      product: map['product'] ?? '',
       supported32BitAbis: _fromList(map['supported32BitAbis'] ?? []),
       supported64BitAbis: _fromList(map['supported64BitAbis'] ?? []),
       supportedAbis: _fromList(map['supportedAbis'] ?? []),
-      tags: map['tags'],
-      type: map['type'],
-      isPhysicalDevice: map['isPhysicalDevice'],
-      androidId: map['androidId'],
+      tags: map['tags'] ?? '',
+      type: map['type'] ?? '',
+      isPhysicalDevice: map['isPhysicalDevice'] ?? false,
+      androidId: map['androidId'] ?? '',
       systemFeatures: _fromList(map['systemFeatures'] ?? []),
     );
   }
@@ -151,13 +151,13 @@
 /// See: https://developer.android.com/reference/android/os/Build.VERSION.html
 class AndroidBuildVersion {
   AndroidBuildVersion._({
-    this.baseOS,
-    this.codename,
-    this.incremental,
-    this.previewSdkInt,
-    this.release,
-    this.sdkInt,
-    this.securityPatch,
+    required this.baseOS,
+    required this.codename,
+    required this.incremental,
+    required this.previewSdkInt,
+    required this.release,
+    required this.sdkInt,
+    required this.securityPatch,
   });
 
   /// The base OS build the product is based on.
@@ -186,13 +186,13 @@
   /// Deserializes from the map message received from [_kChannel].
   static AndroidBuildVersion _fromMap(Map<String, dynamic> map) {
     return AndroidBuildVersion._(
-      baseOS: map['baseOS'],
-      codename: map['codename'],
-      incremental: map['incremental'],
-      previewSdkInt: map['previewSdkInt'],
-      release: map['release'],
-      sdkInt: map['sdkInt'],
-      securityPatch: map['securityPatch'],
+      baseOS: map['baseOS'] ?? '',
+      codename: map['codename'] ?? '',
+      incremental: map['incremental'] ?? '',
+      previewSdkInt: map['previewSdkInt'] ?? 0,
+      release: map['release'] ?? '',
+      sdkInt: map['sdkInt'] ?? 0,
+      securityPatch: map['securityPatch'] ?? '',
     );
   }
 }
diff --git a/packages/device_info/device_info_platform_interface/lib/model/ios_device_info.dart b/packages/device_info/device_info_platform_interface/lib/model/ios_device_info.dart
index d412024..f909fad 100644
--- a/packages/device_info/device_info_platform_interface/lib/model/ios_device_info.dart
+++ b/packages/device_info/device_info_platform_interface/lib/model/ios_device_info.dart
@@ -8,14 +8,14 @@
 class IosDeviceInfo {
   /// IOS device info class.
   IosDeviceInfo({
-    this.name,
-    this.systemName,
-    this.systemVersion,
-    this.model,
-    this.localizedModel,
-    this.identifierForVendor,
-    this.isPhysicalDevice,
-    this.utsname,
+    required this.name,
+    required this.systemName,
+    required this.systemVersion,
+    required this.model,
+    required this.localizedModel,
+    required this.identifierForVendor,
+    required this.isPhysicalDevice,
+    required this.utsname,
   });
 
   /// Device name.
@@ -45,12 +45,12 @@
   /// Deserializes from the map message received from [_kChannel].
   static IosDeviceInfo fromMap(Map<String, dynamic> map) {
     return IosDeviceInfo(
-      name: map['name'],
-      systemName: map['systemName'],
-      systemVersion: map['systemVersion'],
-      model: map['model'],
-      localizedModel: map['localizedModel'],
-      identifierForVendor: map['identifierForVendor'],
+      name: map['name'] ?? '',
+      systemName: map['systemName'] ?? '',
+      systemVersion: map['systemVersion'] ?? '',
+      model: map['model'] ?? '',
+      localizedModel: map['localizedModel'] ?? '',
+      identifierForVendor: map['identifierForVendor'] ?? '',
       isPhysicalDevice: map['isPhysicalDevice'] == 'true',
       utsname:
           IosUtsname._fromMap(map['utsname']?.cast<String, dynamic>() ?? {}),
@@ -62,11 +62,11 @@
 /// See http://pubs.opengroup.org/onlinepubs/7908799/xsh/sysutsname.h.html for details.
 class IosUtsname {
   IosUtsname._({
-    this.sysname,
-    this.nodename,
-    this.release,
-    this.version,
-    this.machine,
+    required this.sysname,
+    required this.nodename,
+    required this.release,
+    required this.version,
+    required this.machine,
   });
 
   /// Operating system name.
@@ -87,11 +87,11 @@
   /// Deserializes from the map message received from [_kChannel].
   static IosUtsname _fromMap(Map<String, dynamic> map) {
     return IosUtsname._(
-      sysname: map['sysname'],
-      nodename: map['nodename'],
-      release: map['release'],
-      version: map['version'],
-      machine: map['machine'],
+      sysname: map['sysname'] ?? '',
+      nodename: map['nodename'] ?? '',
+      release: map['release'] ?? '',
+      version: map['version'] ?? '',
+      machine: map['machine'] ?? '',
     );
   }
 }
diff --git a/packages/device_info/device_info_platform_interface/pubspec.yaml b/packages/device_info/device_info_platform_interface/pubspec.yaml
index 656e5b2..d9c3264 100644
--- a/packages/device_info/device_info_platform_interface/pubspec.yaml
+++ b/packages/device_info/device_info_platform_interface/pubspec.yaml
@@ -3,20 +3,20 @@
 homepage: https://github.com/flutter/plugins/tree/master/packages/device_info
 # NOTE: We strongly prefer non-breaking changes, even at the expense of a
 # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes
-version: 1.0.1
+version: 2.0.0-nullsafety
 
 dependencies:
   flutter:
     sdk: flutter
-  meta: ^1.1.8
-  plugin_platform_interface: ^1.0.2
+  meta: ^1.3.0-nullsafety.3
+  plugin_platform_interface: 1.1.0-nullsafety
 
 dev_dependencies:
   flutter_test:
     sdk: flutter
-  mockito: ^4.1.1
-  pedantic: ^1.8.0
+  test: ^1.10.0-nullsafety.1
+  pedantic: ^1.10.0-nullsafety.1
 
 environment:
-  sdk: ">=2.7.0 <3.0.0"
+  sdk: '>=2.10.0-56.0.dev <3.0.0'
   flutter: ">=1.9.1+hotfix.4 <2.0.0"
diff --git a/packages/device_info/device_info_platform_interface/test/method_channel_device_info_test.dart b/packages/device_info/device_info_platform_interface/test/method_channel_device_info_test.dart
index 1da52e2..0a1ba70 100644
--- a/packages/device_info/device_info_platform_interface/test/method_channel_device_info_test.dart
+++ b/packages/device_info/device_info_platform_interface/test/method_channel_device_info_test.dart
@@ -2,11 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// TODO(cyanglaz): Remove once https://github.com/flutter/flutter/issues/59879 is fixed.
+// @dart = 2.9
+
 import 'package:flutter/services.dart';
 import 'package:flutter_test/flutter_test.dart';
-
 import 'package:device_info_platform_interface/device_info_platform_interface.dart';
-
 import 'package:device_info_platform_interface/method_channel/method_channel_device_info.dart';
 
 void main() {
diff --git a/packages/google_sign_in/google_sign_in/test/google_sign_in_test.dart b/packages/google_sign_in/google_sign_in/test/google_sign_in_test.dart
index 3fc3471..435f307 100755
--- a/packages/google_sign_in/google_sign_in/test/google_sign_in_test.dart
+++ b/packages/google_sign_in/google_sign_in/test/google_sign_in_test.dart
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// @dart = 2.8
+// @dart = 2.9
 
 import 'dart:async';
 
diff --git a/packages/plugin_platform_interface/CHANGELOG.md b/packages/plugin_platform_interface/CHANGELOG.md
index 01b5ff7..f8e8485 100644
--- a/packages/plugin_platform_interface/CHANGELOG.md
+++ b/packages/plugin_platform_interface/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 1.1.0-nullsafety
+
+* Migrate to null safety.
+
 ## 1.0.3
 
 * Fix homepage in `pubspec.yaml`.
diff --git a/packages/plugin_platform_interface/analysis_options.yaml b/packages/plugin_platform_interface/analysis_options.yaml
new file mode 100644
index 0000000..f4819cd
--- /dev/null
+++ b/packages/plugin_platform_interface/analysis_options.yaml
@@ -0,0 +1,4 @@
+include: ../../analysis_options.yaml
+analyzer:
+  enable-experiment:
+    - non-nullable
diff --git a/packages/plugin_platform_interface/lib/plugin_platform_interface.dart b/packages/plugin_platform_interface/lib/plugin_platform_interface.dart
index be48719..cd87b04 100644
--- a/packages/plugin_platform_interface/lib/plugin_platform_interface.dart
+++ b/packages/plugin_platform_interface/lib/plugin_platform_interface.dart
@@ -41,7 +41,7 @@
 /// [MockPlatformInterfaceMixin] for a sample of using Mockito to mock a platform interface.
 abstract class PlatformInterface {
   /// Pass a private, class-specific `const Object()` as the `token`.
-  PlatformInterface({@required Object token}) : _instanceToken = token;
+  PlatformInterface({required Object token}) : _instanceToken = token;
 
   final Object _instanceToken;
 
diff --git a/packages/plugin_platform_interface/pubspec.yaml b/packages/plugin_platform_interface/pubspec.yaml
index ae11b14..d79dbb8 100644
--- a/packages/plugin_platform_interface/pubspec.yaml
+++ b/packages/plugin_platform_interface/pubspec.yaml
@@ -12,17 +12,17 @@
 # be done when absolutely necessary and after the ecosystem has already migrated to 1.X.Y version
 # that is forward compatible with 2.0.0 (ideally the ecosystem have migrated to depend on:
 # `plugin_platform_interface: >=1.X.Y <3.0.0`).
-version: 1.0.3
+version: 1.1.0-nullsafety
 
 repository: https://github.com/flutter/plugins/tree/master/packages/plugin_platform_interface
 
 environment:
-  sdk: ">=2.1.0 <3.0.0"
+  sdk: '>=2.10.0-56.0.dev <3.0.0'
 
 dependencies:
-  meta: ^1.0.0
+  meta: ^1.3.0-nullsafety.3
 
 dev_dependencies:
   mockito: ^4.1.1
-  test: ^1.9.4
-  pedantic: ^1.8.0
+  test: ^1.10.0-nullsafety.1
+  pedantic: ^1.10.0-nullsafety.1
diff --git a/packages/plugin_platform_interface/test/plugin_platform_interface_test.dart b/packages/plugin_platform_interface/test/plugin_platform_interface_test.dart
index 0488c20..b07dd4d 100644
--- a/packages/plugin_platform_interface/test/plugin_platform_interface_test.dart
+++ b/packages/plugin_platform_interface/test/plugin_platform_interface_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.
 
+// TODO(egarciad): Remove once Mockito is migrated to null safety.
+// @dart = 2.9
 import 'package:mockito/mockito.dart';
 import 'package:test/test.dart';
 
diff --git a/packages/shared_preferences/shared_preferences/test/shared_preferences_test.dart b/packages/shared_preferences/shared_preferences/test/shared_preferences_test.dart
index 763d326..80faba4 100755
--- a/packages/shared_preferences/shared_preferences/test/shared_preferences_test.dart
+++ b/packages/shared_preferences/shared_preferences/test/shared_preferences_test.dart
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// @dart = 2.8
+// @dart = 2.9
 
 import 'package:flutter/services.dart';
 import 'package:flutter_test/flutter_test.dart';
diff --git a/packages/url_launcher/url_launcher/CHANGELOG.md b/packages/url_launcher/url_launcher/CHANGELOG.md
index 34b9dbf..3b5fff1 100644
--- a/packages/url_launcher/url_launcher/CHANGELOG.md
+++ b/packages/url_launcher/url_launcher/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 6.0.0-nullsafety
+
+* Migrate to null safety.
+
 ## 5.7.9
 
 * Check in windows/ directory for example/
diff --git a/packages/url_launcher/url_launcher/analysis_options.yaml b/packages/url_launcher/url_launcher/analysis_options.yaml
new file mode 100644
index 0000000..3d64bb5
--- /dev/null
+++ b/packages/url_launcher/url_launcher/analysis_options.yaml
@@ -0,0 +1,4 @@
+include: ../../../analysis_options.yaml
+analyzer:
+  enable-experiment:
+    - non-nullable
diff --git a/packages/url_launcher/url_launcher/example/integration_test/url_launcher_test.dart b/packages/url_launcher/url_launcher/example/integration_test/url_launcher_test.dart
index 9fb5eaa..6ab7c4b 100644
--- a/packages/url_launcher/url_launcher/example/integration_test/url_launcher_test.dart
+++ b/packages/url_launcher/url_launcher/example/integration_test/url_launcher_test.dart
@@ -2,6 +2,9 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+// TODO(egarciad): Remove once integration_test is migrated to null safety.
+// @dart = 2.9
+
 import 'dart:io' show Platform;
 
 import 'package:flutter/foundation.dart' show kIsWeb;
diff --git a/packages/url_launcher/url_launcher/example/lib/main.dart b/packages/url_launcher/url_launcher/example/lib/main.dart
index f7d90c4..b3e65f3 100644
--- a/packages/url_launcher/url_launcher/example/lib/main.dart
+++ b/packages/url_launcher/url_launcher/example/lib/main.dart
@@ -27,7 +27,7 @@
 }
 
 class MyHomePage extends StatefulWidget {
-  MyHomePage({Key key, this.title}) : super(key: key);
+  MyHomePage({Key? key, required this.title}) : super(key: key);
   final String title;
 
   @override
@@ -35,7 +35,7 @@
 }
 
 class _MyHomePageState extends State<MyHomePage> {
-  Future<void> _launched;
+  Future<void>? _launched;
   String _phone = '';
 
   Future<void> _launchInBrowser(String url) async {
diff --git a/packages/url_launcher/url_launcher/example/pubspec.yaml b/packages/url_launcher/url_launcher/example/pubspec.yaml
index b48445c..4a833f0 100644
--- a/packages/url_launcher/url_launcher/example/pubspec.yaml
+++ b/packages/url_launcher/url_launcher/example/pubspec.yaml
@@ -12,9 +12,9 @@
     path: ../../../integration_test
   flutter_driver:
     sdk: flutter
-  pedantic: ^1.8.0
+  pedantic: ^1.10.0-nullsafety.1
   mockito: ^4.1.1
-  plugin_platform_interface: ^1.0.0
+  plugin_platform_interface: 1.1.0-nullsafety
 
 flutter:
   uses-material-design: true
diff --git a/packages/url_launcher/url_launcher/example/test/url_launcher_example_test.dart b/packages/url_launcher/url_launcher/example/test/url_launcher_example_test.dart
index 41b9f6f..eddc126 100644
--- a/packages/url_launcher/url_launcher/example/test/url_launcher_example_test.dart
+++ b/packages/url_launcher/url_launcher/example/test/url_launcher_example_test.dart
@@ -1,3 +1,10 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// TODO(egarciad): Remove once mockito is migrated to null safety.
+// @dart = 2.9
+
 import 'package:flutter_test/flutter_test.dart';
 import 'package:flutter/material.dart';
 import 'package:mockito/mockito.dart';
diff --git a/packages/url_launcher/url_launcher/example/test_driver/integration_test.dart b/packages/url_launcher/url_launcher/example/test_driver/integration_test.dart
index 7a2c213..e56756f 100644
--- a/packages/url_launcher/url_launcher/example/test_driver/integration_test.dart
+++ b/packages/url_launcher/url_launcher/example/test_driver/integration_test.dart
@@ -2,6 +2,9 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+// TODO(egarciad): Remove once flutter_driver is migrated to null safety.
+// @dart = 2.9
+
 import 'dart:async';
 import 'dart:convert';
 import 'dart:io';
diff --git a/packages/url_launcher/url_launcher/lib/src/link.dart b/packages/url_launcher/url_launcher/lib/src/link.dart
index bd54789..f859bc4 100644
--- a/packages/url_launcher/url_launcher/lib/src/link.dart
+++ b/packages/url_launcher/url_launcher/lib/src/link.dart
@@ -40,7 +40,7 @@
   final LinkWidgetBuilder builder;
 
   /// The destination that this link leads to.
-  final Uri uri;
+  final Uri? uri;
 
   /// The target indicating where to open the link.
   final LinkTarget target;
@@ -51,12 +51,11 @@
   /// Creates a widget that renders a real link on the web, and uses WebViews in
   /// native platforms to open links.
   Link({
-    Key key,
-    @required this.uri,
-    LinkTarget target,
-    @required this.builder,
-  })  : target = target ?? LinkTarget.defaultTarget,
-        super(key: key);
+    Key? key,
+    required this.uri,
+    this.target = LinkTarget.defaultTarget,
+    required this.builder,
+  }) : super(key: key);
 
   LinkDelegate get _effectiveDelegate {
     return UrlLauncherPlatform.instance.linkDelegate ??
@@ -90,16 +89,17 @@
   bool get _useWebView {
     if (link.target == LinkTarget.self) return true;
     if (link.target == LinkTarget.blank) return false;
-    return null;
+    return false;
   }
 
   Future<void> _followLink(BuildContext context) async {
-    if (!link.uri.hasScheme) {
+    if (!link.uri!.hasScheme) {
       // A uri that doesn't have a scheme is an internal route name. In this
       // case, we push it via Flutter's navigation system instead of letting the
       // browser handle it.
       final String routeName = link.uri.toString();
-      return pushRouteNameToFramework(context, routeName);
+      await pushRouteNameToFramework(context, routeName);
+      return;
     }
 
     // At this point, we know that the link is external. So we use the `launch`
@@ -119,7 +119,6 @@
         context: ErrorDescription('during launching a link'),
       ));
     }
-    return Future<void>.value(null);
   }
 
   @override
diff --git a/packages/url_launcher/url_launcher/lib/url_launcher.dart b/packages/url_launcher/url_launcher/lib/url_launcher.dart
index 25aa623..6138fff 100644
--- a/packages/url_launcher/url_launcher/lib/url_launcher.dart
+++ b/packages/url_launcher/url_launcher/lib/url_launcher.dart
@@ -62,16 +62,15 @@
 /// is set to true and the universal link failed to launch.
 Future<bool> launch(
   String urlString, {
-  bool forceSafariVC,
-  bool forceWebView,
-  bool enableJavaScript,
-  bool enableDomStorage,
-  bool universalLinksOnly,
-  Map<String, String> headers,
-  Brightness statusBarBrightness,
-  String webOnlyWindowName,
+  bool forceSafariVC = true,
+  bool forceWebView = false,
+  bool enableJavaScript = false,
+  bool enableDomStorage = false,
+  bool universalLinksOnly = false,
+  Map<String, String> headers = const <String, String>{},
+  Brightness? statusBarBrightness,
+  String? webOnlyWindowName,
 }) async {
-  assert(urlString != null);
   final Uri url = Uri.parse(urlString.trimLeft());
   final bool isWebURL = url.scheme == 'http' || url.scheme == 'https';
   if ((forceSafariVC == true || forceWebView == true) && !isWebURL) {
@@ -84,29 +83,32 @@
   /// [true] so that ui is automatically computed if [statusBarBrightness] is set.
   bool previousAutomaticSystemUiAdjustment = true;
   if (statusBarBrightness != null &&
-      defaultTargetPlatform == TargetPlatform.iOS) {
+      defaultTargetPlatform == TargetPlatform.iOS &&
+      WidgetsBinding.instance != null) {
     previousAutomaticSystemUiAdjustment =
-        WidgetsBinding.instance.renderView.automaticSystemUiAdjustment;
-    WidgetsBinding.instance.renderView.automaticSystemUiAdjustment = false;
+        WidgetsBinding.instance!.renderView.automaticSystemUiAdjustment;
+    WidgetsBinding.instance!.renderView.automaticSystemUiAdjustment = false;
     SystemChrome.setSystemUIOverlayStyle(statusBarBrightness == Brightness.light
         ? SystemUiOverlayStyle.dark
         : SystemUiOverlayStyle.light);
   }
+
   final bool result = await UrlLauncherPlatform.instance.launch(
     urlString,
-    useSafariVC: forceSafariVC ?? isWebURL,
-    useWebView: forceWebView ?? false,
-    enableJavaScript: enableJavaScript ?? false,
-    enableDomStorage: enableDomStorage ?? false,
-    universalLinksOnly: universalLinksOnly ?? false,
-    headers: headers ?? <String, String>{},
+    useSafariVC: forceSafariVC,
+    useWebView: forceWebView,
+    enableJavaScript: enableJavaScript,
+    enableDomStorage: enableDomStorage,
+    universalLinksOnly: universalLinksOnly,
+    headers: headers,
     webOnlyWindowName: webOnlyWindowName,
   );
-  assert(previousAutomaticSystemUiAdjustment != null);
-  if (statusBarBrightness != null) {
-    WidgetsBinding.instance.renderView.automaticSystemUiAdjustment =
+
+  if (statusBarBrightness != null && WidgetsBinding.instance != null) {
+    WidgetsBinding.instance!.renderView.automaticSystemUiAdjustment =
         previousAutomaticSystemUiAdjustment;
   }
+
   return result;
 }
 
@@ -118,9 +120,6 @@
 /// For more information see the [Managing package visibility](https://developer.android.com/training/basics/intents/package-visibility)
 /// article in the Android docs.
 Future<bool> canLaunch(String urlString) async {
-  if (urlString == null) {
-    return false;
-  }
   return await UrlLauncherPlatform.instance.canLaunch(urlString);
 }
 
diff --git a/packages/url_launcher/url_launcher/pubspec.yaml b/packages/url_launcher/url_launcher/pubspec.yaml
index 9299086..0ec7409 100644
--- a/packages/url_launcher/url_launcher/pubspec.yaml
+++ b/packages/url_launcher/url_launcher/pubspec.yaml
@@ -2,7 +2,7 @@
 description: Flutter plugin for launching a URL on Android and iOS. Supports
   web, phone, SMS, and email schemes.
 homepage: https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher
-version: 5.7.9
+version: 6.0.0-nullsafety
 
 flutter:
   plugin:
@@ -12,8 +12,9 @@
         pluginClass: UrlLauncherPlugin
       ios:
         pluginClass: FLTURLLauncherPlugin
-      web:
-        default_package: url_launcher_web
+      # TODO(mvanbeusekom): Temporary disabled until web is migrated to nnbd (advised by @blasten).
+      #web:
+      #  default_package: url_launcher_web
       linux:
         default_package: url_laucher_linux
       macos:
@@ -24,25 +25,26 @@
 dependencies:
   flutter:
     sdk: flutter
-  url_launcher_platform_interface: ^1.0.9
+  url_launcher_platform_interface: ^2.0.0-nullsafety
   # The design on https://flutter.dev/go/federated-plugins was to leave
   # this constraint as "any". We cannot do it right now as it fails pub publish
   # validation, so we set a ^ constraint.
   # TODO(amirh): Revisit this (either update this part in the  design or the pub tool).
   # https://github.com/flutter/flutter/issues/46264
-  url_launcher_web: ^0.1.5
-  url_launcher_linux: ^0.0.1
-  url_launcher_macos: ^0.0.1
-  url_launcher_windows: ^0.0.1
+  url_launcher_linux: ^0.1.0-nullsafety
+  url_launcher_macos: ^0.1.0-nullsafety
+  url_launcher_windows: ^0.1.0-nullsafety
+  # TODO(mvanbeusekom): Temporary disabled until web is migrated to nnbd (advised by @blasten).
+  #url_launcher_web: ^0.1.3
 
 dev_dependencies:
   flutter_test:
     sdk: flutter
-  test: ^1.3.0
+  test: ^1.10.0-nullsafety.1
   mockito: ^4.1.1
-  plugin_platform_interface: ^1.0.0
-  pedantic: ^1.8.0
+  plugin_platform_interface: 1.1.0-nullsafety
+  pedantic: ^1.10.0-nullsafety.1
 
 environment:
-  sdk: ">=2.1.0 <3.0.0"
+  sdk: ">=2.10.0-56.0.dev <3.0.0"
   flutter: ">=1.12.13+hotfix.5 <2.0.0"
diff --git a/packages/url_launcher/url_launcher/test/link_test.dart b/packages/url_launcher/url_launcher/test/link_test.dart
index d525153..46903aa 100644
--- a/packages/url_launcher/url_launcher/test/link_test.dart
+++ b/packages/url_launcher/url_launcher/test/link_test.dart
@@ -2,7 +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
+// TODO(egarciad): Remove once Mockito has been migrated to null safety.
+// @dart = 2.9
 
 import 'dart:ui';
 import 'package:flutter/material.dart';
diff --git a/packages/url_launcher/url_launcher/test/url_launcher_test.dart b/packages/url_launcher/url_launcher/test/url_launcher_test.dart
index f18e16c..89a7801 100644
--- a/packages/url_launcher/url_launcher/test/url_launcher_test.dart
+++ b/packages/url_launcher/url_launcher/test/url_launcher_test.dart
@@ -2,10 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// @dart = 2.8
+// TODO(mvanbeusekom): Remove once Mockito is migrated to null safety.
+// @dart = 2.9
 
 import 'dart:async';
 import 'dart:ui';
+
 import 'package:flutter_test/flutter_test.dart';
 import 'package:mockito/mockito.dart';
 import 'package:flutter/foundation.dart';
@@ -41,10 +43,6 @@
     });
   });
   group('launch', () {
-    test('requires a non-null urlString', () {
-      expect(() => launch(null), throwsAssertionError);
-    });
-
     test('default behavior', () async {
       await launch('http://flutter.dev/');
       expect(
diff --git a/packages/url_launcher/url_launcher_linux/CHANGELOG.md b/packages/url_launcher/url_launcher_linux/CHANGELOG.md
index c6a0042..c850ba8 100644
--- a/packages/url_launcher/url_launcher_linux/CHANGELOG.md
+++ b/packages/url_launcher/url_launcher_linux/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.1.0-nullsafety
+
+* Migrate to null safety.
+
 ## 0.0.1+3
 
 * Add a missing include.
diff --git a/packages/url_launcher/url_launcher_linux/pubspec.yaml b/packages/url_launcher/url_launcher_linux/pubspec.yaml
index c4f6f8c..e7ae1e8 100644
--- a/packages/url_launcher/url_launcher_linux/pubspec.yaml
+++ b/packages/url_launcher/url_launcher_linux/pubspec.yaml
@@ -1,6 +1,6 @@
 name: url_launcher_linux
 description: Linux implementation of the url_launcher plugin.
-version: 0.0.1+3
+version: 0.1.0-nullsafety
 homepage: https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher_linux
 
 flutter:
@@ -10,7 +10,7 @@
         pluginClass: UrlLauncherPlugin
 
 environment:
-  sdk: ">=2.1.0 <3.0.0"
+  sdk: ">=2.10.0-56.0.dev <3.0.0"
   flutter: ">=1.12.8 <2.0.0"
 
 dependencies:
diff --git a/packages/url_launcher/url_launcher_macos/CHANGELOG.md b/packages/url_launcher/url_launcher_macos/CHANGELOG.md
index d52bf8c..a43df45 100644
--- a/packages/url_launcher/url_launcher_macos/CHANGELOG.md
+++ b/packages/url_launcher/url_launcher_macos/CHANGELOG.md
@@ -1,3 +1,7 @@
+# 0.1.0-nullsafety
+
+* Migrate to null safety.
+
 # 0.0.1+8
 
 * Remove no-op android folder in the example app.
diff --git a/packages/url_launcher/url_launcher_macos/pubspec.yaml b/packages/url_launcher/url_launcher_macos/pubspec.yaml
index 4775f23..8c48c6d 100644
--- a/packages/url_launcher/url_launcher_macos/pubspec.yaml
+++ b/packages/url_launcher/url_launcher_macos/pubspec.yaml
@@ -3,7 +3,7 @@
 # 0.0.y+z is compatible with 1.0.0, if you land a breaking change bump
 # the version to 2.0.0.
 # See more details: https://github.com/flutter/flutter/wiki/Package-migration-to-1.0.0
-version: 0.0.1+8
+version: 0.1.0-nullsafety
 homepage: https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher_macos
 
 flutter:
@@ -14,7 +14,7 @@
         fileName: url_launcher_macos.dart
 
 environment:
-  sdk: ">=2.1.0 <3.0.0"
+  sdk: ">=2.10.0-56.0.dev <3.0.0"
   flutter: ">=1.12.8 <2.0.0"
 
 dependencies:
diff --git a/packages/url_launcher/url_launcher_platform_interface/CHANGELOG.md b/packages/url_launcher/url_launcher_platform_interface/CHANGELOG.md
index 10057e1..74972e5 100644
--- a/packages/url_launcher/url_launcher_platform_interface/CHANGELOG.md
+++ b/packages/url_launcher/url_launcher_platform_interface/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 2.0.0-nullsafety
+
+* Migrate to null safety.
+
 ## 1.0.9
 
 * Laid the groundwork for introducing a Link widget.
diff --git a/packages/url_launcher/url_launcher_platform_interface/analysis_options.yaml b/packages/url_launcher/url_launcher_platform_interface/analysis_options.yaml
new file mode 100644
index 0000000..3d64bb5
--- /dev/null
+++ b/packages/url_launcher/url_launcher_platform_interface/analysis_options.yaml
@@ -0,0 +1,4 @@
+include: ../../../analysis_options.yaml
+analyzer:
+  enable-experiment:
+    - non-nullable
diff --git a/packages/url_launcher/url_launcher_platform_interface/lib/link.dart b/packages/url_launcher/url_launcher_platform_interface/lib/link.dart
index 425dc88..a176972 100644
--- a/packages/url_launcher/url_launcher_platform_interface/lib/link.dart
+++ b/packages/url_launcher/url_launcher_platform_interface/lib/link.dart
@@ -16,7 +16,7 @@
 /// the widget tree under it.
 typedef LinkWidgetBuilder = Widget Function(
   BuildContext context,
-  FollowLink followLink,
+  FollowLink? followLink,
 );
 
 /// Signature for a delegate function to build the [Link] widget.
@@ -31,7 +31,7 @@
 class LinkTarget {
   /// Const private constructor with a [debugLabel] to allow the creation of
   /// multiple distinct const instances.
-  const LinkTarget._({this.debugLabel});
+  const LinkTarget._({required this.debugLabel});
 
   /// Used to distinguish multiple const instances of [LinkTarget].
   final String debugLabel;
@@ -64,7 +64,7 @@
   LinkWidgetBuilder get builder;
 
   /// The destination that this link leads to.
-  Uri get uri;
+  Uri? get uri;
 
   /// The target indicating where to open the link.
   LinkTarget get target;
@@ -80,10 +80,14 @@
   String routeName, {
   @visibleForTesting bool debugForceRouter = false,
 }) {
+  final PlatformMessageCallback? onPlatformMessage = window.onPlatformMessage;
+  if (onPlatformMessage == null) {
+    return Future<ByteData>.value(null);
+  }
   final Completer<ByteData> completer = Completer<ByteData>();
   if (debugForceRouter || _hasRouter(context)) {
     SystemNavigator.routeInformationUpdated(location: routeName);
-    window.onPlatformMessage(
+    onPlatformMessage(
       'flutter/navigation',
       _codec.encodeMethodCall(
         MethodCall('pushRouteInformation', <dynamic, dynamic>{
@@ -94,7 +98,7 @@
       completer.complete,
     );
   } else {
-    window.onPlatformMessage(
+    onPlatformMessage(
       'flutter/navigation',
       _codec.encodeMethodCall(MethodCall('pushRoute', routeName)),
       completer.complete,
diff --git a/packages/url_launcher/url_launcher_platform_interface/lib/method_channel_url_launcher.dart b/packages/url_launcher/url_launcher_platform_interface/lib/method_channel_url_launcher.dart
index ac5bfa2..7b9dfc9 100644
--- a/packages/url_launcher/url_launcher_platform_interface/lib/method_channel_url_launcher.dart
+++ b/packages/url_launcher/url_launcher_platform_interface/lib/method_channel_url_launcher.dart
@@ -5,7 +5,6 @@
 import 'dart:async';
 
 import 'package:flutter/services.dart';
-import 'package:meta/meta.dart' show required;
 
 import 'link.dart';
 import 'url_launcher_platform_interface.dart';
@@ -15,14 +14,14 @@
 /// An implementation of [UrlLauncherPlatform] that uses method channels.
 class MethodChannelUrlLauncher extends UrlLauncherPlatform {
   @override
-  final LinkDelegate linkDelegate = null;
+  final LinkDelegate? linkDelegate = null;
 
   @override
   Future<bool> canLaunch(String url) {
     return _channel.invokeMethod<bool>(
       'canLaunch',
       <String, Object>{'url': url},
-    );
+    ).then((value) => value ?? false);
   }
 
   @override
@@ -33,13 +32,13 @@
   @override
   Future<bool> launch(
     String url, {
-    @required bool useSafariVC,
-    @required bool useWebView,
-    @required bool enableJavaScript,
-    @required bool enableDomStorage,
-    @required bool universalLinksOnly,
-    @required Map<String, String> headers,
-    String webOnlyWindowName,
+    required bool useSafariVC,
+    required bool useWebView,
+    required bool enableJavaScript,
+    required bool enableDomStorage,
+    required bool universalLinksOnly,
+    required Map<String, String> headers,
+    String? webOnlyWindowName,
   }) {
     return _channel.invokeMethod<bool>(
       'launch',
@@ -52,6 +51,6 @@
         'universalLinksOnly': universalLinksOnly,
         'headers': headers,
       },
-    );
+    ).then((value) => value ?? false);
   }
 }
diff --git a/packages/url_launcher/url_launcher_platform_interface/lib/url_launcher_platform_interface.dart b/packages/url_launcher/url_launcher_platform_interface/lib/url_launcher_platform_interface.dart
index 75002ff..2a4edfa 100644
--- a/packages/url_launcher/url_launcher_platform_interface/lib/url_launcher_platform_interface.dart
+++ b/packages/url_launcher/url_launcher_platform_interface/lib/url_launcher_platform_interface.dart
@@ -4,7 +4,6 @@
 
 import 'dart:async';
 
-import 'package:meta/meta.dart' show required;
 import 'package:plugin_platform_interface/plugin_platform_interface.dart';
 import 'package:url_launcher_platform_interface/link.dart';
 
@@ -40,7 +39,7 @@
   }
 
   /// The delegate used by the Link widget to build itself.
-  LinkDelegate get linkDelegate;
+  LinkDelegate? get linkDelegate;
 
   /// Returns `true` if this platform is able to launch [url].
   Future<bool> canLaunch(String url) {
@@ -53,13 +52,13 @@
   /// in `package:url_launcher/url_launcher.dart`.
   Future<bool> launch(
     String url, {
-    @required bool useSafariVC,
-    @required bool useWebView,
-    @required bool enableJavaScript,
-    @required bool enableDomStorage,
-    @required bool universalLinksOnly,
-    @required Map<String, String> headers,
-    String webOnlyWindowName,
+    required bool useSafariVC,
+    required bool useWebView,
+    required bool enableJavaScript,
+    required bool enableDomStorage,
+    required bool universalLinksOnly,
+    required Map<String, String> headers,
+    String? webOnlyWindowName,
   }) {
     throw UnimplementedError('launch() has not been implemented.');
   }
diff --git a/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml b/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml
index ce0fdd9..f5ea35d 100644
--- a/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml
+++ b/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml
@@ -3,20 +3,19 @@
 homepage: https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher_platform_interface
 # NOTE: We strongly prefer non-breaking changes, even at the expense of a
 # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes
-version: 1.0.9
+version: 2.0.0-nullsafety
 
 dependencies:
   flutter:
     sdk: flutter
-  meta: ^1.0.5
-  plugin_platform_interface: ^1.0.1
+  plugin_platform_interface: 1.1.0-nullsafety
 
 dev_dependencies:
   flutter_test:
     sdk: flutter
   mockito: ^4.1.1
-  pedantic: ^1.8.0
+  pedantic: ^1.10.0-nullsafety.1
 
 environment:
-  sdk: ">=2.1.0 <3.0.0"
-  flutter: ">=1.22.0 <2.0.0"
+  sdk: '>=2.10.0-56.0.dev <3.0.0'
+  flutter: ">=1.9.1+hotfix.4 <2.0.0"
diff --git a/packages/url_launcher/url_launcher_platform_interface/test/link_test.dart b/packages/url_launcher/url_launcher_platform_interface/test/link_test.dart
index 99a885c..58cdd22 100644
--- a/packages/url_launcher/url_launcher_platform_interface/test/link_test.dart
+++ b/packages/url_launcher/url_launcher_platform_interface/test/link_test.dart
@@ -2,6 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// TODO(egarciad): Remove once Mockito has been migrated to null safety.
+// @dart = 2.9
+
 import 'dart:ui';
 
 import 'package:mockito/mockito.dart';
diff --git a/packages/url_launcher/url_launcher_platform_interface/test/method_channel_url_launcher_test.dart b/packages/url_launcher/url_launcher_platform_interface/test/method_channel_url_launcher_test.dart
index d88f53a..dfd4b73 100644
--- a/packages/url_launcher/url_launcher_platform_interface/test/method_channel_url_launcher_test.dart
+++ b/packages/url_launcher/url_launcher_platform_interface/test/method_channel_url_launcher_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.
 
+// TODO(mvanbeusekom): Remove once Mockito is migrated to null safety.
+// @dart = 2.9
 import 'package:mockito/mockito.dart';
 import 'package:flutter/services.dart';
 import 'package:flutter_test/flutter_test.dart';
@@ -42,6 +44,10 @@
     final List<MethodCall> log = <MethodCall>[];
     channel.setMockMethodCallHandler((MethodCall methodCall) async {
       log.add(methodCall);
+
+      // Return null explicitly instead of relying on the implicit null
+      // returned by the method channel if no return statement is specified.
+      return null;
     });
 
     final MethodChannelUrlLauncher launcher = MethodChannelUrlLauncher();
@@ -62,6 +68,12 @@
       );
     });
 
+    test('canLaunch should return false if platform returns null', () async {
+      final canLaunch = await launcher.canLaunch('http://example.com/');
+
+      expect(canLaunch, false);
+    });
+
     test('launch', () async {
       await launcher.launch(
         'http://example.com/',
@@ -270,6 +282,20 @@
       );
     });
 
+    test('launch should return false if platform returns null', () async {
+      final launched = await launcher.launch(
+        'http://example.com/',
+        useSafariVC: true,
+        useWebView: false,
+        enableJavaScript: false,
+        enableDomStorage: false,
+        universalLinksOnly: false,
+        headers: const <String, String>{},
+      );
+
+      expect(launched, false);
+    });
+
     test('closeWebView default behavior', () async {
       await launcher.closeWebView();
       expect(
diff --git a/packages/url_launcher/url_launcher_web/CHANGELOG.md b/packages/url_launcher/url_launcher_web/CHANGELOG.md
index e7abd13..d813b0a 100644
--- a/packages/url_launcher/url_launcher_web/CHANGELOG.md
+++ b/packages/url_launcher/url_launcher_web/CHANGELOG.md
@@ -33,7 +33,7 @@
 
 # 0.1.2
 
-- Adds "tel" and "sms" support 
+- Adds "tel" and "sms" support
 
 # 0.1.1+6
 
diff --git a/packages/url_launcher/url_launcher_web/pubspec.yaml b/packages/url_launcher/url_launcher_web/pubspec.yaml
index 7ae84cd..bb97b6c 100644
--- a/packages/url_launcher/url_launcher_web/pubspec.yaml
+++ b/packages/url_launcher/url_launcher_web/pubspec.yaml
@@ -15,6 +15,12 @@
 
 dependencies:
   url_launcher_platform_interface: ^1.0.9
+  # TODO(mvanbeusekom): Update to use pub.dev once null safety version is published.
+  # url_launcher_platform_interface:
+  #   git:
+  #     url: https://github.com/flutter/plugins.git
+  #     ref: nnbd
+  #     path: packages/url_launcher/url_launcher_platform_interface
   flutter:
     sdk: flutter
   flutter_web_plugins:
@@ -25,6 +31,9 @@
   flutter_test:
     sdk: flutter
   url_launcher: ^5.2.5
+  # TODO(mvanbeusekom): Update to use pub.dev once null safety version is published.
+  # url_launcher:
+  #   path: ../url_launcher
   pedantic: ^1.8.0
   mockito: ^4.1.1
   integration_test:
diff --git a/packages/url_launcher/url_launcher_web/test/pubspec.yaml b/packages/url_launcher/url_launcher_web/test/pubspec.yaml
index e755dff..b8c541f 100644
--- a/packages/url_launcher/url_launcher_web/test/pubspec.yaml
+++ b/packages/url_launcher/url_launcher_web/test/pubspec.yaml
@@ -2,7 +2,7 @@
 publish_to: none
 
 environment:
-  sdk: ">=2.2.2 <3.0.0"
+  sdk: ">=2.10.0-56.0.dev <3.0.0"
 
 dependencies:
   flutter:
diff --git a/packages/url_launcher/url_launcher_windows/CHANGELOG.md b/packages/url_launcher/url_launcher_windows/CHANGELOG.md
index 5dd437e..a8748a3 100644
--- a/packages/url_launcher/url_launcher_windows/CHANGELOG.md
+++ b/packages/url_launcher/url_launcher_windows/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.1.0-nullsafety
+
+* Migrate to null-safety.
+
 ## 0.0.1+2
 
 * Check in windows/ directory for example/
diff --git a/packages/url_launcher/url_launcher_windows/pubspec.yaml b/packages/url_launcher/url_launcher_windows/pubspec.yaml
index 34e410c..39ea2d2 100644
--- a/packages/url_launcher/url_launcher_windows/pubspec.yaml
+++ b/packages/url_launcher/url_launcher_windows/pubspec.yaml
@@ -3,7 +3,7 @@
 # 0.0.y+z is compatible with 1.0.0, if you land a breaking change bump
 # the version to 2.0.0.
 # See more details: https://github.com/flutter/flutter/wiki/Package-migration-to-1.0.0
-version: 0.0.1+2
+version: 0.1.0-nullsafety
 homepage: https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher_windows
 
 flutter:
@@ -13,7 +13,7 @@
         pluginClass: UrlLauncherPlugin
 
 environment:
-  sdk: ">=2.1.0 <3.0.0"
+  sdk: ">=2.10.0-56.0.dev <3.0.0"
   flutter: ">=1.12.8 <2.0.0"
 
 dependencies:
diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md
index e02bd76..36d5132 100644
--- a/packages/video_player/video_player/CHANGELOG.md
+++ b/packages/video_player/video_player/CHANGELOG.md
@@ -1,3 +1,11 @@
+## 2.0.0-nullsafety.1
+
+* Merge master.
+
+## 2.0.0-nullsafety
+
+* Migration to null safety.
+
 ## 0.11.1+4
 
 * Add `toString()` to `Caption`.
diff --git a/packages/video_player/video_player/analysis_options.yaml b/packages/video_player/video_player/analysis_options.yaml
new file mode 100644
index 0000000..3d64bb5
--- /dev/null
+++ b/packages/video_player/video_player/analysis_options.yaml
@@ -0,0 +1,4 @@
+include: ../../../analysis_options.yaml
+analyzer:
+  enable-experiment:
+    - non-nullable
diff --git a/packages/video_player/video_player/android/src/main/java/io/flutter/plugins/videoplayer/Messages.java b/packages/video_player/video_player/android/src/main/java/io/flutter/plugins/videoplayer/Messages.java
index 78da715..98cf6db 100644
--- a/packages/video_player/video_player/android/src/main/java/io/flutter/plugins/videoplayer/Messages.java
+++ b/packages/video_player/video_player/android/src/main/java/io/flutter/plugins/videoplayer/Messages.java
@@ -1,4 +1,4 @@
-// Autogenerated from Pigeon (v0.1.7), do not edit directly.
+// Autogenerated from Pigeon (v0.1.12), do not edit directly.
 // See also: https://pub.dev/packages/pigeon
 
 package io.flutter.plugins.videoplayer;
@@ -597,7 +597,7 @@
   private static HashMap wrapError(Exception exception) {
     HashMap<String, Object> errorMap = new HashMap<>();
     errorMap.put("message", exception.toString());
-    errorMap.put("code", null);
+    errorMap.put("code", exception.getClass().getSimpleName());
     errorMap.put("details", null);
     return errorMap;
   }
diff --git a/packages/video_player/video_player/example/integration_test/video_player_test.dart b/packages/video_player/video_player/example/integration_test/video_player_test.dart
index 0953c8f..8f6daa3 100644
--- a/packages/video_player/video_player/example/integration_test/video_player_test.dart
+++ b/packages/video_player/video_player/example/integration_test/video_player_test.dart
@@ -11,7 +11,7 @@
 
 void main() {
   IntegrationTestWidgetsFlutterBinding.ensureInitialized();
-  VideoPlayerController _controller;
+  late VideoPlayerController _controller;
   tearDown(() async => _controller.dispose());
 
   group('asset videos', () {
@@ -22,7 +22,7 @@
     testWidgets('can be initialized', (WidgetTester tester) async {
       await _controller.initialize();
 
-      expect(_controller.value.initialized, true);
+      expect(_controller.value.isInitialized, true);
       expect(_controller.value.position, const Duration(seconds: 0));
       expect(_controller.value.isPlaying, false);
       expect(_controller.value.duration,
diff --git a/packages/video_player/video_player/example/lib/main.dart b/packages/video_player/video_player/example/lib/main.dart
index a99b9da..42eaaa5 100644
--- a/packages/video_player/video_player/example/lib/main.dart
+++ b/packages/video_player/video_player/example/lib/main.dart
@@ -108,7 +108,7 @@
 
 /// A filler card to show the video in a list of scrolling contents.
 class _ExampleCard extends StatelessWidget {
-  const _ExampleCard({Key key, this.title}) : super(key: key);
+  const _ExampleCard({Key? key, required this.title}) : super(key: key);
 
   final String title;
 
@@ -150,7 +150,7 @@
 }
 
 class _ButterFlyAssetVideoState extends State<_ButterFlyAssetVideo> {
-  VideoPlayerController _controller;
+  late VideoPlayerController _controller;
 
   @override
   void initState() {
@@ -206,7 +206,7 @@
 }
 
 class _BumbleBeeRemoteVideoState extends State<_BumbleBeeRemoteVideo> {
-  VideoPlayerController _controller;
+  late VideoPlayerController _controller;
 
   Future<ClosedCaptionFile> _loadCaptions() async {
     final String fileContents = await DefaultAssetBundle.of(context)
@@ -265,7 +265,8 @@
 }
 
 class _ControlsOverlay extends StatelessWidget {
-  const _ControlsOverlay({Key key, this.controller}) : super(key: key);
+  const _ControlsOverlay({Key? key, required this.controller})
+      : super(key: key);
 
   static const _examplePlaybackRates = [
     0.25,
@@ -345,7 +346,7 @@
 }
 
 class _PlayerVideoAndPopPageState extends State<_PlayerVideoAndPopPage> {
-  VideoPlayerController _videoPlayerController;
+  late VideoPlayerController _videoPlayerController;
   bool startedPlaying = false;
 
   @override
diff --git a/packages/video_player/video_player/example/pubspec.yaml b/packages/video_player/video_player/example/pubspec.yaml
index e0afa41..eeab835 100644
--- a/packages/video_player/video_player/example/pubspec.yaml
+++ b/packages/video_player/video_player/example/pubspec.yaml
@@ -15,7 +15,7 @@
   integration_test:
     path: ../../../integration_test
   test: any
-  pedantic: ^1.8.0
+  pedantic: ^1.10.0-nullsafety.1
 
 flutter:
   uses-material-design: true
diff --git a/packages/video_player/video_player/example/test_driver/integration_test.dart b/packages/video_player/video_player/example/test_driver/integration_test.dart
index 7a2c213..7873aba 100644
--- a/packages/video_player/video_player/example/test_driver/integration_test.dart
+++ b/packages/video_player/video_player/example/test_driver/integration_test.dart
@@ -2,6 +2,9 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+// TODO(egarciad): Remove once Flutter driver is migrated to null safety.
+// @dart = 2.9
+
 import 'dart:async';
 import 'dart:convert';
 import 'dart:io';
diff --git a/packages/video_player/video_player/example/test_driver/video_player.dart b/packages/video_player/video_player/example/test_driver/video_player.dart
index cc498f4..c1ced19 100644
--- a/packages/video_player/video_player/example/test_driver/video_player.dart
+++ b/packages/video_player/video_player/example/test_driver/video_player.dart
@@ -2,6 +2,9 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+// TODO(egarciad): Remove once Flutter driver is migrated to null safety.
+// @dart = 2.9
+
 import 'package:flutter_driver/driver_extension.dart';
 import 'package:video_player_example/main.dart' as app;
 
diff --git a/packages/video_player/video_player/example/test_driver/video_player_test.dart b/packages/video_player/video_player/example/test_driver/video_player_test.dart
index 47f3867..fcbdbb2 100644
--- a/packages/video_player/video_player/example/test_driver/video_player_test.dart
+++ b/packages/video_player/video_player/example/test_driver/video_player_test.dart
@@ -2,6 +2,9 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+// TODO(egarciad): Remove once Flutter driver is migrated to null safety.
+// @dart = 2.9
+
 import 'dart:async';
 import 'package:flutter_driver/flutter_driver.dart';
 import 'package:test/test.dart';
diff --git a/packages/video_player/video_player/ios/Classes/messages.h b/packages/video_player/video_player/ios/Classes/messages.h
index 3c68b3d..84e8fc5 100644
--- a/packages/video_player/video_player/ios/Classes/messages.h
+++ b/packages/video_player/video_player/ios/Classes/messages.h
@@ -1,4 +1,4 @@
-// Autogenerated from Pigeon (v0.1.7), do not edit directly.
+// Autogenerated from Pigeon (v0.1.12), do not edit directly.
 // See also: https://pub.dev/packages/pigeon
 #import <Foundation/Foundation.h>
 @protocol FlutterBinaryMessenger;
diff --git a/packages/video_player/video_player/ios/Classes/messages.m b/packages/video_player/video_player/ios/Classes/messages.m
index e71f8b8..58ff729 100644
--- a/packages/video_player/video_player/ios/Classes/messages.m
+++ b/packages/video_player/video_player/ios/Classes/messages.m
@@ -1,4 +1,4 @@
-// Autogenerated from Pigeon (v0.1.7), do not edit directly.
+// Autogenerated from Pigeon (v0.1.12), do not edit directly.
 // See also: https://pub.dev/packages/pigeon
 #import "messages.h"
 #import <Flutter/Flutter.h>
@@ -7,6 +7,7 @@
 #error File requires ARC to be enabled.
 #endif
 
+#ifndef __clang_analyzer__
 static NSDictionary *wrapResult(NSDictionary *result, FlutterError *error) {
   NSDictionary *errorDict = (NSDictionary *)[NSNull null];
   if (error) {
@@ -59,9 +60,9 @@
   return result;
 }
 - (NSDictionary *)toMap {
-  return [NSDictionary
-      dictionaryWithObjectsAndKeys:(self.textureId != nil ? self.textureId : [NSNull null]),
-                                   @"textureId", nil];
+  return
+      [NSDictionary dictionaryWithObjectsAndKeys:(self.textureId ? self.textureId : [NSNull null]),
+                                                 @"textureId", nil];
 }
 @end
 
@@ -112,10 +113,9 @@
 }
 - (NSDictionary *)toMap {
   return [NSDictionary
-      dictionaryWithObjectsAndKeys:(self.textureId != nil ? self.textureId : [NSNull null]),
-                                   @"textureId",
-                                   (self.isLooping != nil ? self.isLooping : [NSNull null]),
-                                   @"isLooping", nil];
+      dictionaryWithObjectsAndKeys:(self.textureId ? self.textureId : [NSNull null]), @"textureId",
+                                   (self.isLooping ? self.isLooping : [NSNull null]), @"isLooping",
+                                   nil];
 }
 @end
 
@@ -134,9 +134,8 @@
 }
 - (NSDictionary *)toMap {
   return [NSDictionary
-      dictionaryWithObjectsAndKeys:(self.textureId != nil ? self.textureId : [NSNull null]),
-                                   @"textureId", (self.volume != nil ? self.volume : [NSNull null]),
-                                   @"volume", nil];
+      dictionaryWithObjectsAndKeys:(self.textureId ? self.textureId : [NSNull null]), @"textureId",
+                                   (self.volume ? self.volume : [NSNull null]), @"volume", nil];
 }
 @end
 
@@ -155,9 +154,8 @@
 }
 - (NSDictionary *)toMap {
   return [NSDictionary
-      dictionaryWithObjectsAndKeys:(self.textureId != nil ? self.textureId : [NSNull null]),
-                                   @"textureId", (self.speed != nil ? self.speed : [NSNull null]),
-                                   @"speed", nil];
+      dictionaryWithObjectsAndKeys:(self.textureId ? self.textureId : [NSNull null]), @"textureId",
+                                   (self.speed ? self.speed : [NSNull null]), @"speed", nil];
 }
 @end
 
@@ -176,10 +174,9 @@
 }
 - (NSDictionary *)toMap {
   return [NSDictionary
-      dictionaryWithObjectsAndKeys:(self.textureId != nil ? self.textureId : [NSNull null]),
-                                   @"textureId",
-                                   (self.position != nil ? self.position : [NSNull null]),
-                                   @"position", nil];
+      dictionaryWithObjectsAndKeys:(self.textureId ? self.textureId : [NSNull null]), @"textureId",
+                                   (self.position ? self.position : [NSNull null]), @"position",
+                                   nil];
 }
 @end
 
@@ -194,7 +191,7 @@
 }
 - (NSDictionary *)toMap {
   return [NSDictionary
-      dictionaryWithObjectsAndKeys:(self.mixWithOthers != nil ? self.mixWithOthers : [NSNull null]),
+      dictionaryWithObjectsAndKeys:(self.mixWithOthers ? self.mixWithOthers : [NSNull null]),
                                    @"mixWithOthers", nil];
 }
 @end
@@ -365,3 +362,4 @@
     }
   }
 }
+#endif
diff --git a/packages/video_player/video_player/lib/src/closed_caption_file.dart b/packages/video_player/video_player/lib/src/closed_caption_file.dart
index 550e30a..eae9bf5 100644
--- a/packages/video_player/video_player/lib/src/closed_caption_file.dart
+++ b/packages/video_player/video_player/lib/src/closed_caption_file.dart
@@ -31,7 +31,11 @@
   ///
   /// This is not recommended for direct use unless you are writing a parser for
   /// a new closed captioning file type.
-  const Caption({this.number, this.start, this.end, this.text});
+  const Caption(
+      {required this.number,
+      required this.start,
+      required this.end,
+      required this.text});
 
   /// The number that this caption was assigned.
   final int number;
@@ -46,6 +50,11 @@
   /// and [end].
   final String text;
 
+  /// A no caption object. This is a caption with [start] and [end] durations of zero,
+  /// and an empty [text] string.
+  static const Caption none =
+      Caption(number: 0, start: Duration.zero, end: Duration.zero, text: '');
+
   @override
   String toString() {
     return '$runtimeType('
diff --git a/packages/video_player/video_player/lib/src/sub_rip.dart b/packages/video_player/video_player/lib/src/sub_rip.dart
index 15dc43c..11456db 100644
--- a/packages/video_player/video_player/lib/src/sub_rip.dart
+++ b/packages/video_player/video_player/lib/src/sub_rip.dart
@@ -41,8 +41,7 @@
       end: startAndEnd.end,
       text: text,
     );
-
-    if (newCaption.start != null && newCaption.end != null) {
+    if (newCaption.start != newCaption.end) {
       captions.add(newCaption);
     }
   }
@@ -64,7 +63,7 @@
         RegExp(_subRipTimeStamp + _subRipArrow + _subRipTimeStamp);
 
     if (!format.hasMatch(line)) {
-      return _StartAndEnd(null, null);
+      return _StartAndEnd(Duration.zero, Duration.zero);
     }
 
     final List<String> times = line.split(_subRipArrow);
@@ -84,7 +83,7 @@
 // Duration(hours: 0, minutes: 1, seconds: 59, milliseconds: 084)
 Duration _parseSubRipTimestamp(String timestampString) {
   if (!RegExp(_subRipTimeStamp).hasMatch(timestampString)) {
-    return null;
+    return Duration.zero;
   }
 
   final List<String> commaSections = timestampString.split(',');
diff --git a/packages/video_player/video_player/lib/video_player.dart b/packages/video_player/video_player/lib/video_player.dart
index ac16450..6a2af76 100644
--- a/packages/video_player/video_player/lib/video_player.dart
+++ b/packages/video_player/video_player/lib/video_player.dart
@@ -28,11 +28,12 @@
   /// Constructs a video with the given values. Only [duration] is required. The
   /// rest will initialize with default values when unset.
   VideoPlayerValue({
-    @required this.duration,
-    this.size,
-    this.position = const Duration(),
-    this.caption = const Caption(),
+    required this.duration,
+    this.size = Size.zero,
+    this.position = Duration.zero,
+    this.caption = Caption.none,
     this.buffered = const <DurationRange>[],
+    this.isInitialized = false,
     this.isPlaying = false,
     this.isLooping = false,
     this.isBuffering = false,
@@ -41,17 +42,20 @@
     this.errorDescription,
   });
 
-  /// Returns an instance with a `null` [Duration].
-  VideoPlayerValue.uninitialized() : this(duration: null);
+  /// Returns an instance for a video that hasn't been loaded.
+  VideoPlayerValue.uninitialized()
+      : this(duration: Duration.zero, isInitialized: false);
 
-  /// Returns an instance with a `null` [Duration] and the given
-  /// [errorDescription].
+  /// Returns an instance with the given [errorDescription].
   VideoPlayerValue.erroneous(String errorDescription)
-      : this(duration: null, errorDescription: errorDescription);
+      : this(
+            duration: Duration.zero,
+            isInitialized: false,
+            errorDescription: errorDescription);
 
   /// The total duration of the video.
   ///
-  /// Is null when [initialized] is false.
+  /// The duration is [Duration.zero] if the video hasn't been initialized.
   final Duration duration;
 
   /// The current playback position.
@@ -60,7 +64,7 @@
   /// The [Caption] that should be displayed based on the current [position].
   ///
   /// This field will never be null. If there is no caption for the current
-  /// [position], this will be an empty [Caption] object.
+  /// [position], this will be a [Caption.none] object.
   final Caption caption;
 
   /// The currently buffered ranges.
@@ -84,7 +88,7 @@
   /// A description of the error if present.
   ///
   /// If [hasError] is false this is [null].
-  final String errorDescription;
+  final String? errorDescription;
 
   /// The [size] of the currently loaded video.
   ///
@@ -92,7 +96,7 @@
   final Size size;
 
   /// Indicates whether or not the video has been loaded and is ready to play.
-  bool get initialized => duration != null;
+  final bool isInitialized;
 
   /// Indicates whether or not the video is in an error state. If this is true
   /// [errorDescription] should have information about the problem.
@@ -101,7 +105,7 @@
   /// Returns [size.width] / [size.height] when size is non-null, or `1.0.` when
   /// size is null or the aspect ratio would be less than or equal to 0.0.
   double get aspectRatio {
-    if (size == null || size.width == 0 || size.height == 0) {
+    if (!isInitialized || size.width == 0 || size.height == 0) {
       return 1.0;
     }
     final double aspectRatio = size.width / size.height;
@@ -114,17 +118,18 @@
   /// Returns a new instance that has the same values as this current instance,
   /// except for any overrides passed in as arguments to [copyWidth].
   VideoPlayerValue copyWith({
-    Duration duration,
-    Size size,
-    Duration position,
-    Caption caption,
-    List<DurationRange> buffered,
-    bool isPlaying,
-    bool isLooping,
-    bool isBuffering,
-    double volume,
-    double playbackSpeed,
-    String errorDescription,
+    Duration? duration,
+    Size? size,
+    Duration? position,
+    Caption? caption,
+    List<DurationRange>? buffered,
+    bool? isInitialized,
+    bool? isPlaying,
+    bool? isLooping,
+    bool? isBuffering,
+    double? volume,
+    double? playbackSpeed,
+    String? errorDescription,
   }) {
     return VideoPlayerValue(
       duration: duration ?? this.duration,
@@ -132,6 +137,7 @@
       position: position ?? this.position,
       caption: caption ?? this.caption,
       buffered: buffered ?? this.buffered,
+      isInitialized: isInitialized ?? this.isInitialized,
       isPlaying: isPlaying ?? this.isPlaying,
       isLooping: isLooping ?? this.isLooping,
       isBuffering: isBuffering ?? this.isBuffering,
@@ -149,6 +155,7 @@
         'position: $position, '
         'caption: $caption, '
         'buffered: [${buffered.join(', ')}], '
+        'isInitialized: $isInitialized, '
         'isPlaying: $isPlaying, '
         'isLooping: $isLooping, '
         'isBuffering: $isBuffering, '
@@ -178,7 +185,7 @@
       {this.package, this.closedCaptionFile, this.videoPlayerOptions})
       : dataSourceType = DataSourceType.asset,
         formatHint = null,
-        super(VideoPlayerValue(duration: null));
+        super(VideoPlayerValue(duration: Duration.zero));
 
   /// Constructs a [VideoPlayerController] playing a video from obtained from
   /// the network.
@@ -191,7 +198,7 @@
       {this.formatHint, this.closedCaptionFile, this.videoPlayerOptions})
       : dataSourceType = DataSourceType.network,
         package = null,
-        super(VideoPlayerValue(duration: null));
+        super(VideoPlayerValue(duration: Duration.zero));
 
   /// Constructs a [VideoPlayerController] playing a video from a file.
   ///
@@ -203,9 +210,7 @@
         dataSourceType = DataSourceType.file,
         package = null,
         formatHint = null,
-        super(VideoPlayerValue(duration: null));
-
-  int _textureId;
+        super(VideoPlayerValue(duration: Duration.zero));
 
   /// The URI to the video file. This will be in different formats depending on
   /// the [DataSourceType] of the original video.
@@ -213,31 +218,36 @@
 
   /// **Android only**. Will override the platform's generic file format
   /// detection with whatever is set here.
-  final VideoFormat formatHint;
+  final VideoFormat? formatHint;
 
   /// Describes the type of data source this [VideoPlayerController]
   /// is constructed with.
   final DataSourceType dataSourceType;
 
   /// Provide additional configuration options (optional). Like setting the audio mode to mix
-  final VideoPlayerOptions videoPlayerOptions;
+  final VideoPlayerOptions? videoPlayerOptions;
 
   /// Only set for [asset] videos. The package that the asset was loaded from.
-  final String package;
+  final String? package;
 
   /// Optional field to specify a file containing the closed
   /// captioning.
   ///
   /// This future will be awaited and the file will be loaded when
   /// [initialize()] is called.
-  final Future<ClosedCaptionFile> closedCaptionFile;
+  final Future<ClosedCaptionFile>? closedCaptionFile;
 
-  ClosedCaptionFile _closedCaptionFile;
-  Timer _timer;
+  ClosedCaptionFile? _closedCaptionFile;
+  Timer? _timer;
   bool _isDisposed = false;
-  Completer<void> _creatingCompleter;
-  StreamSubscription<dynamic> _eventSubscription;
-  _VideoAppLifeCycleObserver _lifeCycleObserver;
+  Completer<void>? _creatingCompleter;
+  StreamSubscription<dynamic>? _eventSubscription;
+  late _VideoAppLifeCycleObserver _lifeCycleObserver;
+
+  /// The id of a texture that hasn't been initialized.
+  @visibleForTesting
+  static const int kUninitializedTextureId = -1;
+  int _textureId = kUninitializedTextureId;
 
   /// This is just exposed for testing. It shouldn't be used by anyone depending
   /// on the plugin.
@@ -250,7 +260,7 @@
     _lifeCycleObserver.initialize();
     _creatingCompleter = Completer<void>();
 
-    DataSource dataSourceDescription;
+    late DataSource dataSourceDescription;
     switch (dataSourceType) {
       case DataSourceType.asset:
         dataSourceDescription = DataSource(
@@ -276,11 +286,12 @@
 
     if (videoPlayerOptions?.mixWithOthers != null) {
       await _videoPlayerPlatform
-          .setMixWithOthers(videoPlayerOptions.mixWithOthers);
+          .setMixWithOthers(videoPlayerOptions!.mixWithOthers);
     }
 
-    _textureId = await _videoPlayerPlatform.create(dataSourceDescription);
-    _creatingCompleter.complete(null);
+    _textureId = (await _videoPlayerPlatform.create(dataSourceDescription)) ??
+        kUninitializedTextureId;
+    _creatingCompleter!.complete(null);
     final Completer<void> initializingCompleter = Completer<void>();
 
     void eventListener(VideoEvent event) {
@@ -293,6 +304,7 @@
           value = value.copyWith(
             duration: event.duration,
             size: event.size,
+            isInitialized: event.duration != null,
           );
           initializingCompleter.complete(null);
           _applyLooping();
@@ -325,8 +337,8 @@
     }
 
     void errorListener(Object obj) {
-      final PlatformException e = obj;
-      value = VideoPlayerValue.erroneous(e.message);
+      final PlatformException e = obj as PlatformException;
+      value = VideoPlayerValue.erroneous(e.message!);
       _timer?.cancel();
       if (!initializingCompleter.isCompleted) {
         initializingCompleter.completeError(obj);
@@ -342,7 +354,7 @@
   @override
   Future<void> dispose() async {
     if (_creatingCompleter != null) {
-      await _creatingCompleter.future;
+      await _creatingCompleter!.future;
       if (!_isDisposed) {
         _isDisposed = true;
         _timer?.cancel();
@@ -379,14 +391,14 @@
   }
 
   Future<void> _applyLooping() async {
-    if (!value.initialized || _isDisposed) {
+    if (!value.isInitialized || _isDisposed) {
       return;
     }
     await _videoPlayerPlatform.setLooping(_textureId, value.isLooping);
   }
 
   Future<void> _applyPlayPause() async {
-    if (!value.initialized || _isDisposed) {
+    if (!value.isInitialized || _isDisposed) {
       return;
     }
     if (value.isPlaying) {
@@ -400,8 +412,8 @@
           if (_isDisposed) {
             return;
           }
-          final Duration newPosition = await position;
-          if (_isDisposed) {
+          final Duration? newPosition = await position;
+          if (newPosition == null) {
             return;
           }
           _updatePosition(newPosition);
@@ -419,14 +431,14 @@
   }
 
   Future<void> _applyVolume() async {
-    if (!value.initialized || _isDisposed) {
+    if (!value.isInitialized || _isDisposed) {
       return;
     }
     await _videoPlayerPlatform.setVolume(_textureId, value.volume);
   }
 
   Future<void> _applyPlaybackSpeed() async {
-    if (!value.initialized || _isDisposed) {
+    if (!value.isInitialized || _isDisposed) {
       return;
     }
 
@@ -442,7 +454,7 @@
   }
 
   /// The position in the current video.
-  Future<Duration> get position async {
+  Future<Duration?> get position async {
     if (_isDisposed) {
       return null;
     }
@@ -519,17 +531,17 @@
   /// [Caption].
   Caption _getCaptionAt(Duration position) {
     if (_closedCaptionFile == null) {
-      return Caption();
+      return Caption.none;
     }
 
     // TODO: This would be more efficient as a binary search.
-    for (final caption in _closedCaptionFile.captions) {
+    for (final caption in _closedCaptionFile!.captions) {
       if (caption.start <= position && caption.end >= position) {
         return caption;
       }
     }
 
-    return Caption();
+    return Caption.none;
   }
 
   void _updatePosition(Duration position) {
@@ -545,7 +557,7 @@
   final VideoPlayerController _controller;
 
   void initialize() {
-    WidgetsBinding.instance.addObserver(this);
+    WidgetsBinding.instance!.addObserver(this);
   }
 
   @override
@@ -565,7 +577,7 @@
   }
 
   void dispose() {
-    WidgetsBinding.instance.removeObserver(this);
+    WidgetsBinding.instance!.removeObserver(this);
   }
 }
 
@@ -594,8 +606,9 @@
     };
   }
 
-  VoidCallback _listener;
-  int _textureId;
+  late VoidCallback _listener;
+
+  late int _textureId;
 
   @override
   void initState() {
@@ -622,7 +635,7 @@
 
   @override
   Widget build(BuildContext context) {
-    return _textureId == null
+    return _textureId == VideoPlayerController.kUninitializedTextureId
         ? Container()
         : _videoPlayerPlatform.buildView(_textureId);
   }
@@ -646,7 +659,7 @@
   /// [backgroundColor] defaults to gray at 50% opacity. This is the background
   /// color behind both [playedColor] and [bufferedColor] to denote the total
   /// size of the video compared to either of those values.
-  VideoProgressColors({
+  const VideoProgressColors({
     this.playedColor = const Color.fromRGBO(255, 0, 0, 0.7),
     this.bufferedColor = const Color.fromRGBO(50, 50, 200, 0.2),
     this.backgroundColor = const Color.fromRGBO(200, 200, 200, 0.5),
@@ -670,8 +683,8 @@
 
 class _VideoScrubber extends StatefulWidget {
   _VideoScrubber({
-    @required this.child,
-    @required this.controller,
+    required this.child,
+    required this.controller,
   });
 
   final Widget child;
@@ -689,7 +702,7 @@
   @override
   Widget build(BuildContext context) {
     void seekToRelativePosition(Offset globalPosition) {
-      final RenderBox box = context.findRenderObject();
+      final RenderBox box = context.findRenderObject() as RenderBox;
       final Offset tapPos = box.globalToLocal(globalPosition);
       final double relative = tapPos.dx / box.size.width;
       final Duration position = controller.value.duration * relative;
@@ -700,7 +713,7 @@
       behavior: HitTestBehavior.opaque,
       child: widget.child,
       onHorizontalDragStart: (DragStartDetails details) {
-        if (!controller.value.initialized) {
+        if (!controller.value.isInitialized) {
           return;
         }
         _controllerWasPlaying = controller.value.isPlaying;
@@ -709,7 +722,7 @@
         }
       },
       onHorizontalDragUpdate: (DragUpdateDetails details) {
-        if (!controller.value.initialized) {
+        if (!controller.value.isInitialized) {
           return;
         }
         seekToRelativePosition(details.globalPosition);
@@ -720,7 +733,7 @@
         }
       },
       onTapDown: (TapDownDetails details) {
-        if (!controller.value.initialized) {
+        if (!controller.value.isInitialized) {
           return;
         }
         seekToRelativePosition(details.globalPosition);
@@ -745,10 +758,10 @@
   /// to `top: 5.0`.
   VideoProgressIndicator(
     this.controller, {
-    VideoProgressColors colors,
-    this.allowScrubbing,
+    this.colors = const VideoProgressColors(),
+    required this.allowScrubbing,
     this.padding = const EdgeInsets.only(top: 5.0),
-  }) : colors = colors ?? VideoProgressColors();
+  });
 
   /// The [VideoPlayerController] that actually associates a video with this
   /// widget.
@@ -785,7 +798,7 @@
     };
   }
 
-  VoidCallback listener;
+  late VoidCallback listener;
 
   VideoPlayerController get controller => widget.controller;
 
@@ -806,7 +819,7 @@
   @override
   Widget build(BuildContext context) {
     Widget progressIndicator;
-    if (controller.value.initialized) {
+    if (controller.value.isInitialized) {
       final int duration = controller.value.duration.inMilliseconds;
       final int position = controller.value.position.inMilliseconds;
 
@@ -878,17 +891,17 @@
   /// [VideoPlayerValue.caption].
   ///
   /// If [text] is null, nothing will be displayed.
-  const ClosedCaption({Key key, this.text, this.textStyle}) : super(key: key);
+  const ClosedCaption({Key? key, this.text, this.textStyle}) : super(key: key);
 
   /// The text that will be shown in the closed caption, or null if no caption
   /// should be shown.
-  final String text;
+  final String? text;
 
   /// Specifies how the text in the closed caption should look.
   ///
   /// If null, defaults to [DefaultTextStyle.of(context).style] with size 36
   /// font colored white.
-  final TextStyle textStyle;
+  final TextStyle? textStyle;
 
   @override
   Widget build(BuildContext context) {
@@ -913,7 +926,7 @@
           ),
           child: Padding(
             padding: EdgeInsets.symmetric(horizontal: 2.0),
-            child: Text(text, style: effectiveTextStyle),
+            child: Text(text!, style: effectiveTextStyle),
           ),
         ),
       ),
diff --git a/packages/video_player/video_player/pigeons/messages.dart b/packages/video_player/video_player/pigeons/messages.dart
index 427aea2..f1771af 100644
--- a/packages/video_player/video_player/pigeons/messages.dart
+++ b/packages/video_player/video_player/pigeons/messages.dart
@@ -1,3 +1,5 @@
+// @dart = 2.9
+
 import 'package:pigeon/pigeon_lib.dart';
 
 class TextureMessage {
diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml
index 8234fee..cfefaad 100644
--- a/packages/video_player/video_player/pubspec.yaml
+++ b/packages/video_player/video_player/pubspec.yaml
@@ -4,7 +4,7 @@
 # 0.10.y+z is compatible with 1.0.0, if you land a breaking change bump
 # the version to 2.0.0.
 # See more details: https://github.com/flutter/flutter/wiki/Package-migration-to-1.0.0
-version: 0.11.1+4
+version: 2.0.0-nullsafety.1
 homepage: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player
 
 flutter:
@@ -19,15 +19,15 @@
         default_package: video_player_web
 
 dependencies:
-  meta: ^1.0.5
-  video_player_platform_interface: ^2.2.0
+  meta: ^1.3.0-nullsafety.3
+  video_player_platform_interface: ^3.0.0-nullsafety.1
 
   # The design on https://flutter.dev/go/federated-plugins was to leave
   # this constraint as "any". We cannot do it right now as it fails pub publish
   # validation, so we set a ^ constraint.
   # TODO(amirh): Revisit this (either update this part in the  design or the pub tool).
   # https://github.com/flutter/flutter/issues/46264
-  video_player_web: '>=0.1.4 <2.0.0'
+  video_player_web: ^2.0.0-nullsafety
 
   flutter:
     sdk: flutter
@@ -35,9 +35,9 @@
 dev_dependencies:
   flutter_test:
     sdk: flutter
-  pedantic: ^1.8.0
+  pedantic: ^1.10.0-nullsafety.1
   pigeon: 0.1.7
 
 environment:
-  sdk: ">=2.8.0 <3.0.0"
+  sdk: '>=2.10.0-56.0.dev <3.0.0'
   flutter: ">=1.12.13+hotfix.5 <2.0.0"
diff --git a/packages/video_player/video_player/test/sub_rip_file_test.dart b/packages/video_player/video_player/test/sub_rip_file_test.dart
index cf25ff7..2b9803d 100644
--- a/packages/video_player/video_player/test/sub_rip_file_test.dart
+++ b/packages/video_player/video_player/test/sub_rip_file_test.dart
@@ -108,6 +108,6 @@
 
 3
 00:01:54,724 --> 00:01:6,760
-This one should be ignored because the 
+This one should be ignored because the
 ned time is missing a digit.
 ''';
diff --git a/packages/video_player/video_player/test/video_player_initialization_test.dart b/packages/video_player/video_player/test/video_player_initialization_test.dart
index 231c399..1a09ed9 100644
--- a/packages/video_player/video_player/test/video_player_initialization_test.dart
+++ b/packages/video_player/video_player/test/video_player_initialization_test.dart
@@ -2,8 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// @dart = 2.8
-
 import 'package:flutter/widgets.dart';
 import 'package:flutter_test/flutter_test.dart';
 import 'package:video_player/video_player.dart';
diff --git a/packages/video_player/video_player/test/video_player_test.dart b/packages/video_player/video_player/test/video_player_test.dart
index a7b23c3..d9701ba 100644
--- a/packages/video_player/video_player/test/video_player_test.dart
+++ b/packages/video_player/video_player/test/video_player_test.dart
@@ -2,8 +2,6 @@
 // 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';
 
@@ -18,7 +16,7 @@
 
 class FakeController extends ValueNotifier<VideoPlayerValue>
     implements VideoPlayerController {
-  FakeController() : super(VideoPlayerValue(duration: null));
+  FakeController() : super(VideoPlayerValue(duration: Duration.zero));
 
   @override
   Future<void> dispose() async {
@@ -26,7 +24,7 @@
   }
 
   @override
-  int textureId;
+  int textureId = VideoPlayerController.kUninitializedTextureId;
 
   @override
   String get dataSource => '';
@@ -35,7 +33,7 @@
   DataSourceType get dataSourceType => DataSourceType.file;
 
   @override
-  String get package => null;
+  String get package => null!;
 
   @override
   Future<Duration> get position async => value.position;
@@ -62,13 +60,13 @@
   Future<void> setLooping(bool looping) async {}
 
   @override
-  VideoFormat get formatHint => null;
+  VideoFormat? get formatHint => null;
 
   @override
   Future<ClosedCaptionFile> get closedCaptionFile => _loadClosedCaption();
 
   @override
-  VideoPlayerOptions get videoPlayerOptions => null;
+  VideoPlayerOptions? get videoPlayerOptions => null;
 }
 
 Future<ClosedCaptionFile> _loadClosedCaption() async =>
@@ -80,11 +78,13 @@
     return <Caption>[
       Caption(
         text: 'one',
+        number: 0,
         start: Duration(milliseconds: 100),
         end: Duration(milliseconds: 200),
       ),
       Caption(
         text: 'two',
+        number: 1,
         start: Duration(milliseconds: 300),
         end: Duration(milliseconds: 400),
       ),
@@ -101,6 +101,7 @@
     controller.textureId = 123;
     controller.value = controller.value.copyWith(
       duration: const Duration(milliseconds: 100),
+      isInitialized: true,
     );
 
     await tester.pump();
@@ -133,8 +134,8 @@
       await tester.pumpWidget(MaterialApp(home: ClosedCaption(text: text)));
 
       final Text textWidget = tester.widget<Text>(find.text(text));
-      expect(textWidget.style.fontSize, 36.0);
-      expect(textWidget.style.color, Colors.white);
+      expect(textWidget.style!.fontSize, 36.0);
+      expect(textWidget.style!.color, Colors.white);
     });
 
     testWidgets('uses given text and style', (WidgetTester tester) async {
@@ -149,7 +150,7 @@
       expect(find.text(text), findsOneWidget);
 
       final Text textWidget = tester.widget<Text>(find.text(text));
-      expect(textWidget.style.fontSize, textStyle.fontSize);
+      expect(textWidget.style!.fontSize, textStyle.fontSize);
     });
 
     testWidgets('handles null text', (WidgetTester tester) async {
@@ -173,7 +174,7 @@
   });
 
   group('VideoPlayerController', () {
-    FakeVideoPlayerPlatform fakeVideoPlayerPlatform;
+    late FakeVideoPlayerPlatform fakeVideoPlayerPlatform;
 
     setUp(() {
       fakeVideoPlayerPlatform = FakeVideoPlayerPlatform();
@@ -221,7 +222,7 @@
           'http://testing.com/invalid_url',
         );
         try {
-          dynamic error;
+          late dynamic error;
           fakeVideoPlayerPlatform.forceInitError = true;
           await controller.initialize().catchError((dynamic e) => error = e);
           final PlatformException platformEx = error;
@@ -245,13 +246,14 @@
       final VideoPlayerController controller = VideoPlayerController.network(
         'https://127.0.0.1',
       );
-      expect(controller.textureId, isNull);
+      expect(
+          controller.textureId, VideoPlayerController.kUninitializedTextureId);
       expect(await controller.position, const Duration(seconds: 0));
       await controller.initialize();
 
       await controller.dispose();
 
-      expect(controller.textureId, isNotNull);
+      expect(controller.textureId, 0);
       expect(await controller.position, isNull);
     });
 
@@ -390,19 +392,19 @@
 
         await controller.initialize();
         expect(controller.value.position, const Duration());
-        expect(controller.value.caption.text, isNull);
+        expect(controller.value.caption.text, '');
 
         await controller.seekTo(const Duration(milliseconds: 100));
         expect(controller.value.caption.text, 'one');
 
         await controller.seekTo(const Duration(milliseconds: 250));
-        expect(controller.value.caption.text, isNull);
+        expect(controller.value.caption.text, '');
 
         await controller.seekTo(const Duration(milliseconds: 300));
         expect(controller.value.caption.text, 'two');
 
         await controller.seekTo(const Duration(milliseconds: 500));
-        expect(controller.value.caption.text, isNull);
+        expect(controller.value.caption.text, '');
 
         await controller.seekTo(const Duration(milliseconds: 300));
         expect(controller.value.caption.text, 'two');
@@ -419,8 +421,7 @@
         await controller.play();
         expect(controller.value.isPlaying, isTrue);
         final FakeVideoEventStream fakeVideoEventStream =
-            fakeVideoPlayerPlatform.streams[controller.textureId];
-        assert(fakeVideoEventStream != null);
+            fakeVideoPlayerPlatform.streams[controller.textureId]!;
 
         fakeVideoEventStream.eventsChannel
             .sendEvent(<String, dynamic>{'event': 'completed'});
@@ -438,8 +439,7 @@
         expect(controller.value.isBuffering, false);
         expect(controller.value.buffered, isEmpty);
         final FakeVideoEventStream fakeVideoEventStream =
-            fakeVideoPlayerPlatform.streams[controller.textureId];
-        assert(fakeVideoEventStream != null);
+            fakeVideoPlayerPlatform.streams[controller.textureId]!;
 
         fakeVideoEventStream.eventsChannel
             .sendEvent(<String, dynamic>{'event': 'bufferingStart'});
@@ -496,9 +496,9 @@
     test('uninitialized()', () {
       final VideoPlayerValue uninitialized = VideoPlayerValue.uninitialized();
 
-      expect(uninitialized.duration, isNull);
-      expect(uninitialized.position, equals(const Duration(seconds: 0)));
-      expect(uninitialized.caption, equals(const Caption()));
+      expect(uninitialized.duration, equals(Duration.zero));
+      expect(uninitialized.position, equals(Duration.zero));
+      expect(uninitialized.caption, equals(Caption.none));
       expect(uninitialized.buffered, isEmpty);
       expect(uninitialized.isPlaying, isFalse);
       expect(uninitialized.isLooping, isFalse);
@@ -506,9 +506,8 @@
       expect(uninitialized.volume, 1.0);
       expect(uninitialized.playbackSpeed, 1.0);
       expect(uninitialized.errorDescription, isNull);
-      expect(uninitialized.size, isNull);
-      expect(uninitialized.size, isNull);
-      expect(uninitialized.initialized, isFalse);
+      expect(uninitialized.size, equals(Size.zero));
+      expect(uninitialized.isInitialized, isFalse);
       expect(uninitialized.hasError, isFalse);
       expect(uninitialized.aspectRatio, 1.0);
     });
@@ -517,9 +516,9 @@
       const String errorMessage = 'foo';
       final VideoPlayerValue error = VideoPlayerValue.erroneous(errorMessage);
 
-      expect(error.duration, isNull);
-      expect(error.position, equals(const Duration(seconds: 0)));
-      expect(error.caption, equals(const Caption()));
+      expect(error.duration, equals(Duration.zero));
+      expect(error.position, equals(Duration.zero));
+      expect(error.caption, equals(Caption.none));
       expect(error.buffered, isEmpty);
       expect(error.isPlaying, isFalse);
       expect(error.isLooping, isFalse);
@@ -527,9 +526,8 @@
       expect(error.volume, 1.0);
       expect(error.playbackSpeed, 1.0);
       expect(error.errorDescription, errorMessage);
-      expect(error.size, isNull);
-      expect(error.size, isNull);
-      expect(error.initialized, isFalse);
+      expect(error.size, equals(Size.zero));
+      expect(error.isInitialized, isFalse);
       expect(error.hasError, isTrue);
       expect(error.aspectRatio, 1.0);
     });
@@ -538,10 +536,12 @@
       const Duration duration = Duration(seconds: 5);
       const Size size = Size(400, 300);
       const Duration position = Duration(seconds: 1);
-      const Caption caption = Caption(text: 'foo');
+      const Caption caption = Caption(
+          text: 'foo', number: 0, start: Duration.zero, end: Duration.zero);
       final List<DurationRange> buffered = <DurationRange>[
         DurationRange(const Duration(seconds: 0), const Duration(seconds: 4))
       ];
+      const bool isInitialized = true;
       const bool isPlaying = true;
       const bool isLooping = true;
       const bool isBuffering = true;
@@ -554,6 +554,7 @@
         position: position,
         caption: caption,
         buffered: buffered,
+        isInitialized: isInitialized,
         isPlaying: isPlaying,
         isLooping: isLooping,
         isBuffering: isBuffering,
@@ -568,6 +569,7 @@
           'position: 0:00:01.000000, '
           'caption: Caption(number: null, start: null, end: null, text: foo), '
           'buffered: [DurationRange(start: 0:00:00.000000, end: 0:00:04.000000)], '
+          'isInitialized: true, '
           'isPlaying: true, '
           'isLooping: true, '
           'isBuffering: true, '
@@ -586,15 +588,16 @@
     group('aspectRatio', () {
       test('640x480 -> 4:3', () {
         final value = VideoPlayerValue(
+          isInitialized: true,
           size: Size(640, 480),
           duration: Duration(seconds: 1),
         );
         expect(value.aspectRatio, 4 / 3);
       });
 
-      test('null size -> 1.0', () {
+      test('no size -> 1.0', () {
         final value = VideoPlayerValue(
-          size: null,
+          isInitialized: true,
           duration: Duration(seconds: 1),
         );
         expect(value.aspectRatio, 1.0);
@@ -602,6 +605,7 @@
 
       test('height = 0 -> 1.0', () {
         final value = VideoPlayerValue(
+          isInitialized: true,
           size: Size(640, 0),
           duration: Duration(seconds: 1),
         );
@@ -610,6 +614,7 @@
 
       test('width = 0 -> 1.0', () {
         final value = VideoPlayerValue(
+          isInitialized: true,
           size: Size(0, 480),
           duration: Duration(seconds: 1),
         );
@@ -618,6 +623,7 @@
 
       test('negative aspect ratio -> 1.0', () {
         final value = VideoPlayerValue(
+          isInitialized: true,
           size: Size(640, -480),
           duration: Duration(seconds: 1),
         );
@@ -646,7 +652,7 @@
         File(''),
         videoPlayerOptions: VideoPlayerOptions(mixWithOthers: true));
     await controller.initialize();
-    expect(controller.videoPlayerOptions.mixWithOthers, true);
+    expect(controller.videoPlayerOptions!.mixWithOthers, true);
   });
 }
 
@@ -706,7 +712,7 @@
   @override
   void seekTo(PositionMessage arg) {
     calls.add('seekTo');
-    _positions[arg.textureId] = Duration(milliseconds: arg.position);
+    _positions[arg.textureId!] = Duration(milliseconds: arg.position!);
   }
 
   @override
@@ -742,7 +748,7 @@
   int height;
   Duration duration;
   bool initWithError;
-  FakeEventsChannel eventsChannel;
+  late FakeEventsChannel eventsChannel;
 
   void onListen() {
     if (!initWithError) {
@@ -764,7 +770,7 @@
     eventsMethodChannel.setMockMethodCallHandler(onMethodCall);
   }
 
-  MethodChannel eventsMethodChannel;
+  late MethodChannel eventsMethodChannel;
   VoidCallback onListen;
 
   Future<dynamic> onMethodCall(MethodCall call) {
@@ -780,7 +786,7 @@
     _sendMessage(const StandardMethodCodec().encodeSuccessEnvelope(event));
   }
 
-  void sendError(String code, [String message, dynamic details]) {
+  void sendError(String code, [String? message, dynamic details]) {
     _sendMessage(const StandardMethodCodec().encodeErrorEnvelope(
       code: code,
       message: message,
@@ -794,6 +800,6 @@
     // available on all the versions of Flutter that we test.
     // ignore: deprecated_member_use
     defaultBinaryMessenger.handlePlatformMessage(
-        eventsMethodChannel.name, data, (ByteData data) {});
+        eventsMethodChannel.name, data, (ByteData? data) {});
   }
 }
diff --git a/packages/video_player/video_player_platform_interface/CHANGELOG.md b/packages/video_player/video_player_platform_interface/CHANGELOG.md
index 8af22f7..1522d2b 100644
--- a/packages/video_player/video_player_platform_interface/CHANGELOG.md
+++ b/packages/video_player/video_player_platform_interface/CHANGELOG.md
@@ -1,3 +1,11 @@
+## 3.0.0-nullsafety.1
+
+* Make DataSource's `uri` parameter nullable.
+
+## 3.0.0-nullsafety
+
+* Migrate to null safety.
+
 ## 2.2.0
 
 * Added option to set the video playback speed on the video controller.
diff --git a/packages/video_player/video_player_platform_interface/analysis_options.yaml b/packages/video_player/video_player_platform_interface/analysis_options.yaml
new file mode 100644
index 0000000..3d64bb5
--- /dev/null
+++ b/packages/video_player/video_player_platform_interface/analysis_options.yaml
@@ -0,0 +1,4 @@
+include: ../../../analysis_options.yaml
+analyzer:
+  enable-experiment:
+    - non-nullable
diff --git a/packages/video_player/video_player_platform_interface/lib/messages.dart b/packages/video_player/video_player_platform_interface/lib/messages.dart
index bfe65f1..5faf6fb 100644
--- a/packages/video_player/video_player_platform_interface/lib/messages.dart
+++ b/packages/video_player/video_player_platform_interface/lib/messages.dart
@@ -1,13 +1,13 @@
-// Autogenerated from Pigeon (v0.1.7), do not edit directly.
+// Autogenerated from Pigeon (v0.1.12), do not edit directly.
 // See also: https://pub.dev/packages/pigeon
 // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import
-// @dart = 2.8
+// @dart = 2.10
 import 'dart:async';
 import 'package:flutter/services.dart';
 import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List;
 
 class TextureMessage {
-  int textureId;
+  int? textureId;
   // ignore: unused_element
   Map<dynamic, dynamic> _toMap() {
     final Map<dynamic, dynamic> pigeonMap = <dynamic, dynamic>{};
@@ -17,9 +17,6 @@
 
   // ignore: unused_element
   static TextureMessage _fromMap(Map<dynamic, dynamic> pigeonMap) {
-    if (pigeonMap == null) {
-      return null;
-    }
     final TextureMessage result = TextureMessage();
     result.textureId = pigeonMap['textureId'];
     return result;
@@ -27,10 +24,10 @@
 }
 
 class CreateMessage {
-  String asset;
-  String uri;
-  String packageName;
-  String formatHint;
+  String? asset;
+  String? uri;
+  String? packageName;
+  String? formatHint;
   // ignore: unused_element
   Map<dynamic, dynamic> _toMap() {
     final Map<dynamic, dynamic> pigeonMap = <dynamic, dynamic>{};
@@ -43,9 +40,6 @@
 
   // ignore: unused_element
   static CreateMessage _fromMap(Map<dynamic, dynamic> pigeonMap) {
-    if (pigeonMap == null) {
-      return null;
-    }
     final CreateMessage result = CreateMessage();
     result.asset = pigeonMap['asset'];
     result.uri = pigeonMap['uri'];
@@ -56,8 +50,8 @@
 }
 
 class LoopingMessage {
-  int textureId;
-  bool isLooping;
+  int? textureId;
+  bool? isLooping;
   // ignore: unused_element
   Map<dynamic, dynamic> _toMap() {
     final Map<dynamic, dynamic> pigeonMap = <dynamic, dynamic>{};
@@ -68,9 +62,6 @@
 
   // ignore: unused_element
   static LoopingMessage _fromMap(Map<dynamic, dynamic> pigeonMap) {
-    if (pigeonMap == null) {
-      return null;
-    }
     final LoopingMessage result = LoopingMessage();
     result.textureId = pigeonMap['textureId'];
     result.isLooping = pigeonMap['isLooping'];
@@ -79,8 +70,8 @@
 }
 
 class VolumeMessage {
-  int textureId;
-  double volume;
+  int? textureId;
+  double? volume;
   // ignore: unused_element
   Map<dynamic, dynamic> _toMap() {
     final Map<dynamic, dynamic> pigeonMap = <dynamic, dynamic>{};
@@ -91,9 +82,6 @@
 
   // ignore: unused_element
   static VolumeMessage _fromMap(Map<dynamic, dynamic> pigeonMap) {
-    if (pigeonMap == null) {
-      return null;
-    }
     final VolumeMessage result = VolumeMessage();
     result.textureId = pigeonMap['textureId'];
     result.volume = pigeonMap['volume'];
@@ -102,8 +90,8 @@
 }
 
 class PlaybackSpeedMessage {
-  int textureId;
-  double speed;
+  int? textureId;
+  double? speed;
   // ignore: unused_element
   Map<dynamic, dynamic> _toMap() {
     final Map<dynamic, dynamic> pigeonMap = <dynamic, dynamic>{};
@@ -114,9 +102,6 @@
 
   // ignore: unused_element
   static PlaybackSpeedMessage _fromMap(Map<dynamic, dynamic> pigeonMap) {
-    if (pigeonMap == null) {
-      return null;
-    }
     final PlaybackSpeedMessage result = PlaybackSpeedMessage();
     result.textureId = pigeonMap['textureId'];
     result.speed = pigeonMap['speed'];
@@ -125,8 +110,8 @@
 }
 
 class PositionMessage {
-  int textureId;
-  int position;
+  int? textureId;
+  int? position;
   // ignore: unused_element
   Map<dynamic, dynamic> _toMap() {
     final Map<dynamic, dynamic> pigeonMap = <dynamic, dynamic>{};
@@ -137,9 +122,6 @@
 
   // ignore: unused_element
   static PositionMessage _fromMap(Map<dynamic, dynamic> pigeonMap) {
-    if (pigeonMap == null) {
-      return null;
-    }
     final PositionMessage result = PositionMessage();
     result.textureId = pigeonMap['textureId'];
     result.position = pigeonMap['position'];
@@ -148,7 +130,7 @@
 }
 
 class MixWithOthersMessage {
-  bool mixWithOthers;
+  bool? mixWithOthers;
   // ignore: unused_element
   Map<dynamic, dynamic> _toMap() {
     final Map<dynamic, dynamic> pigeonMap = <dynamic, dynamic>{};
@@ -158,9 +140,6 @@
 
   // ignore: unused_element
   static MixWithOthersMessage _fromMap(Map<dynamic, dynamic> pigeonMap) {
-    if (pigeonMap == null) {
-      return null;
-    }
     final MixWithOthersMessage result = MixWithOthersMessage();
     result.mixWithOthers = pigeonMap['mixWithOthers'];
     return result;
@@ -172,7 +151,7 @@
     const BasicMessageChannel<dynamic> channel = BasicMessageChannel<dynamic>(
         'dev.flutter.pigeon.VideoPlayerApi.initialize', StandardMessageCodec());
 
-    final Map<dynamic, dynamic> replyMap = await channel.send(null);
+    final Map<dynamic, dynamic>? replyMap = await channel.send(null);
     if (replyMap == null) {
       throw PlatformException(
           code: 'channel-error',
@@ -194,7 +173,7 @@
     const BasicMessageChannel<dynamic> channel = BasicMessageChannel<dynamic>(
         'dev.flutter.pigeon.VideoPlayerApi.create', StandardMessageCodec());
 
-    final Map<dynamic, dynamic> replyMap = await channel.send(requestMap);
+    final Map<dynamic, dynamic>? replyMap = await channel.send(requestMap);
     if (replyMap == null) {
       throw PlatformException(
           code: 'channel-error',
@@ -216,7 +195,7 @@
     const BasicMessageChannel<dynamic> channel = BasicMessageChannel<dynamic>(
         'dev.flutter.pigeon.VideoPlayerApi.dispose', StandardMessageCodec());
 
-    final Map<dynamic, dynamic> replyMap = await channel.send(requestMap);
+    final Map<dynamic, dynamic>? replyMap = await channel.send(requestMap);
     if (replyMap == null) {
       throw PlatformException(
           code: 'channel-error',
@@ -238,7 +217,7 @@
     const BasicMessageChannel<dynamic> channel = BasicMessageChannel<dynamic>(
         'dev.flutter.pigeon.VideoPlayerApi.setLooping', StandardMessageCodec());
 
-    final Map<dynamic, dynamic> replyMap = await channel.send(requestMap);
+    final Map<dynamic, dynamic>? replyMap = await channel.send(requestMap);
     if (replyMap == null) {
       throw PlatformException(
           code: 'channel-error',
@@ -260,7 +239,7 @@
     const BasicMessageChannel<dynamic> channel = BasicMessageChannel<dynamic>(
         'dev.flutter.pigeon.VideoPlayerApi.setVolume', StandardMessageCodec());
 
-    final Map<dynamic, dynamic> replyMap = await channel.send(requestMap);
+    final Map<dynamic, dynamic>? replyMap = await channel.send(requestMap);
     if (replyMap == null) {
       throw PlatformException(
           code: 'channel-error',
@@ -283,7 +262,7 @@
         'dev.flutter.pigeon.VideoPlayerApi.setPlaybackSpeed',
         StandardMessageCodec());
 
-    final Map<dynamic, dynamic> replyMap = await channel.send(requestMap);
+    final Map<dynamic, dynamic>? replyMap = await channel.send(requestMap);
     if (replyMap == null) {
       throw PlatformException(
           code: 'channel-error',
@@ -305,7 +284,7 @@
     const BasicMessageChannel<dynamic> channel = BasicMessageChannel<dynamic>(
         'dev.flutter.pigeon.VideoPlayerApi.play', StandardMessageCodec());
 
-    final Map<dynamic, dynamic> replyMap = await channel.send(requestMap);
+    final Map<dynamic, dynamic>? replyMap = await channel.send(requestMap);
     if (replyMap == null) {
       throw PlatformException(
           code: 'channel-error',
@@ -327,7 +306,7 @@
     const BasicMessageChannel<dynamic> channel = BasicMessageChannel<dynamic>(
         'dev.flutter.pigeon.VideoPlayerApi.position', StandardMessageCodec());
 
-    final Map<dynamic, dynamic> replyMap = await channel.send(requestMap);
+    final Map<dynamic, dynamic>? replyMap = await channel.send(requestMap);
     if (replyMap == null) {
       throw PlatformException(
           code: 'channel-error',
@@ -349,7 +328,7 @@
     const BasicMessageChannel<dynamic> channel = BasicMessageChannel<dynamic>(
         'dev.flutter.pigeon.VideoPlayerApi.seekTo', StandardMessageCodec());
 
-    final Map<dynamic, dynamic> replyMap = await channel.send(requestMap);
+    final Map<dynamic, dynamic>? replyMap = await channel.send(requestMap);
     if (replyMap == null) {
       throw PlatformException(
           code: 'channel-error',
@@ -371,7 +350,7 @@
     const BasicMessageChannel<dynamic> channel = BasicMessageChannel<dynamic>(
         'dev.flutter.pigeon.VideoPlayerApi.pause', StandardMessageCodec());
 
-    final Map<dynamic, dynamic> replyMap = await channel.send(requestMap);
+    final Map<dynamic, dynamic>? replyMap = await channel.send(requestMap);
     if (replyMap == null) {
       throw PlatformException(
           code: 'channel-error',
@@ -394,7 +373,7 @@
         'dev.flutter.pigeon.VideoPlayerApi.setMixWithOthers',
         StandardMessageCodec());
 
-    final Map<dynamic, dynamic> replyMap = await channel.send(requestMap);
+    final Map<dynamic, dynamic>? replyMap = await channel.send(requestMap);
     if (replyMap == null) {
       throw PlatformException(
           code: 'channel-error',
@@ -424,131 +403,175 @@
   void seekTo(PositionMessage arg);
   void pause(TextureMessage arg);
   void setMixWithOthers(MixWithOthersMessage arg);
-  static void setup(TestHostVideoPlayerApi api) {
+  static void setup(TestHostVideoPlayerApi? api) {
     {
       const BasicMessageChannel<dynamic> channel = BasicMessageChannel<dynamic>(
           'dev.flutter.pigeon.VideoPlayerApi.initialize',
           StandardMessageCodec());
-      channel.setMockMessageHandler((dynamic message) async {
-        api.initialize();
-        return <dynamic, dynamic>{};
-      });
+      if (api == null) {
+        channel.setMockMessageHandler(null);
+      } else {
+        channel.setMockMessageHandler((dynamic message) async {
+          api.initialize();
+          return <dynamic, dynamic>{};
+        });
+      }
     }
     {
       const BasicMessageChannel<dynamic> channel = BasicMessageChannel<dynamic>(
           'dev.flutter.pigeon.VideoPlayerApi.create', StandardMessageCodec());
-      channel.setMockMessageHandler((dynamic message) async {
-        final Map<dynamic, dynamic> mapMessage =
-            message as Map<dynamic, dynamic>;
-        final CreateMessage input = CreateMessage._fromMap(mapMessage);
-        final TextureMessage output = api.create(input);
-        return <dynamic, dynamic>{'result': output._toMap()};
-      });
+      if (api == null) {
+        channel.setMockMessageHandler(null);
+      } else {
+        channel.setMockMessageHandler((dynamic message) async {
+          final Map<dynamic, dynamic> mapMessage =
+              message as Map<dynamic, dynamic>;
+          final CreateMessage input = CreateMessage._fromMap(mapMessage);
+          final TextureMessage output = api.create(input);
+          return <dynamic, dynamic>{'result': output._toMap()};
+        });
+      }
     }
     {
       const BasicMessageChannel<dynamic> channel = BasicMessageChannel<dynamic>(
           'dev.flutter.pigeon.VideoPlayerApi.dispose', StandardMessageCodec());
-      channel.setMockMessageHandler((dynamic message) async {
-        final Map<dynamic, dynamic> mapMessage =
-            message as Map<dynamic, dynamic>;
-        final TextureMessage input = TextureMessage._fromMap(mapMessage);
-        api.dispose(input);
-        return <dynamic, dynamic>{};
-      });
+      if (api == null) {
+        channel.setMockMessageHandler(null);
+      } else {
+        channel.setMockMessageHandler((dynamic message) async {
+          final Map<dynamic, dynamic> mapMessage =
+              message as Map<dynamic, dynamic>;
+          final TextureMessage input = TextureMessage._fromMap(mapMessage);
+          api.dispose(input);
+          return <dynamic, dynamic>{};
+        });
+      }
     }
     {
       const BasicMessageChannel<dynamic> channel = BasicMessageChannel<dynamic>(
           'dev.flutter.pigeon.VideoPlayerApi.setLooping',
           StandardMessageCodec());
-      channel.setMockMessageHandler((dynamic message) async {
-        final Map<dynamic, dynamic> mapMessage =
-            message as Map<dynamic, dynamic>;
-        final LoopingMessage input = LoopingMessage._fromMap(mapMessage);
-        api.setLooping(input);
-        return <dynamic, dynamic>{};
-      });
+      if (api == null) {
+        channel.setMockMessageHandler(null);
+      } else {
+        channel.setMockMessageHandler((dynamic message) async {
+          final Map<dynamic, dynamic> mapMessage =
+              message as Map<dynamic, dynamic>;
+          final LoopingMessage input = LoopingMessage._fromMap(mapMessage);
+          api.setLooping(input);
+          return <dynamic, dynamic>{};
+        });
+      }
     }
     {
       const BasicMessageChannel<dynamic> channel = BasicMessageChannel<dynamic>(
           'dev.flutter.pigeon.VideoPlayerApi.setVolume',
           StandardMessageCodec());
-      channel.setMockMessageHandler((dynamic message) async {
-        final Map<dynamic, dynamic> mapMessage =
-            message as Map<dynamic, dynamic>;
-        final VolumeMessage input = VolumeMessage._fromMap(mapMessage);
-        api.setVolume(input);
-        return <dynamic, dynamic>{};
-      });
+      if (api == null) {
+        channel.setMockMessageHandler(null);
+      } else {
+        channel.setMockMessageHandler((dynamic message) async {
+          final Map<dynamic, dynamic> mapMessage =
+              message as Map<dynamic, dynamic>;
+          final VolumeMessage input = VolumeMessage._fromMap(mapMessage);
+          api.setVolume(input);
+          return <dynamic, dynamic>{};
+        });
+      }
     }
     {
       const BasicMessageChannel<dynamic> channel = BasicMessageChannel<dynamic>(
           'dev.flutter.pigeon.VideoPlayerApi.setPlaybackSpeed',
           StandardMessageCodec());
-      channel.setMockMessageHandler((dynamic message) async {
-        final Map<dynamic, dynamic> mapMessage =
-            message as Map<dynamic, dynamic>;
-        final PlaybackSpeedMessage input =
-            PlaybackSpeedMessage._fromMap(mapMessage);
-        api.setPlaybackSpeed(input);
-        return <dynamic, dynamic>{};
-      });
+      if (api == null) {
+        channel.setMockMessageHandler(null);
+      } else {
+        channel.setMockMessageHandler((dynamic message) async {
+          final Map<dynamic, dynamic> mapMessage =
+              message as Map<dynamic, dynamic>;
+          final PlaybackSpeedMessage input =
+              PlaybackSpeedMessage._fromMap(mapMessage);
+          api.setPlaybackSpeed(input);
+          return <dynamic, dynamic>{};
+        });
+      }
     }
     {
       const BasicMessageChannel<dynamic> channel = BasicMessageChannel<dynamic>(
           'dev.flutter.pigeon.VideoPlayerApi.play', StandardMessageCodec());
-      channel.setMockMessageHandler((dynamic message) async {
-        final Map<dynamic, dynamic> mapMessage =
-            message as Map<dynamic, dynamic>;
-        final TextureMessage input = TextureMessage._fromMap(mapMessage);
-        api.play(input);
-        return <dynamic, dynamic>{};
-      });
+      if (api == null) {
+        channel.setMockMessageHandler(null);
+      } else {
+        channel.setMockMessageHandler((dynamic message) async {
+          final Map<dynamic, dynamic> mapMessage =
+              message as Map<dynamic, dynamic>;
+          final TextureMessage input = TextureMessage._fromMap(mapMessage);
+          api.play(input);
+          return <dynamic, dynamic>{};
+        });
+      }
     }
     {
       const BasicMessageChannel<dynamic> channel = BasicMessageChannel<dynamic>(
           'dev.flutter.pigeon.VideoPlayerApi.position', StandardMessageCodec());
-      channel.setMockMessageHandler((dynamic message) async {
-        final Map<dynamic, dynamic> mapMessage =
-            message as Map<dynamic, dynamic>;
-        final TextureMessage input = TextureMessage._fromMap(mapMessage);
-        final PositionMessage output = api.position(input);
-        return <dynamic, dynamic>{'result': output._toMap()};
-      });
+      if (api == null) {
+        channel.setMockMessageHandler(null);
+      } else {
+        channel.setMockMessageHandler((dynamic message) async {
+          final Map<dynamic, dynamic> mapMessage =
+              message as Map<dynamic, dynamic>;
+          final TextureMessage input = TextureMessage._fromMap(mapMessage);
+          final PositionMessage output = api.position(input);
+          return <dynamic, dynamic>{'result': output._toMap()};
+        });
+      }
     }
     {
       const BasicMessageChannel<dynamic> channel = BasicMessageChannel<dynamic>(
           'dev.flutter.pigeon.VideoPlayerApi.seekTo', StandardMessageCodec());
-      channel.setMockMessageHandler((dynamic message) async {
-        final Map<dynamic, dynamic> mapMessage =
-            message as Map<dynamic, dynamic>;
-        final PositionMessage input = PositionMessage._fromMap(mapMessage);
-        api.seekTo(input);
-        return <dynamic, dynamic>{};
-      });
+      if (api == null) {
+        channel.setMockMessageHandler(null);
+      } else {
+        channel.setMockMessageHandler((dynamic message) async {
+          final Map<dynamic, dynamic> mapMessage =
+              message as Map<dynamic, dynamic>;
+          final PositionMessage input = PositionMessage._fromMap(mapMessage);
+          api.seekTo(input);
+          return <dynamic, dynamic>{};
+        });
+      }
     }
     {
       const BasicMessageChannel<dynamic> channel = BasicMessageChannel<dynamic>(
           'dev.flutter.pigeon.VideoPlayerApi.pause', StandardMessageCodec());
-      channel.setMockMessageHandler((dynamic message) async {
-        final Map<dynamic, dynamic> mapMessage =
-            message as Map<dynamic, dynamic>;
-        final TextureMessage input = TextureMessage._fromMap(mapMessage);
-        api.pause(input);
-        return <dynamic, dynamic>{};
-      });
+      if (api == null) {
+        channel.setMockMessageHandler(null);
+      } else {
+        channel.setMockMessageHandler((dynamic message) async {
+          final Map<dynamic, dynamic> mapMessage =
+              message as Map<dynamic, dynamic>;
+          final TextureMessage input = TextureMessage._fromMap(mapMessage);
+          api.pause(input);
+          return <dynamic, dynamic>{};
+        });
+      }
     }
     {
       const BasicMessageChannel<dynamic> channel = BasicMessageChannel<dynamic>(
           'dev.flutter.pigeon.VideoPlayerApi.setMixWithOthers',
           StandardMessageCodec());
-      channel.setMockMessageHandler((dynamic message) async {
-        final Map<dynamic, dynamic> mapMessage =
-            message as Map<dynamic, dynamic>;
-        final MixWithOthersMessage input =
-            MixWithOthersMessage._fromMap(mapMessage);
-        api.setMixWithOthers(input);
-        return <dynamic, dynamic>{};
-      });
+      if (api == null) {
+        channel.setMockMessageHandler(null);
+      } else {
+        channel.setMockMessageHandler((dynamic message) async {
+          final Map<dynamic, dynamic> mapMessage =
+              message as Map<dynamic, dynamic>;
+          final MixWithOthersMessage input =
+              MixWithOthersMessage._fromMap(mapMessage);
+          api.setMixWithOthers(input);
+          return <dynamic, dynamic>{};
+        });
+      }
     }
   }
 }
diff --git a/packages/video_player/video_player_platform_interface/lib/method_channel_video_player.dart b/packages/video_player/video_player_platform_interface/lib/method_channel_video_player.dart
index 0ea443f..9b007d0 100644
--- a/packages/video_player/video_player_platform_interface/lib/method_channel_video_player.dart
+++ b/packages/video_player/video_player_platform_interface/lib/method_channel_video_player.dart
@@ -26,7 +26,7 @@
   }
 
   @override
-  Future<int> create(DataSource dataSource) async {
+  Future<int?> create(DataSource dataSource) async {
     CreateMessage message = CreateMessage();
 
     switch (dataSource.sourceType) {
@@ -91,7 +91,7 @@
   Future<Duration> getPosition(int textureId) async {
     PositionMessage response =
         await _api.position(TextureMessage()..textureId = textureId);
-    return Duration(milliseconds: response.position);
+    return Duration(milliseconds: response.position!);
   }
 
   @override
diff --git a/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart b/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart
index 2757fb1..f2bc002 100644
--- a/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart
+++ b/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart
@@ -7,7 +7,7 @@
 
 import 'package:flutter/foundation.dart';
 import 'package:flutter/widgets.dart';
-import 'package:meta/meta.dart' show required, visibleForTesting;
+import 'package:meta/meta.dart' show visibleForTesting;
 
 import 'method_channel_video_player.dart';
 
@@ -66,7 +66,7 @@
   }
 
   /// Creates an instance of a video player and returns its textureId.
-  Future<int> create(DataSource dataSource) {
+  Future<int?> create(DataSource dataSource) {
     throw UnimplementedError('create() has not been implemented.');
   }
 
@@ -146,7 +146,7 @@
   /// The [package] argument must be non-null when the asset comes from a
   /// package and null otherwise.
   DataSource({
-    @required this.sourceType,
+    required this.sourceType,
     this.uri,
     this.formatHint,
     this.asset,
@@ -163,18 +163,18 @@
   ///
   /// This will be in different formats depending on the [DataSourceType] of
   /// the original video.
-  final String uri;
+  final String? uri;
 
   /// **Android only**. Will override the platform's generic file format
   /// detection with whatever is set here.
-  final VideoFormat formatHint;
+  final VideoFormat? formatHint;
 
   /// The name of the asset. Only set for [DataSourceType.asset] videos.
-  final String asset;
+  final String? asset;
 
   /// The package that the asset was loaded from. Only set for
   /// [DataSourceType.asset] videos.
-  final String package;
+  final String? package;
 }
 
 /// The way in which the video was originally loaded.
@@ -216,7 +216,7 @@
   /// Depending on the [eventType], the [duration], [size] and [buffered]
   /// arguments can be null.
   VideoEvent({
-    @required this.eventType,
+    required this.eventType,
     this.duration,
     this.size,
     this.buffered,
@@ -228,17 +228,17 @@
   /// Duration of the video.
   ///
   /// Only used if [eventType] is [VideoEventType.initialized].
-  final Duration duration;
+  final Duration? duration;
 
   /// Size of the video.
   ///
   /// Only used if [eventType] is [VideoEventType.initialized].
-  final Size size;
+  final Size? size;
 
   /// Buffered parts of the video.
   ///
   /// Only used if [eventType] is [VideoEventType.bufferingUpdate].
-  final List<DurationRange> buffered;
+  final List<DurationRange>? buffered;
 
   @override
   bool operator ==(Object other) {
diff --git a/packages/video_player/video_player_platform_interface/pubspec.yaml b/packages/video_player/video_player_platform_interface/pubspec.yaml
index cc3cd79..7394a33 100644
--- a/packages/video_player/video_player_platform_interface/pubspec.yaml
+++ b/packages/video_player/video_player_platform_interface/pubspec.yaml
@@ -3,19 +3,19 @@
 homepage: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player_platform_interface
 # NOTE: We strongly prefer non-breaking changes, even at the expense of a
 # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes
-version: 2.2.0
+version: 3.0.0-nullsafety.1
 
 dependencies:
   flutter:
     sdk: flutter
-  meta: ^1.0.5
+  meta: ^1.3.0-nullsafety.3
 
 dev_dependencies:
   flutter_test:
     sdk: flutter
   mockito: ^4.1.1
-  pedantic: ^1.8.0
+  pedantic: ^1.10.0-nullsafety.1
 
 environment:
-  sdk: ">=2.8.0 <3.0.0"
+  sdk: ">=2.10.0-56.0.dev <3.0.0"
   flutter: ">=1.10.0 <2.0.0"
diff --git a/packages/video_player/video_player_platform_interface/test/method_channel_video_player_test.dart b/packages/video_player/video_player_platform_interface/test/method_channel_video_player_test.dart
index c479100..5c19ebc 100644
--- a/packages/video_player/video_player_platform_interface/test/method_channel_video_player_test.dart
+++ b/packages/video_player/video_player_platform_interface/test/method_channel_video_player_test.dart
@@ -2,6 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// TODO(egarciad): Remove once Mockito is migrated to null safety.
+// @dart = 2.9
+
 import 'dart:ui';
 
 import 'package:flutter/services.dart';
diff --git a/packages/video_player/video_player_web/CHANGELOG.md b/packages/video_player/video_player_web/CHANGELOG.md
index d185049..d035810 100644
--- a/packages/video_player/video_player_web/CHANGELOG.md
+++ b/packages/video_player/video_player_web/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 2.0.0-nullsafety
+
+* Migrate to null safety.
+
 ## 0.1.4
 
 * Added option to set the video playback speed on the video controller.
diff --git a/packages/video_player/video_player_web/analysis_options.yaml b/packages/video_player/video_player_web/analysis_options.yaml
index 443b165..7e5b7b3 100644
--- a/packages/video_player/video_player_web/analysis_options.yaml
+++ b/packages/video_player/video_player_web/analysis_options.yaml
@@ -6,5 +6,7 @@
 include: ../../../analysis_options.yaml
 
 analyzer:
+  enable-experiment:
+    - non-nullable
   errors:
     undefined_prefixed_name: ignore
diff --git a/packages/video_player/video_player_web/lib/video_player_web.dart b/packages/video_player/video_player_web/lib/video_player_web.dart
index 251da37..7c9c338 100644
--- a/packages/video_player/video_player_web/lib/video_player_web.dart
+++ b/packages/video_player/video_player_web/lib/video_player_web.dart
@@ -50,7 +50,7 @@
 
   @override
   Future<void> dispose(int textureId) async {
-    _videoPlayers[textureId].dispose();
+    _videoPlayers[textureId]!.dispose();
     _videoPlayers.remove(textureId);
     return null;
   }
@@ -66,16 +66,16 @@
     final int textureId = _textureCounter;
     _textureCounter++;
 
-    String uri;
+    late String uri;
     switch (dataSource.sourceType) {
       case DataSourceType.network:
         // Do NOT modify the incoming uri, it can be a Blob, and Safari doesn't
         // like blobs that have changed.
-        uri = dataSource.uri;
+        uri = dataSource.uri ?? '';
         break;
       case DataSourceType.asset:
-        String assetUrl = dataSource.asset;
-        if (dataSource.package != null && dataSource.package.isNotEmpty) {
+        String assetUrl = dataSource.asset!;
+        if (dataSource.package != null && dataSource.package!.isNotEmpty) {
           assetUrl = 'packages/${dataSource.package}/$assetUrl';
         }
         // 'webOnlyAssetManager' is only in the web version of dart:ui
@@ -101,45 +101,45 @@
 
   @override
   Future<void> setLooping(int textureId, bool looping) async {
-    return _videoPlayers[textureId].setLooping(looping);
+    return _videoPlayers[textureId]!.setLooping(looping);
   }
 
   @override
   Future<void> play(int textureId) async {
-    return _videoPlayers[textureId].play();
+    return _videoPlayers[textureId]!.play();
   }
 
   @override
   Future<void> pause(int textureId) async {
-    return _videoPlayers[textureId].pause();
+    return _videoPlayers[textureId]!.pause();
   }
 
   @override
   Future<void> setVolume(int textureId, double volume) async {
-    return _videoPlayers[textureId].setVolume(volume);
+    return _videoPlayers[textureId]!.setVolume(volume);
   }
 
   @override
   Future<void> setPlaybackSpeed(int textureId, double speed) async {
     assert(speed > 0);
 
-    return _videoPlayers[textureId].setPlaybackSpeed(speed);
+    return _videoPlayers[textureId]!.setPlaybackSpeed(speed);
   }
 
   @override
   Future<void> seekTo(int textureId, Duration position) async {
-    return _videoPlayers[textureId].seekTo(position);
+    return _videoPlayers[textureId]!.seekTo(position);
   }
 
   @override
   Future<Duration> getPosition(int textureId) async {
-    _videoPlayers[textureId].sendBufferingUpdate();
-    return _videoPlayers[textureId].getPosition();
+    _videoPlayers[textureId]!.sendBufferingUpdate();
+    return _videoPlayers[textureId]!.getPosition();
   }
 
   @override
   Stream<VideoEvent> videoEventsFor(int textureId) {
-    return _videoPlayers[textureId].eventController.stream;
+    return _videoPlayers[textureId]!.eventController.stream;
   }
 
   @override
@@ -149,14 +149,14 @@
 }
 
 class _VideoPlayer {
-  _VideoPlayer({this.uri, this.textureId});
+  _VideoPlayer({required this.uri, required this.textureId});
 
   final StreamController<VideoEvent> eventController =
       StreamController<VideoEvent>();
 
   final String uri;
   final int textureId;
-  VideoElement videoElement;
+  late VideoElement videoElement;
   bool isInitialized = false;
 
   void initialize() {
@@ -186,9 +186,9 @@
       // The Event itself (_) doesn't contain info about the actual error.
       // We need to look at the HTMLMediaElement.error.
       // See: https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/error
-      MediaError error = videoElement.error;
+      MediaError error = videoElement.error!;
       eventController.addError(PlatformException(
-        code: _kErrorValueToErrorName[error.code],
+        code: _kErrorValueToErrorName[error.code]!,
         message: error.message != '' ? error.message : _kDefaultErrorMessage,
         details: _kErrorValueToErrorDescription[error.code],
       ));
@@ -261,8 +261,8 @@
           milliseconds: (videoElement.duration * 1000).round(),
         ),
         size: Size(
-          videoElement.videoWidth.toDouble() ?? 0.0,
-          videoElement.videoHeight.toDouble() ?? 0.0,
+          videoElement.videoWidth.toDouble(),
+          videoElement.videoHeight.toDouble(),
         ),
       ),
     );
diff --git a/packages/video_player/video_player_web/pubspec.yaml b/packages/video_player/video_player_web/pubspec.yaml
index 98191bf..6538bbc 100644
--- a/packages/video_player/video_player_web/pubspec.yaml
+++ b/packages/video_player/video_player_web/pubspec.yaml
@@ -4,7 +4,7 @@
 # 0.1.y+z is compatible with 1.0.0, if you land a breaking change bump
 # the version to 2.0.0.
 # See more details: https://github.com/flutter/flutter/wiki/Package-migration-to-1.0.0
-version: 0.1.4
+version: 2.0.0-nullsafety
 
 flutter:
   plugin:
@@ -18,16 +18,16 @@
     sdk: flutter
   flutter_web_plugins:
     sdk: flutter
-  meta: ^1.1.7
-  video_player_platform_interface: ^2.2.0
+  meta: ^1.3.0-nullsafety.3
+  video_player_platform_interface: ^3.0.0-nullsafety.1
 
 dev_dependencies:
   flutter_test:
     sdk: flutter
   video_player:
     path: ../video_player
-  pedantic: ^1.8.0
+  pedantic: ^1.10.0-nullsafety.1
 
 environment:
-  sdk: ">=2.8.0 <3.0.0"
-  flutter: ">=1.12.8 <2.0.0"
+  sdk: '>=2.10.0-56.0.dev <3.0.0'
+  flutter: ">=1.12.13+hotfix.5 <2.0.0"
diff --git a/packages/video_player/video_player_web/test/video_player_web_test.dart b/packages/video_player/video_player_web/test/video_player_web_test.dart
index 453079b..c433d82 100644
--- a/packages/video_player/video_player_web/test/video_player_web_test.dart
+++ b/packages/video_player/video_player_web/test/video_player_web_test.dart
@@ -14,16 +14,16 @@
 
 void main() {
   group('VideoPlayer for Web', () {
-    int textureId;
+    late int textureId;
 
     setUp(() async {
       VideoPlayerPlatform.instance = VideoPlayerPlugin();
-      textureId = await VideoPlayerPlatform.instance.create(
+      textureId = (await VideoPlayerPlatform.instance.create(
         DataSource(
             sourceType: DataSourceType.network,
             uri:
                 'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4'),
-      );
+      ))!;
     });
 
     test('$VideoPlayerPlugin is the live instance', () {
@@ -84,12 +84,12 @@
     });
 
     test('throws PlatformException when playing bad media', () async {
-      int videoPlayerId = await VideoPlayerPlatform.instance.create(
+      int videoPlayerId = (await VideoPlayerPlatform.instance.create(
         DataSource(
             sourceType: DataSourceType.network,
             uri:
                 'https://flutter.github.io/assets-for-api-docs/assets/videos/_non_existent_video.mp4'),
-      );
+      ))!;
 
       Stream<VideoEvent> eventStream =
           VideoPlayerPlatform.instance.videoEventsFor(videoPlayerId);
diff --git a/script/incremental_build.sh b/script/incremental_build.sh
index 550413a..809229b 100755
--- a/script/incremental_build.sh
+++ b/script/incremental_build.sh
@@ -20,8 +20,19 @@
 #
 # TODO(mklim): Remove everything from this list. https://github.com/flutter/flutter/issues/45440
 CUSTOM_ANALYSIS_PLUGINS=(
+  "in_app_purchase"
+  "camera"
+  "plugin_platform_interface"
+  "video_player/video_player"
+  "video_player/video_player_platform_interface"
   "video_player/video_player_web"
   "google_maps_flutter/google_maps_flutter_web"
+  "url_launcher/url_launcher_platform_interface"
+  "url_launcher/url_launcher"
+  "device_info/device_info_platform_interface"
+  "device_info/device_info"
+  "connectivity/connectivity_platform_interface"
+  "connectivity/connectivity"
   "url_launcher/url_launcher_web"
 )
 # Comma-separated string of the list above