Skip to content

[plugin_platform_interface] Adopt code-excerpts #4534

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/plugin_platform_interface/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
54 changes: 40 additions & 14 deletions packages/plugin_platform_interface/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -12,24 +12,34 @@ and not implemented.

## Sample usage:

<?code-excerpt "test/plugin_platform_interface_test.dart (Example)"?>
```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();

static SamplePluginPlatform 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) {
/// 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`
Expand All @@ -45,8 +55,24 @@ code only to disable the `extends` enforcement.

For example, a Mockito mock of a platform interface can be created with:

<?code-excerpt "test/plugin_platform_interface_test.dart (Mock)"?>
```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
2 changes: 1 addition & 1 deletion packages/plugin_platform_interface/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,34 @@ 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 {}
Expand All @@ -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 {}

Expand Down Expand Up @@ -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;
});

Expand Down
2 changes: 0 additions & 2 deletions script/configs/temp_exclude_excerpt.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@
- ios_platform_images
- multicast_dns
- palette_generator
- pigeon
- plugin_platform_interface
- pointer_interceptor
- quick_actions/quick_actions
- webview_flutter_android
Expand Down