Repository: flutter
Issue: Annotation to specify on which OS a class/method/property is available
Labels: c: new feature,framework,c: proposal,P3,a: plugins,team-framework,triaged-framework

## Use case

As a native plugin developer, it could be useful to have an annotation that we can put on a class/method/property to specify that the class/method/property annotated is available only for the specified OS or multiple OS.
This way, when a developer uses a particular class/method/property, the IDE (and/or at compile-time or whatever) says that he/she should check if the current platform support that feature.

For example, I have a plugin that communicates with native Java/Swift code.

Here is a class example that talks with native code with the `FlutterBinaryMessenger`:
```dart
class Foo {
  MethodChannel _channel = MethodChannel('com.pichillilorenzo/best_flutter_plugin_in_the_world');

  ///It does that.
  Future<dynamic> feature1() async {
    return await _channel.invokeMethod('feature1');
  }

  ///It does that.
  ///
  ///**NOTE**: Available only on Android
  Future<dynamic> feature2() async {
    return await _channel.invokeMethod('feature2');
  }
  
  ///It does that.
  ///
  ///**NOTE**: Available only on iOS
  Future<dynamic> feature3() async {
    return await _channel.invokeMethod('feature3');
  }
}
```
where:
- `feature1` is a cross-platform method available to both Android and iOS platform;
- `feature2` is a method available only on Android, otherwise `MissingPluginException`;
- `feature3`, you guessed it, is a method available only on iOS, otherwise `MissingPluginException`.

Also, imagine that these features are all well documented and the plugin developer, as you can see from the class example, already says in the docs that some of these features are available only in a specific OS.

A random developer sees my package and wants to use it. He/she install it using `pub` etc.
The developer goes directly into the code and uses the `feature3()` on the Android emulator.
No compile-errors, all goes well and the Flutter App gets installed correctly on the emulator.
Here we are, the app crashes because of a `MissingPluginException: No implementation found for method` error.

Using this annotation, for example, the IDE itself could already help the random developer to avoid these exceptions before even compile the App.

Obviously, at least, there is a workaround for that, that is the plugin developer should manage, in all platforms, all the `MethodCall` coming from Flutter/Dart side and return `null` when the feature is not supported, for example:
```dart
  ///It does that.
  ///
  ///**NOTE**: Available only on iOS
  Future<dynamic> feature3() async {
    if (!Platform.isIOS) {
      return null;
    }
    return await _channel.invokeMethod('feature3');
  }
```

Or on the native side, for example, on Android using Java:
```java
public class Foo implements MethodChannel.MethodCallHandler {

  @Override
  public void onMethodCall(@NonNull MethodCall call, @NonNull final MethodChannel.Result result) {
    switch (call.method) {
      case "feature1":
        result.success(feature1());
        break;
      case "feature2":
        result.success(feature2());
        break;
      case "feature3":
        result.success(null); // only on iOS :'(
        break;
      default:
        result.notImplemented();
    }
  }
}
```
The same thing for iOS using Swift or Obj C.

Is there something else that could be done? Other workarounds or best practices for that?

Every developer that wants to use a plugin/library/framework should read the documentation or, at least, the specific part of the feature he wants to use, but when you have a lot of plugins and the App is big enough, I don't know how many people do that 🤷.

So, in this case, we can close the issue here and we can say that the fault is of the developer that didn't read all the documentation.

However, this is a very simple and basic example, but you can extend it.
Think of a plugin where there are a lot of classes/methods/properties that the plugin developer wants to support for both Mobile and Desktop platforms.

Or you can think also of a random developer that wants to build first on Android, and after N months (so he probably doesn't remember that there is something available only on Android and he didn't add `if (Platform.isAndroid) { } else { }`), he wants to build it on iOS, but, unfortunately, he is using some features that are not available on iOS and that will cause the `MissingPluginException` at runtime.

## Proposal

Just as a proof of concept, a proposal could be something like this:
```dart
class Foo {
  MethodChannel _channel = MethodChannel('com.pichillilorenzo/best_flutter_plugin_in_the_world');

  ///It does that.
  Future<dynamic> feature1() async {
    return await _channel.invokeMethod('feature1');
  }

  ///It does that.
  ///
  ///**NOTE**: Available only on Android
  @platform(Android)
  Future<dynamic> feature2() async {
    return await _channel.invokeMethod('feature2');
  }
  
  ///It does that.
  ///
  ///**NOTE**: Available only on iOS
  @platform(iOS)
  Future<dynamic> feature3() async {
    return await _channel.invokeMethod('feature3');
  }
}
```
where `@platform()` accepts a list of Platforms, such as `@platform(Android, iOS)`.

Even a check on the OS version could be added, as you would do on Android using `@TargetApi(Build.VERSION_CODES.LOLLIPOP)` or iOS `@available(iOS 14.0, *)`.
In this case, it could be something like this:
```dart
class Foo {
  MethodChannel _channel = MethodChannel('com.pichillilorenzo/best_flutter_plugin_in_the_world');

  ///It does that.
  Future<dynamic> feature1() async {
    return await _channel.invokeMethod('feature1');
  }

  ///It does that.
  ///
  ///**NOTE**: Available only on Android
  @platform(Platform(Android, 21.0))
  Future<dynamic> feature2() async {
    return await _channel.invokeMethod('feature2');
  }
  
  ///It does that.
  ///
  ///**NOTE**: Available only on iOS
  @platform(Platform(iOS, 14.0))
  Future<dynamic> feature3() async {
    return await _channel.invokeMethod('feature3');
  }
}
```
where, same thing here, `@platform()` accepts a list of Platforms, such as `@platform(Platform(Android, 21.0), Platform(iOS, 14.0))`.

I don't know if this is something feasible or if there are other solutions or, simply, is not possible.