diff --git a/packages/plugin_platform_interface/CHANGELOG.md b/packages/plugin_platform_interface/CHANGELOG.md index d63cc2ae65..b45ca28fa2 100644 --- a/packages/plugin_platform_interface/CHANGELOG.md +++ b/packages/plugin_platform_interface/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 2.1.5 +* Updates README to improve example and discuss `base`. * Updates minimum Flutter version to 3.3. ## 2.1.4 diff --git a/packages/plugin_platform_interface/README.md b/packages/plugin_platform_interface/README.md index 1b1f80425f..c53b98dc21 100644 --- a/packages/plugin_platform_interface/README.md +++ b/packages/plugin_platform_interface/README.md @@ -2,8 +2,8 @@ This package provides a base class for platform interfaces of [federated flutter plugins](https://flutter.dev/go/federated-plugins). -Platform implementations should extend their platform interface classes rather than implement it as -newly added methods to platform interfaces are not considered as breaking changes. Extending a platform +Platform implementations should `extends` their platform interface class rather than `implement`s it, as +newly added methods to platform interfaces are not considered breaking changes. Extending a platform interface ensures that subclasses will get the default implementations from the base class, while platform implementations that `implements` their platform interface will be broken by newly added methods. @@ -12,24 +12,34 @@ and not implemented. ## Sample usage: + ```dart -abstract class UrlLauncherPlatform extends PlatformInterface { - UrlLauncherPlatform() : super(token: _token); - - static UrlLauncherPlatform _instance = MethodChannelUrlLauncher(); +abstract class SamplePluginPlatform extends PlatformInterface { + SamplePluginPlatform() : super(token: _token); static final Object _token = Object(); - static UrlLauncherPlatform get instance => _instance; + // A plugin can have a default implementation, as shown here, or `instance` + // can be nullable, and the default instance can be null. + static SamplePluginPlatform _instance = SamplePluginDefault(); - /// Platform-specific plugins should set this with their own platform-specific - /// class that extends [UrlLauncherPlatform] when they register themselves. - static set instance(UrlLauncherPlatform instance) { + static SamplePluginPlatform get instance => _instance; + + /// Platform-specific implementations should set this to their own + /// platform-specific class that extends [SamplePluginPlatform] when they + /// register themselves. + static set instance(SamplePluginPlatform instance) { PlatformInterface.verify(instance, _token); _instance = instance; } - } + // Methods for the plugin's platform interface would go here, often with + // implementations that throw UnimplementedError. +} + +class SamplePluginDefault extends SamplePluginPlatform { + // A default real implementation of the platform interface would go here. +} ``` This guarantees that UrlLauncherPlatform.instance cannot be set to an object that `implements` @@ -45,8 +55,24 @@ code only to disable the `extends` enforcement. For example, a Mockito mock of a platform interface can be created with: + ```dart -class UrlLauncherPlatformMock extends Mock - with MockPlatformInterfaceMixin - implements UrlLauncherPlatform {} +class SamplePluginPlatformMock extends Mock + with MockPlatformInterfaceMixin + implements SamplePluginPlatform {} ``` + +## A note about `base` + +In Dart 3, [the `base` keyword](https://dart.dev/language/class-modifiers#base) +was introduced to the language, which enforces that subclasses use `extends` +rather than `implements` at compile time. The Flutter team is +[considering deprecating this package in favor of using +`base`](https://github.com/flutter/flutter/issues/127396) for platfom interfaces, +but no decision has been made yet since it removes the ability to do mocking/faking +as shown above. + +Plugin authors may want to consider using `base` instead of this package when +creating new plugins. + +https://github.com/flutter/flutter/issues/127396 diff --git a/packages/plugin_platform_interface/pubspec.yaml b/packages/plugin_platform_interface/pubspec.yaml index 9ccd4a059e..2bd3909bc5 100644 --- a/packages/plugin_platform_interface/pubspec.yaml +++ b/packages/plugin_platform_interface/pubspec.yaml @@ -15,7 +15,7 @@ issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+ # 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.4 +version: 2.1.5 environment: sdk: ">=2.18.0 <4.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 869017cd4f..00da4a129f 100644 --- a/packages/plugin_platform_interface/test/plugin_platform_interface_test.dart +++ b/packages/plugin_platform_interface/test/plugin_platform_interface_test.dart @@ -6,18 +6,35 @@ import 'package:mockito/mockito.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; import 'package:test/test.dart'; -class SamplePluginPlatform extends PlatformInterface { +// #docregion Example +abstract class SamplePluginPlatform extends PlatformInterface { SamplePluginPlatform() : super(token: _token); static final Object _token = Object(); - // ignore: avoid_setters_without_getters + // A plugin can have a default implementation, as shown here, or `instance` + // can be nullable, and the default instance can be null. + static SamplePluginPlatform _instance = SamplePluginDefault(); + + static SamplePluginPlatform get instance => _instance; + + /// Platform-specific implementations should set this to their own + /// platform-specific class that extends [SamplePluginPlatform] when they + /// register themselves. static set instance(SamplePluginPlatform instance) { PlatformInterface.verify(instance, _token); - // A real implementation would set a static instance field here. + _instance = instance; } + + // Methods for the plugin's platform interface would go here, often with + // implementations that throw UnimplementedError. } +class SamplePluginDefault extends SamplePluginPlatform { + // A default real implementation of the platform interface would go here. +} +// #enddocregion Example + class ImplementsSamplePluginPlatform extends Mock implements SamplePluginPlatform {} @@ -27,11 +44,13 @@ class ImplementsSamplePluginPlatformUsingNoSuchMethod dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); } -class ImplementsSamplePluginPlatformUsingMockPlatformInterfaceMixin extends Mock +// #docregion Mock +class SamplePluginPlatformMock extends Mock with MockPlatformInterfaceMixin implements SamplePluginPlatform {} +// #enddocregion Mock -class ImplementsSamplePluginPlatformUsingFakePlatformInterfaceMixin extends Fake +class SamplePluginPlatformFake extends Fake with MockPlatformInterfaceMixin implements SamplePluginPlatform {} @@ -112,14 +131,12 @@ void main() { }); test('allows mocking with `implements`', () { - final SamplePluginPlatform mock = - ImplementsSamplePluginPlatformUsingMockPlatformInterfaceMixin(); + final SamplePluginPlatform mock = SamplePluginPlatformMock(); SamplePluginPlatform.instance = mock; }); test('allows faking with `implements`', () { - final SamplePluginPlatform fake = - ImplementsSamplePluginPlatformUsingFakePlatformInterfaceMixin(); + final SamplePluginPlatform fake = SamplePluginPlatformFake(); SamplePluginPlatform.instance = fake; }); diff --git a/script/configs/temp_exclude_excerpt.yaml b/script/configs/temp_exclude_excerpt.yaml index 7736efb7b8..4d712ed1ca 100644 --- a/script/configs/temp_exclude_excerpt.yaml +++ b/script/configs/temp_exclude_excerpt.yaml @@ -22,8 +22,6 @@ - ios_platform_images - multicast_dns - palette_generator -- pigeon -- plugin_platform_interface - pointer_interceptor - quick_actions/quick_actions - webview_flutter_android