[plugin_platform_interface] Add a new method `verify` that prevents use of `const Object()` as token. (#4640)

- Introduce `verify`, a more future-proof name. It throws `AssertionError` if `const Object()` used as the instance's token. 
- Soft-deprecate `verifyToken` with a comment. It will actually deprecated in a future release, to avoid breaking tests with this minor change.
- Update documentation for `PlatformInterface` to show new usage of `verify` and other cosmetic fixes.
- Add a test for the new assertion.

Fixes https://github.com/flutter/flutter/issues/96178.
diff --git a/packages/plugin_platform_interface/CHANGELOG.md b/packages/plugin_platform_interface/CHANGELOG.md
index af79d11..950da03 100644
--- a/packages/plugin_platform_interface/CHANGELOG.md
+++ b/packages/plugin_platform_interface/CHANGELOG.md
@@ -1,3 +1,8 @@
+## 2.1.0
+
+* Introduce `verify`, which prevents use of `const Object()` as instance token.
+* Add a comment indicating that `verifyToken` will be deprecated in a future release.
+
 ## 2.0.2
 
 * Update package description.
diff --git a/packages/plugin_platform_interface/lib/plugin_platform_interface.dart b/packages/plugin_platform_interface/lib/plugin_platform_interface.dart
index d9bd881..f12fd3c 100644
--- a/packages/plugin_platform_interface/lib/plugin_platform_interface.dart
+++ b/packages/plugin_platform_interface/lib/plugin_platform_interface.dart
@@ -12,7 +12,7 @@
 /// implemented using `extends` instead of `implements`.
 ///
 /// Platform interface classes are expected to have a private static token object which will be
-/// be passed to [verifyToken] along with a platform interface object for verification.
+/// be passed to [verify] along with a platform interface object for verification.
 ///
 /// Sample usage:
 ///
@@ -22,14 +22,14 @@
 ///
 ///   static UrlLauncherPlatform _instance = MethodChannelUrlLauncher();
 ///
-///   static const Object _token = Object();
+///   static final Object _token = Object();
 ///
 ///   static UrlLauncherPlatform get instance => _instance;
 ///
 ///   /// Platform-specific plugins should set this with their own platform-specific
 ///   /// class that extends [UrlLauncherPlatform] when they register themselves.
 ///   static set instance(UrlLauncherPlatform instance) {
-///     PlatformInterface.verifyToken(instance, _token);
+///     PlatformInterface.verify(instance, _token);
 ///     _instance = instance;
 ///   }
 ///
@@ -40,13 +40,16 @@
 /// to include the [MockPlatformInterfaceMixin] for the verification to be temporarily disabled. See
 /// [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`.
+  /// Constructs a PlatformInterface, for use only in constructors of abstract
+  /// derived classes.
+  ///
+  /// @param token The same, non-`const` `Object` that will be passed to `verify`.
   PlatformInterface({required Object token}) : _instanceToken = token;
 
   final Object? _instanceToken;
 
-  /// Ensures that the platform instance has a token that matches the
-  /// provided token and throws [AssertionError] if not.
+  /// Ensures that the platform instance was constructed with a non-`const` token
+  /// that matches the provided token and throws [AssertionError] if not.
   ///
   /// This is used to ensure that implementers are using `extends` rather than
   /// `implements`.
@@ -56,7 +59,22 @@
   ///
   /// This is implemented as a static method so that it cannot be overridden
   /// with `noSuchMethod`.
+  static void verify(PlatformInterface instance, Object token) {
+    if (identical(instance._instanceToken, const Object())) {
+      throw AssertionError('`const Object()` cannot be used as the token.');
+    }
+    _verify(instance, token);
+  }
+
+  /// Performs the same checks as `verify` but without throwing an
+  /// [AssertionError] if `const Object()` is used as the instance token.
+  ///
+  /// This method will be deprecated in a future release.
   static void verifyToken(PlatformInterface instance, Object token) {
+    _verify(instance, token);
+  }
+
+  static void _verify(PlatformInterface instance, Object token) {
     if (instance is MockPlatformInterfaceMixin) {
       bool assertionsEnabled = false;
       assert(() {
@@ -78,12 +96,12 @@
 
 /// A [PlatformInterface] mixin that can be combined with mockito's `Mock`.
 ///
-/// It passes the [PlatformInterface.verifyToken] check even though it isn't
+/// It passes the [PlatformInterface.verify] check even though it isn't
 /// using `extends`.
 ///
 /// This class is intended for use in tests only.
 ///
-/// Sample usage (assuming UrlLauncherPlatform extends [PlatformInterface]:
+/// Sample usage (assuming `UrlLauncherPlatform` extends [PlatformInterface]):
 ///
 /// ```dart
 /// class UrlLauncherPlatformMock extends Mock
diff --git a/packages/plugin_platform_interface/pubspec.yaml b/packages/plugin_platform_interface/pubspec.yaml
index 0b4b178..0559b8d 100644
--- a/packages/plugin_platform_interface/pubspec.yaml
+++ b/packages/plugin_platform_interface/pubspec.yaml
@@ -9,13 +9,13 @@
 #
 # This package is used as a second level dependency for many plugins, a major version bump here
 # is guaranteed to lead the ecosystem to a version lock (the first plugin that upgrades to version
-# 2 of this package cannot be used with any other plugin that have not yet migrated).
+# 3 of this package cannot be used with any other plugin that have not yet migrated).
 #
 # Please consider carefully before bumping the major version of this package, ideally it should only
-# 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: 2.0.2
+# be done when absolutely necessary and after the ecosystem has already migrated to 2.X.Y version
+# that is forward compatible with 3.0.0 (ideally the ecosystem have migrated to depend on:
+# `plugin_platform_interface: >=2.X.Y <4.0.0`).
+version: 2.1.0
 
 environment:
   sdk: ">=2.12.0 <3.0.0"
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 967fa79..a00462f 100644
--- a/packages/plugin_platform_interface/test/plugin_platform_interface_test.dart
+++ b/packages/plugin_platform_interface/test/plugin_platform_interface_test.dart
@@ -12,7 +12,7 @@
   static final Object _token = Object();
 
   static set instance(SamplePluginPlatform instance) {
-    PlatformInterface.verifyToken(instance, _token);
+    PlatformInterface.verify(instance, _token);
     // A real implementation would set a static instance field here.
   }
 }
@@ -26,20 +26,81 @@
 
 class ExtendsSamplePluginPlatform extends SamplePluginPlatform {}
 
+class ConstTokenPluginPlatform extends PlatformInterface {
+  ConstTokenPluginPlatform() : super(token: _token);
+
+  static const Object _token = Object(); // invalid
+
+  static set instance(ConstTokenPluginPlatform instance) {
+    PlatformInterface.verify(instance, _token);
+  }
+}
+
+class ExtendsConstTokenPluginPlatform extends ConstTokenPluginPlatform {}
+
+class VerifyTokenPluginPlatform extends PlatformInterface {
+  VerifyTokenPluginPlatform() : super(token: _token);
+
+  static final Object _token = Object();
+
+  static set instance(VerifyTokenPluginPlatform instance) {
+    PlatformInterface.verifyToken(instance, _token);
+    // A real implementation would set a static instance field here.
+  }
+}
+
+class ImplementsVerifyTokenPluginPlatform extends Mock
+    implements VerifyTokenPluginPlatform {}
+
+class ImplementsVerifyTokenPluginPlatformUsingMockPlatformInterfaceMixin
+    extends Mock
+    with MockPlatformInterfaceMixin
+    implements VerifyTokenPluginPlatform {}
+
+class ExtendsVerifyTokenPluginPlatform extends VerifyTokenPluginPlatform {}
+
 void main() {
-  test('Cannot be implemented with `implements`', () {
-    expect(() {
-      SamplePluginPlatform.instance = ImplementsSamplePluginPlatform();
-    }, throwsA(isA<AssertionError>()));
+  group('`verify`', () {
+    test('prevents implementation with `implements`', () {
+      expect(() {
+        SamplePluginPlatform.instance = ImplementsSamplePluginPlatform();
+      }, throwsA(isA<AssertionError>()));
+    });
+
+    test('allows mocking with `implements`', () {
+      final SamplePluginPlatform mock =
+          ImplementsSamplePluginPlatformUsingMockPlatformInterfaceMixin();
+      SamplePluginPlatform.instance = mock;
+    });
+
+    test('allows extending', () {
+      SamplePluginPlatform.instance = ExtendsSamplePluginPlatform();
+    });
+
+    test('prevents `const Object()` token', () {
+      expect(() {
+        ConstTokenPluginPlatform.instance = ExtendsConstTokenPluginPlatform();
+      }, throwsA(isA<AssertionError>()));
+    });
   });
 
-  test('Can be mocked with `implements`', () {
-    final SamplePluginPlatform mock =
-        ImplementsSamplePluginPlatformUsingMockPlatformInterfaceMixin();
-    SamplePluginPlatform.instance = mock;
-  });
+  // Tests of the earlier, to-be-deprecated `verifyToken` method
+  group('`verifyToken`', () {
+    test('prevents implementation with `implements`', () {
+      expect(() {
+        VerifyTokenPluginPlatform.instance =
+            ImplementsVerifyTokenPluginPlatform();
+      }, throwsA(isA<AssertionError>()));
+    });
 
-  test('Can be extended', () {
-    SamplePluginPlatform.instance = ExtendsSamplePluginPlatform();
+    test('allows mocking with `implements`', () {
+      final VerifyTokenPluginPlatform mock =
+          ImplementsVerifyTokenPluginPlatformUsingMockPlatformInterfaceMixin();
+      VerifyTokenPluginPlatform.instance = mock;
+    });
+
+    test('allows extending', () {
+      VerifyTokenPluginPlatform.instance = ExtendsVerifyTokenPluginPlatform();
+    });
   });
 }