44
55import 'dart:async' ;
66
7- import 'package:meta/meta.dart' show required;
7+ import 'package:meta/meta.dart' show required, visibleForTesting ;
88
99import 'method_channel_url_launcher.dart' ;
1010
1111/// The interface that implementations of url_launcher must implement.
1212///
13- /// Platform implementations that live in a separate package should extend this
14- /// class rather than implement it as `url_launcher` does not consider newly
15- /// added methods to be breaking changes. Extending this class (using `extends` )
16- /// ensures that the subclass will get the default implementation, while
17- /// platform implementations that `implements` this interface will be broken by
18- /// newly added [UrlLauncherPlatform] methods.
13+ /// Platform implementations should extend this class rather than implement it as `url_launcher`
14+ /// does not consider newly added methods to be breaking changes. Extending this class
15+ /// (using `extends` ) ensures that the subclass will get the default implementation, while
16+ /// platform implementations that `implements` this interface will be broken by newly added
17+ /// [UrlLauncherPlatform] methods.
1918abstract class UrlLauncherPlatform {
19+ /// Only mock implementations should set this to true.
20+ ///
21+ /// Mockito mocks are implementing this class with `implements` which is forbidden for anything
22+ /// other than mocks (see class docs). This property provides a backdoor for mockito mocks to
23+ /// skip the verification that the class isn't implemented with `implements` .
24+ @visibleForTesting
25+ bool get isMock => false ;
26+
2027 /// The default instance of [UrlLauncherPlatform] to use.
2128 ///
2229 /// Platform-specific plugins should override this with their own
2330 /// platform-specific class that extends [UrlLauncherPlatform] when they
2431 /// register themselves.
2532 ///
2633 /// Defaults to [MethodChannelUrlLauncher] .
27- static UrlLauncherPlatform instance = MethodChannelUrlLauncher ();
34+ static UrlLauncherPlatform _instance = MethodChannelUrlLauncher ();
35+
36+ static UrlLauncherPlatform get instance => _instance;
37+
38+ static set instance (UrlLauncherPlatform instance) {
39+ if (! instance.isMock) {
40+ try {
41+ instance._verifyProvidesDefaultImplementations ();
42+ } on NoSuchMethodError catch (_) {
43+ throw AssertionError (
44+ 'Platform interfaces must not be implemented with `implements`' );
45+ }
46+ }
47+ _instance = instance;
48+ }
2849
2950 /// Returns `true` if this platform is able to launch [url] .
3051 Future <bool > canLaunch (String url) {
@@ -51,4 +72,12 @@ abstract class UrlLauncherPlatform {
5172 Future <void > closeWebView () {
5273 throw UnimplementedError ('closeWebView() has not been implemented.' );
5374 }
75+
76+ // This method makes sure that UrlLauncher isn't implemented with `implements`.
77+ //
78+ // See class doc for more details on why implementing this class is forbidden.
79+ //
80+ // This private method is called by the instance setter, which fails if the class is
81+ // implemented with `implements`.
82+ void _verifyProvidesDefaultImplementations () {}
5483}
0 commit comments