[url_launcher] Update platforms to NNBD stable (#3584)

Updates all versions to stable.

Converts all desktop examples to null-safety, and migrates Linux and
macOS to use platform interface for examples rather than app-facing
package to eliminate circular dependencies (implementation copied
directly from Windows).
diff --git a/packages/url_launcher/url_launcher_linux/CHANGELOG.md b/packages/url_launcher/url_launcher_linux/CHANGELOG.md
index bd3c15c..ec9fad5 100644
--- a/packages/url_launcher/url_launcher_linux/CHANGELOG.md
+++ b/packages/url_launcher/url_launcher_linux/CHANGELOG.md
@@ -1,18 +1,9 @@
-## 2.0.0-nullsafety
-
-* Update version for consistency with other implementations.
-
-## 0.1.0-nullsafety.3
-
-* Update the example app: remove the deprecated `RaisedButton` and `FlatButton` widgets.
-
-## 0.1.0-nullsafety.2
-
-* Fix outdated links across a number of markdown files ([#3276](https://github.com/flutter/plugins/pull/3276))
-
-## 0.1.0-nullsafety.1
+## 2.0.0
 
 * Migrate to null safety.
+* Update the example app: remove the deprecated `RaisedButton` and `FlatButton` widgets.
+* Fix outdated links across a number of markdown files ([#3276](https://github.com/flutter/plugins/pull/3276))
+* Set `implementation` in pubspec.yaml
 
 ## 0.0.2+1
 
diff --git a/packages/url_launcher/url_launcher_linux/example/integration_test/url_launcher_test.dart b/packages/url_launcher/url_launcher_linux/example/integration_test/url_launcher_test.dart
index d11ddb4..e1008fd 100644
--- a/packages/url_launcher/url_launcher_linux/example/integration_test/url_launcher_test.dart
+++ b/packages/url_launcher/url_launcher_linux/example/integration_test/url_launcher_test.dart
@@ -2,22 +2,21 @@
 // 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.
 
+// @dart = 2.9
+
 import 'package:flutter_test/flutter_test.dart';
 import 'package:integration_test/integration_test.dart';
-import 'package:url_launcher/url_launcher.dart';
+import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart';
 
 void main() {
   IntegrationTestWidgetsFlutterBinding.ensureInitialized();
 
   testWidgets('canLaunch', (WidgetTester _) async {
-    expect(await canLaunch('randomstring'), false);
+    UrlLauncherPlatform launcher = UrlLauncherPlatform.instance;
+
+    expect(await launcher.canLaunch('randomstring'), false);
 
     // Generally all devices should have some default browser.
-    expect(await canLaunch('http://flutter.dev'), true);
-
-    // Desktop will not necessarily support sms:.
-
-    // tel: and mailto: links may not be openable on every device. iOS
-    // simulators notably can't open these link types.
+    expect(await launcher.canLaunch('http://flutter.dev'), true);
   });
 }
diff --git a/packages/url_launcher/url_launcher_linux/example/lib/main.dart b/packages/url_launcher/url_launcher_linux/example/lib/main.dart
index a458620..f49e9fa 100644
--- a/packages/url_launcher/url_launcher_linux/example/lib/main.dart
+++ b/packages/url_launcher/url_launcher_linux/example/lib/main.dart
@@ -6,7 +6,7 @@
 
 import 'dart:async';
 import 'package:flutter/material.dart';
-import 'package:url_launcher/url_launcher.dart';
+import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart';
 
 void main() {
   runApp(MyApp());
@@ -26,7 +26,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
@@ -34,77 +34,24 @@
 }
 
 class _MyHomePageState extends State<MyHomePage> {
-  Future<void> _launched;
-  String _phone = '';
+  Future<void>? _launched;
 
   Future<void> _launchInBrowser(String url) async {
-    if (await canLaunch(url)) {
-      await launch(
+    if (await UrlLauncherPlatform.instance.canLaunch(url)) {
+      await UrlLauncherPlatform.instance.launch(
         url,
-        forceSafariVC: false,
-        forceWebView: false,
-        headers: <String, String>{'my_header_key': 'my_header_value'},
+        useSafariVC: false,
+        useWebView: false,
+        enableJavaScript: false,
+        enableDomStorage: false,
+        universalLinksOnly: false,
+        headers: <String, String>{},
       );
     } else {
       throw 'Could not launch $url';
     }
   }
 
-  Future<void> _launchInWebViewOrVC(String url) async {
-    if (await canLaunch(url)) {
-      await launch(
-        url,
-        forceSafariVC: true,
-        forceWebView: true,
-        headers: <String, String>{'my_header_key': 'my_header_value'},
-      );
-    } else {
-      throw 'Could not launch $url';
-    }
-  }
-
-  Future<void> _launchInWebViewWithJavaScript(String url) async {
-    if (await canLaunch(url)) {
-      await launch(
-        url,
-        forceSafariVC: true,
-        forceWebView: true,
-        enableJavaScript: true,
-      );
-    } else {
-      throw 'Could not launch $url';
-    }
-  }
-
-  Future<void> _launchInWebViewWithDomStorage(String url) async {
-    if (await canLaunch(url)) {
-      await launch(
-        url,
-        forceSafariVC: true,
-        forceWebView: true,
-        enableDomStorage: true,
-      );
-    } else {
-      throw 'Could not launch $url';
-    }
-  }
-
-  Future<void> _launchUniversalLinkIos(String url) async {
-    if (await canLaunch(url)) {
-      final bool nativeAppLaunchSucceeded = await launch(
-        url,
-        forceSafariVC: false,
-        universalLinksOnly: true,
-      );
-      if (!nativeAppLaunchSucceeded) {
-        await launch(
-          url,
-          forceSafariVC: true,
-        );
-      }
-    }
-  }
-
   Widget _launchStatus(BuildContext context, AsyncSnapshot<void> snapshot) {
     if (snapshot.hasError) {
       return Text('Error: ${snapshot.error}');
@@ -113,14 +60,6 @@
     }
   }
 
-  Future<void> _makePhoneCall(String url) async {
-    if (await canLaunch(url)) {
-      await launch(url);
-    } else {
-      throw 'Could not launch $url';
-    }
-  }
-
   @override
   Widget build(BuildContext context) {
     const String toLaunch = 'https://www.cylog.org/headers/';
@@ -133,19 +72,6 @@
           Column(
             mainAxisAlignment: MainAxisAlignment.center,
             children: <Widget>[
-              Padding(
-                padding: const EdgeInsets.all(16.0),
-                child: TextField(
-                    onChanged: (String text) => _phone = text,
-                    decoration: const InputDecoration(
-                        hintText: 'Input the phone number to launch')),
-              ),
-              ElevatedButton(
-                onPressed: () => setState(() {
-                  _launched = _makePhoneCall('tel:$_phone');
-                }),
-                child: const Text('Make phone call'),
-              ),
               const Padding(
                 padding: EdgeInsets.all(16.0),
                 child: Text(toLaunch),
@@ -157,33 +83,6 @@
                 child: const Text('Launch in browser'),
               ),
               const Padding(padding: EdgeInsets.all(16.0)),
-              ElevatedButton(
-                onPressed: () => setState(() {
-                  _launched = _launchInWebViewOrVC(toLaunch);
-                }),
-                child: const Text('Launch in app'),
-              ),
-              ElevatedButton(
-                onPressed: () => setState(() {
-                  _launched = _launchInWebViewWithJavaScript(toLaunch);
-                }),
-                child: const Text('Launch in app(JavaScript ON)'),
-              ),
-              ElevatedButton(
-                onPressed: () => setState(() {
-                  _launched = _launchInWebViewWithDomStorage(toLaunch);
-                }),
-                child: const Text('Launch in app(DOM storage ON)'),
-              ),
-              const Padding(padding: EdgeInsets.all(16.0)),
-              ElevatedButton(
-                onPressed: () => setState(() {
-                  _launched = _launchUniversalLinkIos(toLaunch);
-                }),
-                child: const Text(
-                    'Launch a universal link in a native app, fallback to Safari.(Youtube)'),
-              ),
-              const Padding(padding: EdgeInsets.all(16.0)),
               FutureBuilder<void>(future: _launched, builder: _launchStatus),
             ],
           ),
diff --git a/packages/url_launcher/url_launcher_linux/example/pubspec.yaml b/packages/url_launcher/url_launcher_linux/example/pubspec.yaml
index e95bcd0..63c920f 100644
--- a/packages/url_launcher/url_launcher_linux/example/pubspec.yaml
+++ b/packages/url_launcher/url_launcher_linux/example/pubspec.yaml
@@ -4,7 +4,6 @@
 dependencies:
   flutter:
     sdk: flutter
-  url_launcher: any
   url_launcher_linux:
     # When depending on this package from a real application you should use:
     #   url_launcher_linux: ^x.y.z
@@ -12,17 +11,18 @@
     # The example app is bundled with the plugin so we use a path dependency on
     # the parent directory to use the current plugin's version.
     path: ../
+  url_launcher_platform_interface: ^2.0.0
 
 dev_dependencies:
   integration_test:
     path: ../../../integration_test
   flutter_driver:
     sdk: flutter
-  pedantic: ^1.8.0
+  pedantic: ^1.10.0
 
 flutter:
   uses-material-design: true
 
 environment:
-  sdk: ">=2.1.0 <3.0.0"
-  flutter: ">=1.12.8"
+  sdk: ">=2.12.0-259.9.beta <3.0.0"
+  flutter: ">=1.20.0"
diff --git a/packages/url_launcher/url_launcher_linux/example/test_driver/integration_test.dart b/packages/url_launcher/url_launcher_linux/example/test_driver/integration_test.dart
index 7a2c213..a8a56aa 100644
--- a/packages/url_launcher/url_launcher_linux/example/test_driver/integration_test.dart
+++ b/packages/url_launcher/url_launcher_linux/example/test_driver/integration_test.dart
@@ -2,6 +2,8 @@
 // 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.
 
+// @dart = 2.9
+
 import 'dart:async';
 import 'dart:convert';
 import 'dart:io';
diff --git a/packages/url_launcher/url_launcher_linux/pubspec.yaml b/packages/url_launcher/url_launcher_linux/pubspec.yaml
index 37a074a..cc97409 100644
--- a/packages/url_launcher/url_launcher_linux/pubspec.yaml
+++ b/packages/url_launcher/url_launcher_linux/pubspec.yaml
@@ -1,17 +1,18 @@
 name: url_launcher_linux
 description: Linux implementation of the url_launcher plugin.
-version: 2.0.0-nullsafety
+version: 2.0.0
 homepage: https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher_linux
 
 flutter:
   plugin:
+    implements: url_launcher
     platforms:
       linux:
         pluginClass: UrlLauncherPlugin
 
 environment:
-  sdk: ">=2.12.0-0 <3.0.0"
-  flutter: ">=1.12.8"
+  sdk: ">=2.12.0-259.9.beta <3.0.0"
+  flutter: ">=1.20.0"
 
 dependencies:
   flutter:
diff --git a/packages/url_launcher/url_launcher_macos/CHANGELOG.md b/packages/url_launcher/url_launcher_macos/CHANGELOG.md
index 5835c15..6b0820f 100644
--- a/packages/url_launcher/url_launcher_macos/CHANGELOG.md
+++ b/packages/url_launcher/url_launcher_macos/CHANGELOG.md
@@ -1,18 +1,8 @@
-## 2.0.0-nullsafety
-
-* Update version to (semi-belatedly) meet 1.0-consistency promise.
-
-# 0.1.0-nullsafety.2
-
-* Update the example app: remove the deprecated `RaisedButton` and `FlatButton` widgets.
-
-# 0.1.0-nullsafety.1
-
-* Bump SDK to support null safety.
-
-# 0.1.0-nullsafety
+## 2.0.0
 
 * Migrate to null safety.
+* Update the example app: remove the deprecated `RaisedButton` and `FlatButton` widgets.
+* Set `implementation` in pubspec.yaml
 
 ## 0.0.2+1
 
diff --git a/packages/url_launcher/url_launcher_macos/example/integration_test/url_launcher_test.dart b/packages/url_launcher/url_launcher_macos/example/integration_test/url_launcher_test.dart
index 3e8d34c..d0c1a8b 100644
--- a/packages/url_launcher/url_launcher_macos/example/integration_test/url_launcher_test.dart
+++ b/packages/url_launcher/url_launcher_macos/example/integration_test/url_launcher_test.dart
@@ -2,23 +2,24 @@
 // 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.
 
+// @dart = 2.9
+
 import 'package:flutter_test/flutter_test.dart';
 import 'package:integration_test/integration_test.dart';
-import 'package:url_launcher/url_launcher.dart';
+import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart';
 
 void main() {
   IntegrationTestWidgetsFlutterBinding.ensureInitialized();
 
   testWidgets('canLaunch', (WidgetTester _) async {
-    expect(await canLaunch('randomstring'), false);
+    UrlLauncherPlatform launcher = UrlLauncherPlatform.instance;
+
+    expect(await launcher.canLaunch('randomstring'), false);
 
     // Generally all devices should have some default browser.
-    expect(await canLaunch('http://flutter.dev'), true);
+    expect(await launcher.canLaunch('http://flutter.dev'), true);
 
     // Generally all devices should have some default SMS app.
-    expect(await canLaunch('sms:5555555555'), true);
-
-    // tel: and mailto: links may not be openable on every device. iOS
-    // simulators notably can't open these link types.
+    expect(await launcher.canLaunch('sms:5555555555'), true);
   });
 }
diff --git a/packages/url_launcher/url_launcher_macos/example/lib/main.dart b/packages/url_launcher/url_launcher_macos/example/lib/main.dart
index a458620..f49e9fa 100644
--- a/packages/url_launcher/url_launcher_macos/example/lib/main.dart
+++ b/packages/url_launcher/url_launcher_macos/example/lib/main.dart
@@ -6,7 +6,7 @@
 
 import 'dart:async';
 import 'package:flutter/material.dart';
-import 'package:url_launcher/url_launcher.dart';
+import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart';
 
 void main() {
   runApp(MyApp());
@@ -26,7 +26,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
@@ -34,77 +34,24 @@
 }
 
 class _MyHomePageState extends State<MyHomePage> {
-  Future<void> _launched;
-  String _phone = '';
+  Future<void>? _launched;
 
   Future<void> _launchInBrowser(String url) async {
-    if (await canLaunch(url)) {
-      await launch(
+    if (await UrlLauncherPlatform.instance.canLaunch(url)) {
+      await UrlLauncherPlatform.instance.launch(
         url,
-        forceSafariVC: false,
-        forceWebView: false,
-        headers: <String, String>{'my_header_key': 'my_header_value'},
+        useSafariVC: false,
+        useWebView: false,
+        enableJavaScript: false,
+        enableDomStorage: false,
+        universalLinksOnly: false,
+        headers: <String, String>{},
       );
     } else {
       throw 'Could not launch $url';
     }
   }
 
-  Future<void> _launchInWebViewOrVC(String url) async {
-    if (await canLaunch(url)) {
-      await launch(
-        url,
-        forceSafariVC: true,
-        forceWebView: true,
-        headers: <String, String>{'my_header_key': 'my_header_value'},
-      );
-    } else {
-      throw 'Could not launch $url';
-    }
-  }
-
-  Future<void> _launchInWebViewWithJavaScript(String url) async {
-    if (await canLaunch(url)) {
-      await launch(
-        url,
-        forceSafariVC: true,
-        forceWebView: true,
-        enableJavaScript: true,
-      );
-    } else {
-      throw 'Could not launch $url';
-    }
-  }
-
-  Future<void> _launchInWebViewWithDomStorage(String url) async {
-    if (await canLaunch(url)) {
-      await launch(
-        url,
-        forceSafariVC: true,
-        forceWebView: true,
-        enableDomStorage: true,
-      );
-    } else {
-      throw 'Could not launch $url';
-    }
-  }
-
-  Future<void> _launchUniversalLinkIos(String url) async {
-    if (await canLaunch(url)) {
-      final bool nativeAppLaunchSucceeded = await launch(
-        url,
-        forceSafariVC: false,
-        universalLinksOnly: true,
-      );
-      if (!nativeAppLaunchSucceeded) {
-        await launch(
-          url,
-          forceSafariVC: true,
-        );
-      }
-    }
-  }
-
   Widget _launchStatus(BuildContext context, AsyncSnapshot<void> snapshot) {
     if (snapshot.hasError) {
       return Text('Error: ${snapshot.error}');
@@ -113,14 +60,6 @@
     }
   }
 
-  Future<void> _makePhoneCall(String url) async {
-    if (await canLaunch(url)) {
-      await launch(url);
-    } else {
-      throw 'Could not launch $url';
-    }
-  }
-
   @override
   Widget build(BuildContext context) {
     const String toLaunch = 'https://www.cylog.org/headers/';
@@ -133,19 +72,6 @@
           Column(
             mainAxisAlignment: MainAxisAlignment.center,
             children: <Widget>[
-              Padding(
-                padding: const EdgeInsets.all(16.0),
-                child: TextField(
-                    onChanged: (String text) => _phone = text,
-                    decoration: const InputDecoration(
-                        hintText: 'Input the phone number to launch')),
-              ),
-              ElevatedButton(
-                onPressed: () => setState(() {
-                  _launched = _makePhoneCall('tel:$_phone');
-                }),
-                child: const Text('Make phone call'),
-              ),
               const Padding(
                 padding: EdgeInsets.all(16.0),
                 child: Text(toLaunch),
@@ -157,33 +83,6 @@
                 child: const Text('Launch in browser'),
               ),
               const Padding(padding: EdgeInsets.all(16.0)),
-              ElevatedButton(
-                onPressed: () => setState(() {
-                  _launched = _launchInWebViewOrVC(toLaunch);
-                }),
-                child: const Text('Launch in app'),
-              ),
-              ElevatedButton(
-                onPressed: () => setState(() {
-                  _launched = _launchInWebViewWithJavaScript(toLaunch);
-                }),
-                child: const Text('Launch in app(JavaScript ON)'),
-              ),
-              ElevatedButton(
-                onPressed: () => setState(() {
-                  _launched = _launchInWebViewWithDomStorage(toLaunch);
-                }),
-                child: const Text('Launch in app(DOM storage ON)'),
-              ),
-              const Padding(padding: EdgeInsets.all(16.0)),
-              ElevatedButton(
-                onPressed: () => setState(() {
-                  _launched = _launchUniversalLinkIos(toLaunch);
-                }),
-                child: const Text(
-                    'Launch a universal link in a native app, fallback to Safari.(Youtube)'),
-              ),
-              const Padding(padding: EdgeInsets.all(16.0)),
               FutureBuilder<void>(future: _launched, builder: _launchStatus),
             ],
           ),
diff --git a/packages/url_launcher/url_launcher_macos/example/pubspec.yaml b/packages/url_launcher/url_launcher_macos/example/pubspec.yaml
index 2e66616..40bb4ea 100644
--- a/packages/url_launcher/url_launcher_macos/example/pubspec.yaml
+++ b/packages/url_launcher/url_launcher_macos/example/pubspec.yaml
@@ -4,7 +4,6 @@
 dependencies:
   flutter:
     sdk: flutter
-  url_launcher: any
   url_launcher_macos:
     # When depending on this package from a real application you should use:
     #   url_launcher_macos: ^x.y.z
@@ -12,17 +11,18 @@
     # The example app is bundled with the plugin so we use a path dependency on
     # the parent directory to use the current plugin's version.
     path: ../
+  url_launcher_platform_interface: ^2.0.0
 
 dev_dependencies:
   integration_test:
     path: ../../../integration_test
   flutter_driver:
     sdk: flutter
-  pedantic: ^1.8.0
+  pedantic: ^1.10.0
 
 flutter:
   uses-material-design: true
 
 environment:
-  sdk: ">=2.1.0 <3.0.0"
-  flutter: ">=1.12.8"
+  sdk: ">=2.12.0-259.9.beta <3.0.0"
+  flutter: ">=1.20.0"
diff --git a/packages/url_launcher/url_launcher_macos/example/test_driver/integration_test.dart b/packages/url_launcher/url_launcher_macos/example/test_driver/integration_test.dart
index 7a2c213..a8a56aa 100644
--- a/packages/url_launcher/url_launcher_macos/example/test_driver/integration_test.dart
+++ b/packages/url_launcher/url_launcher_macos/example/test_driver/integration_test.dart
@@ -2,6 +2,8 @@
 // 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.
 
+// @dart = 2.9
+
 import 'dart:async';
 import 'dart:convert';
 import 'dart:io';
diff --git a/packages/url_launcher/url_launcher_macos/pubspec.yaml b/packages/url_launcher/url_launcher_macos/pubspec.yaml
index bd918bf..6b5e6cf 100644
--- a/packages/url_launcher/url_launcher_macos/pubspec.yaml
+++ b/packages/url_launcher/url_launcher_macos/pubspec.yaml
@@ -1,18 +1,19 @@
 name: url_launcher_macos
 description: macOS implementation of the url_launcher plugin.
-version: 2.0.0-nullsafety
+version: 2.0.0
 homepage: https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher_macos
 
 flutter:
   plugin:
+    implements: url_launcher
     platforms:
       macos:
         pluginClass: UrlLauncherPlugin
         fileName: url_launcher_macos.dart
 
 environment:
-  sdk: ">=2.12.0-0 <3.0.0"
-  flutter: ">=1.12.8"
+  sdk: ">=2.12.0-259.9.beta <3.0.0"
+  flutter: ">=1.20.0"
 
 dependencies:
   flutter:
diff --git a/packages/url_launcher/url_launcher_web/CHANGELOG.md b/packages/url_launcher/url_launcher_web/CHANGELOG.md
index 49d7245..7da0ba0 100644
--- a/packages/url_launcher/url_launcher_web/CHANGELOG.md
+++ b/packages/url_launcher/url_launcher_web/CHANGELOG.md
@@ -1,4 +1,4 @@
-# 2.0.0-nullsafety
+# 2.0.0
 
 - Migrate to null safety.
 
diff --git a/packages/url_launcher/url_launcher_web/example/pubspec.yaml b/packages/url_launcher/url_launcher_web/example/pubspec.yaml
index 5fc060f..5174861 100644
--- a/packages/url_launcher/url_launcher_web/example/pubspec.yaml
+++ b/packages/url_launcher/url_launcher_web/example/pubspec.yaml
@@ -18,5 +18,5 @@
     sdk: flutter
 
 environment:
-  sdk: ">=2.12.0-0 <3.0.0"
+  sdk: ">=2.12.0-259.9.beta <3.0.0"
   flutter: ">=1.26.0-0" # For integration_test from sdk
diff --git a/packages/url_launcher/url_launcher_web/pubspec.yaml b/packages/url_launcher/url_launcher_web/pubspec.yaml
index b9f957a..371a40e 100644
--- a/packages/url_launcher/url_launcher_web/pubspec.yaml
+++ b/packages/url_launcher/url_launcher_web/pubspec.yaml
@@ -1,7 +1,7 @@
 name: url_launcher_web
 description: Web platform implementation of url_launcher
 homepage: https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher_web
-version: 2.0.0-nullsafety
+version: 2.0.0
 
 flutter:
   plugin:
@@ -11,7 +11,7 @@
         fileName: url_launcher_web.dart
 
 dependencies:
-  url_launcher_platform_interface: ^2.0.0-nullsafety
+  url_launcher_platform_interface: ^2.0.0
   meta: ^1.3.0 # null safe
   flutter:
     sdk: flutter
@@ -24,5 +24,5 @@
     sdk: flutter
 
 environment:
-  sdk: ">=2.12.0-0 <3.0.0"
-  flutter: ">=1.12.13+hotfix.5"
+  sdk: ">=2.12.0-259.9.beta <3.0.0"
+  flutter: ">=1.20.0"
diff --git a/packages/url_launcher/url_launcher_windows/CHANGELOG.md b/packages/url_launcher/url_launcher_windows/CHANGELOG.md
index b577855..e906254 100644
--- a/packages/url_launcher/url_launcher_windows/CHANGELOG.md
+++ b/packages/url_launcher/url_launcher_windows/CHANGELOG.md
@@ -1,18 +1,8 @@
-## 2.0.0-nullsafety
-
-* Update version to (semi-belatedly) meet 1.0-consistency promise.
-
-## 0.1.0-nullsafety.2
-
-* Update the example app: remove the deprecated `RaisedButton` and `FlatButton` widgets.
-
-## 0.1.0-nullsafety.1
-
-* Bump Dart SDK to support null safety.
-
-## 0.1.0-nullsafety
+## 2.0.0
 
 * Migrate to null-safety.
+* Update the example app: remove the deprecated `RaisedButton` and `FlatButton` widgets.
+* Set `implementation` in pubspec.yaml
 
 ## 0.0.2+1
 
diff --git a/packages/url_launcher/url_launcher_windows/example/integration_test/url_launcher_test.dart b/packages/url_launcher/url_launcher_windows/example/integration_test/url_launcher_test.dart
index 2617150..e1008fd 100644
--- a/packages/url_launcher/url_launcher_windows/example/integration_test/url_launcher_test.dart
+++ b/packages/url_launcher/url_launcher_windows/example/integration_test/url_launcher_test.dart
@@ -2,6 +2,8 @@
 // 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.
 
+// @dart = 2.9
+
 import 'package:flutter_test/flutter_test.dart';
 import 'package:integration_test/integration_test.dart';
 import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart';
diff --git a/packages/url_launcher/url_launcher_windows/example/lib/main.dart b/packages/url_launcher/url_launcher_windows/example/lib/main.dart
index e6c9f47..f49e9fa 100644
--- a/packages/url_launcher/url_launcher_windows/example/lib/main.dart
+++ b/packages/url_launcher/url_launcher_windows/example/lib/main.dart
@@ -26,7 +26,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
@@ -34,7 +34,7 @@
 }
 
 class _MyHomePageState extends State<MyHomePage> {
-  Future<void> _launched;
+  Future<void>? _launched;
 
   Future<void> _launchInBrowser(String url) async {
     if (await UrlLauncherPlatform.instance.canLaunch(url)) {
diff --git a/packages/url_launcher/url_launcher_windows/example/pubspec.yaml b/packages/url_launcher/url_launcher_windows/example/pubspec.yaml
index 2de2bcf..8a273ba 100644
--- a/packages/url_launcher/url_launcher_windows/example/pubspec.yaml
+++ b/packages/url_launcher/url_launcher_windows/example/pubspec.yaml
@@ -4,7 +4,7 @@
 dependencies:
   flutter:
     sdk: flutter
-  url_launcher_platform_interface: any
+  url_launcher_platform_interface: ^2.0.0
   url_launcher_windows:
     # When depending on this package from a real application you should use:
     #   url_launcher_windows: ^x.y.z
@@ -18,11 +18,11 @@
     path: ../../../integration_test
   flutter_driver:
     sdk: flutter
-  pedantic: ^1.8.0
+  pedantic: ^1.10.0
 
 flutter:
   uses-material-design: true
 
 environment:
-  sdk: ">=2.1.0 <3.0.0"
-  flutter: ">=1.12.8"
+  sdk: ">=2.12.0-259.9.beta <3.0.0"
+  flutter: ">=1.20.0"
diff --git a/packages/url_launcher/url_launcher_windows/example/test_driver/integration_test.dart b/packages/url_launcher/url_launcher_windows/example/test_driver/integration_test.dart
index 7a2c213..a8a56aa 100644
--- a/packages/url_launcher/url_launcher_windows/example/test_driver/integration_test.dart
+++ b/packages/url_launcher/url_launcher_windows/example/test_driver/integration_test.dart
@@ -2,6 +2,8 @@
 // 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.
 
+// @dart = 2.9
+
 import 'dart:async';
 import 'dart:convert';
 import 'dart:io';
diff --git a/packages/url_launcher/url_launcher_windows/pubspec.yaml b/packages/url_launcher/url_launcher_windows/pubspec.yaml
index 368c3f8..e5b611f 100644
--- a/packages/url_launcher/url_launcher_windows/pubspec.yaml
+++ b/packages/url_launcher/url_launcher_windows/pubspec.yaml
@@ -1,17 +1,18 @@
 name: url_launcher_windows
 description: Windows implementation of the url_launcher plugin.
-version: 2.0.0-nullsafety
+version: 2.0.0
 homepage: https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher_windows
 
 flutter:
   plugin:
+    implements: url_launcher
     platforms:
       windows:
         pluginClass: UrlLauncherPlugin
 
 environment:
-  sdk: ">=2.12.0-0 <3.0.0"
-  flutter: ">=1.12.8"
+  sdk: ">=2.12.0-259.9.beta <3.0.0"
+  flutter: ">=1.20.0"
 
 dependencies:
   flutter: