[path_provider] Update to stable NNBD (#3582)

Bumps the versions in the app-facing package to make it stable NNBD.

Changes the interface of four core methods to non-nullable, and adds a new exceptions if they aren't provided by the platform implementations. The list is somewhat arbitrary, but these seem like the four that are core enough that any implementation should either provide them, or explicitly say they don't have such a concept via UnsupportedError, since there isn't an obvious way for a developer to fall back if they are unexpectedly missing.
diff --git a/packages/path_provider/path_provider/CHANGELOG.md b/packages/path_provider/path_provider/CHANGELOG.md
index a52711b..c28c617 100644
--- a/packages/path_provider/path_provider/CHANGELOG.md
+++ b/packages/path_provider/path_provider/CHANGELOG.md
@@ -1,11 +1,10 @@
-## 2.0.0-nullsafety.1
-
-* Require latest path_provider_windows to avoid potential issues
-  with breaking changes in `ffi` and `win32`.
-
-## 2.0.0-nullsafety
+## 2.0.0
 
 * Migrate to null safety.
+* BREAKING CHANGE: Path accessors that return non-nullable results will throw
+  a `MissingPlatformDirectoryException` if the platform implementation is unable
+  to get the corresponding directory (except on platforms where the method is
+  explicitly unsupported, where they will continue to throw `UnsupportedError`).
 
 ## 1.6.28
 
diff --git a/packages/path_provider/path_provider/example/pubspec.yaml b/packages/path_provider/path_provider/example/pubspec.yaml
index cef0449..68c751a8 100644
--- a/packages/path_provider/path_provider/example/pubspec.yaml
+++ b/packages/path_provider/path_provider/example/pubspec.yaml
@@ -17,11 +17,11 @@
     path: ../../../integration_test
   flutter_driver:
     sdk: flutter
-  pedantic: ^1.8.0
+  pedantic: ^1.10.0
 
 flutter:
   uses-material-design: true
 
 environment:
-  sdk: ">=2.12.0-0 <3.0.0"
+  sdk: ">=2.12.0-259.9.beta <3.0.0"
   flutter: ">=1.12.13+hotfix.5"
diff --git a/packages/path_provider/path_provider/lib/path_provider.dart b/packages/path_provider/path_provider/lib/path_provider.dart
index 1560c33..da9c0b3 100644
--- a/packages/path_provider/path_provider/lib/path_provider.dart
+++ b/packages/path_provider/path_provider/lib/path_provider.dart
@@ -20,6 +20,27 @@
 
 bool _manualDartRegistrationNeeded = true;
 
+/// An exception thrown when a directory that should always be available on
+/// the current platform cannot be obtained.
+class MissingPlatformDirectoryException implements Exception {
+  /// Creates a new exception
+  MissingPlatformDirectoryException(this.message, {this.details});
+
+  /// The explanation of the exception.
+  final String message;
+
+  /// Added details, if any.
+  ///
+  /// E.g., an error object from the platform implementation.
+  final Object? details;
+
+  @override
+  String toString() {
+    String detailsAddition = details == null ? '' : ': $details';
+    return 'MissingPlatformDirectoryException($message)$detailsAddition';
+  }
+}
+
 PathProviderPlatform get _platform {
   // This is to manually endorse Dart implementations until automatic
   // registration of Dart plugins is implemented. For details see
@@ -51,10 +72,14 @@
 /// On iOS, this uses the `NSCachesDirectory` API.
 ///
 /// On Android, this uses the `getCacheDir` API on the context.
-Future<Directory?> getTemporaryDirectory() async {
+///
+/// Throws a `MissingPlatformDirectoryException` if the system is unable to
+/// provide the directory.
+Future<Directory> getTemporaryDirectory() async {
   final String? path = await _platform.getTemporaryPath();
   if (path == null) {
-    return null;
+    throw MissingPlatformDirectoryException(
+        'Unable to get temporary directory');
   }
   return Directory(path);
 }
@@ -69,10 +94,14 @@
 /// If this directory does not exist, it is created automatically.
 ///
 /// On Android, this function uses the `getFilesDir` API on the context.
-Future<Directory?> getApplicationSupportDirectory() async {
+///
+/// Throws a `MissingPlatformDirectoryException` if the system is unable to
+/// provide the directory.
+Future<Directory> getApplicationSupportDirectory() async {
   final String? path = await _platform.getApplicationSupportPath();
   if (path == null) {
-    return null;
+    throw MissingPlatformDirectoryException(
+        'Unable to get application support directory');
   }
 
   return Directory(path);
@@ -83,10 +112,13 @@
 ///
 /// On Android, this function throws an [UnsupportedError] as no equivalent
 /// path exists.
-Future<Directory?> getLibraryDirectory() async {
+///
+/// Throws a `MissingPlatformDirectoryException` if the system is unable to
+/// provide the directory on a supported platform.
+Future<Directory> getLibraryDirectory() async {
   final String? path = await _platform.getLibraryPath();
   if (path == null) {
-    return null;
+    throw MissingPlatformDirectoryException('Unable to get library directory');
   }
   return Directory(path);
 }
@@ -100,10 +132,14 @@
 /// On Android, this uses the `getDataDirectory` API on the context. Consider
 /// using [getExternalStorageDirectory] instead if data is intended to be visible
 /// to the user.
-Future<Directory?> getApplicationDocumentsDirectory() async {
+///
+/// Throws a `MissingPlatformDirectoryException` if the system is unable to
+/// provide the directory.
+Future<Directory> getApplicationDocumentsDirectory() async {
   final String? path = await _platform.getApplicationDocumentsPath();
   if (path == null) {
-    return null;
+    throw MissingPlatformDirectoryException(
+        'Unable to get application documents directory');
   }
   return Directory(path);
 }
diff --git a/packages/path_provider/path_provider/pubspec.yaml b/packages/path_provider/path_provider/pubspec.yaml
index 3d79c99..81941da 100644
--- a/packages/path_provider/path_provider/pubspec.yaml
+++ b/packages/path_provider/path_provider/pubspec.yaml
@@ -1,7 +1,7 @@
 name: path_provider
 description: Flutter plugin for getting commonly used locations on host platform file systems, such as the temp and app data directories.
 homepage: https://github.com/flutter/plugins/tree/master/packages/path_provider/path_provider
-version: 2.0.0-nullsafety.1
+version: 2.0.0
 
 flutter:
   plugin:
@@ -21,10 +21,10 @@
 dependencies:
   flutter:
     sdk: flutter
-  path_provider_platform_interface: ^2.0.0-nullsafety
-  path_provider_macos: ^0.0.5-nullsafety
-  path_provider_linux: ^0.2.0-nullsafety
-  path_provider_windows: ^0.1.0-nullsafety.3
+  path_provider_platform_interface: ^2.0.0
+  path_provider_macos: ^2.0.0
+  path_provider_linux: ^2.0.0
+  path_provider_windows: ^2.0.0
 
 dev_dependencies:
   integration_test:
@@ -33,10 +33,10 @@
     sdk: flutter
   flutter_driver:
     sdk: flutter
-  pedantic: ^1.10.0-nullsafety
-  mockito: ^5.0.0-nullsafety.0
-  plugin_platform_interface: ^1.1.0-nullsafety
+  pedantic: ^1.10.0
+  plugin_platform_interface: ">=1.0.0 <3.0.0"
+  test: ^1.16.0
 
 environment:
-  sdk: ">=2.12.0-0 <3.0.0"
+  sdk: ">=2.12.0-259.9.beta <3.0.0"
   flutter: ">=1.12.13+hotfix.5"
diff --git a/packages/path_provider/path_provider/test/path_provider_test.dart b/packages/path_provider/path_provider/test/path_provider_test.dart
index aec5e06..759e9c0 100644
--- a/packages/path_provider/path_provider/test/path_provider_test.dart
+++ b/packages/path_provider/path_provider/test/path_provider_test.dart
@@ -6,10 +6,10 @@
 import 'dart:async';
 
 import 'package:flutter_test/flutter_test.dart';
-import 'package:mockito/mockito.dart';
 import 'package:path_provider/path_provider.dart';
 import 'package:path_provider_platform_interface/path_provider_platform_interface.dart';
 import 'package:plugin_platform_interface/plugin_platform_interface.dart';
+import 'package:test/fake.dart';
 
 const String kTemporaryPath = 'temporaryPath';
 const String kApplicationSupportPath = 'applicationSupportPath';
@@ -20,31 +20,30 @@
 const String kExternalStoragePath = 'externalStoragePath';
 
 void main() {
-  group('PathProvider', () {
-    TestWidgetsFlutterBinding.ensureInitialized();
-
+  TestWidgetsFlutterBinding.ensureInitialized();
+  group('PathProvider full implementation', () {
     setUp(() async {
-      PathProviderPlatform.instance = MockPathProviderPlatform();
+      PathProviderPlatform.instance = FakePathProviderPlatform();
     });
 
     test('getTemporaryDirectory', () async {
-      Directory? result = await getTemporaryDirectory();
-      expect(result?.path, kTemporaryPath);
+      Directory result = await getTemporaryDirectory();
+      expect(result.path, kTemporaryPath);
     });
 
     test('getApplicationSupportDirectory', () async {
-      Directory? result = await getApplicationSupportDirectory();
-      expect(result?.path, kApplicationSupportPath);
+      Directory result = await getApplicationSupportDirectory();
+      expect(result.path, kApplicationSupportPath);
     });
 
     test('getLibraryDirectory', () async {
-      Directory? result = await getLibraryDirectory();
-      expect(result?.path, kLibraryPath);
+      Directory result = await getLibraryDirectory();
+      expect(result.path, kLibraryPath);
     });
 
     test('getApplicationDocumentsDirectory', () async {
-      Directory? result = await getApplicationDocumentsDirectory();
-      expect(result?.path, kApplicationDocumentsPath);
+      Directory result = await getApplicationDocumentsDirectory();
+      expect(result.path, kApplicationDocumentsPath);
     });
 
     test('getExternalStorageDirectory', () async {
@@ -69,42 +68,126 @@
       expect(result?.path, kDownloadsPath);
     });
   });
+
+  group('PathProvider null implementation', () {
+    setUp(() async {
+      PathProviderPlatform.instance = AllNullFakePathProviderPlatform();
+    });
+
+    test('getTemporaryDirectory throws on null', () async {
+      expect(getTemporaryDirectory(),
+          throwsA(isA<MissingPlatformDirectoryException>()));
+    });
+
+    test('getApplicationSupportDirectory throws on null', () async {
+      expect(getApplicationSupportDirectory(),
+          throwsA(isA<MissingPlatformDirectoryException>()));
+    });
+
+    test('getLibraryDirectory throws on null', () async {
+      expect(getLibraryDirectory(),
+          throwsA(isA<MissingPlatformDirectoryException>()));
+    });
+
+    test('getApplicationDocumentsDirectory throws on null', () async {
+      expect(getApplicationDocumentsDirectory(),
+          throwsA(isA<MissingPlatformDirectoryException>()));
+    });
+
+    test('getExternalStorageDirectory passes null through', () async {
+      Directory? result = await getExternalStorageDirectory();
+      expect(result, isNull);
+    });
+
+    test('getExternalCacheDirectories passes null through', () async {
+      List<Directory>? result = await getExternalCacheDirectories();
+      expect(result, isNull);
+    });
+
+    test('getExternalStorageDirectories passes null through', () async {
+      List<Directory>? result = await getExternalStorageDirectories();
+      expect(result, isNull);
+    });
+
+    test('getDownloadsDirectory passses null through', () async {
+      Directory? result = await getDownloadsDirectory();
+      expect(result, isNull);
+    });
+  });
 }
 
-class MockPathProviderPlatform extends Mock
+class FakePathProviderPlatform extends Fake
     with MockPlatformInterfaceMixin
     implements PathProviderPlatform {
-  Future<String> getTemporaryPath() async {
+  Future<String?> getTemporaryPath() async {
     return kTemporaryPath;
   }
 
-  Future<String> getApplicationSupportPath() async {
+  Future<String?> getApplicationSupportPath() async {
     return kApplicationSupportPath;
   }
 
-  Future<String> getLibraryPath() async {
+  Future<String?> getLibraryPath() async {
     return kLibraryPath;
   }
 
-  Future<String> getApplicationDocumentsPath() async {
+  Future<String?> getApplicationDocumentsPath() async {
     return kApplicationDocumentsPath;
   }
 
-  Future<String> getExternalStoragePath() async {
+  Future<String?> getExternalStoragePath() async {
     return kExternalStoragePath;
   }
 
-  Future<List<String>> getExternalCachePaths() async {
+  Future<List<String>?> getExternalCachePaths() async {
     return <String>[kExternalCachePath];
   }
 
-  Future<List<String>> getExternalStoragePaths({
+  Future<List<String>?> getExternalStoragePaths({
     StorageDirectory? type,
   }) async {
     return <String>[kExternalStoragePath];
   }
 
-  Future<String> getDownloadsPath() async {
+  Future<String?> getDownloadsPath() async {
     return kDownloadsPath;
   }
 }
+
+class AllNullFakePathProviderPlatform extends Fake
+    with MockPlatformInterfaceMixin
+    implements PathProviderPlatform {
+  Future<String?> getTemporaryPath() async {
+    return null;
+  }
+
+  Future<String?> getApplicationSupportPath() async {
+    return null;
+  }
+
+  Future<String?> getLibraryPath() async {
+    return null;
+  }
+
+  Future<String?> getApplicationDocumentsPath() async {
+    return null;
+  }
+
+  Future<String?> getExternalStoragePath() async {
+    return null;
+  }
+
+  Future<List<String>?> getExternalCachePaths() async {
+    return null;
+  }
+
+  Future<List<String>?> getExternalStoragePaths({
+    StorageDirectory? type,
+  }) async {
+    return null;
+  }
+
+  Future<String?> getDownloadsPath() async {
+    return null;
+  }
+}