Please start by taking a look at the general guide to contributing to the flutter/packages
repo:
https://github.com/flutter/packages/blob/main/CONTRIBUTING.md
The structure of this plugin is similar to a federated plugin, except the code for each package (platform interface, platform implementations, and app-facing interface) are maintained in this single plugin. The sections below will provide an overview of how this plugin implements each portion.
If you are familiar with changing federated plugin
in the flutter/packages
repo, the process is similar except that all changes are made in this
plugin. Therefore, it is not necessary to run the script that makes dependencies path based.
This plugin uses the native IMA SDKs for Android and iOS. The API for the SDK of both platforms are relatively similar, so this plugin attempts to maintain an interface that is similar to the native SDKs.
The app-facing interface uses delegation to interact with the underlying platform implementations.
Therefore, the platform interface is similar to the app-facing interface with the differences being
explained in the sections below. Many app-facing interface classes will contain a platform
field
that is used to forward handling to the platform implementation:
// App-facing class used by apps
class AdsLoader {
AdsLoader.fromPlatform(this.platform);
final PlatformAdsLoader platform;
Future<void> requestAds(AdsRequest request) {
return platform.requestAds(request);
}
}
// Platform interface class implemented by each platform
abstract base class PlatformAdsLoader {
Future<void> requestAds(AdsRequest request);
}
The platform
variable should also be used to provide access to platform specific methods or
platform specific creation parameters:
final AdsLoader loader = AdsLoader();
(loader.platform as AndroidAdsLoader).callAndroidSpecificMethod();
The other classes/enums included in the app-facing interface are typically exported from the platform interface. A data class being a good example of a class that is exported.
Code location: lib/src/platform_interface/
.
This declares an interface that each platform must implement to be supported by the app-facing interface.
The design of the platform interface should prioritize:
- Minimizing the chances of needing a breaking change when adding a new feature.
- Allowing platform implementations to easily add platform specific features.
- Being straight-forward to write unit tests.
Each platform creates a subclass of the central InteractiveMediaAdsPlatform
class. A platform implementation is set by setting InteractiveMediaAdsPlatform.instance
to an
instance of a platform implementation of InteractiveMediaAdsPlatform
.
Below are some of the types of classes in the interface.
These are classes where the app-facing interface needs to delegate handling to the platform
implementation. These classes are typically prefixed with Platform
.
If the corresponding app-facing class can be instantiated by the app (e.g. AdsLoader),
the InteractiveMediaAdsPlatform.instance
field should be used in a factory to instantiate the
correct platform implementation. See PlatformAdsLoader as an example. This class should should
also take a creation params class as the only constructor parameter.
If the corresponding app-facing class can't be instantiated by the app (e.g. AdsManager
), the
class should only have a single protected constructor. See PlatformAdsManager.
If the corresponding app-facing class needs to be a Widget
(e.g. AdDisplayContainer), this
should follow the same pattern as being instantiable by the app except it should contain a single
method: Widget build(BuildContext)
. See PlatformAdDisplayContainer.
Note
Every method should contain no more than one parameter. This allows the platform interface and platform implementations to add new features without requiring a breaking change.
These classes contain only fields and no methods. Each data class should be made @immutable
.
Code location:
- Android:
lib/src/android/
- iOS:
lib/src/ios/
The platform implementations create a subclass of InteractiveMediaAdsPlatform
and implement the
platform classes that are returned by this.
The platform implementations use Dart wrappers of their native SDKs. The SDKs are wrapped using
using the pigeon
package. However, the code that handles generating the wrappers for iOS is still
in the process of review, so this plugin must use a git dependency in the pubspec.
The wrappers for the SDK of each platform can be updated and modified by changing the pigeon files:
- Android:
pigeons/interactive_media_ads_android.dart
- iOS:
pigeons/interactive_media_ads_ios.dart
The generated files are located:
- Android:
lib/src/android/interactive_media_ads.g.dart
android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/InteractiveMediaAdsLibrary.g.kt
- iOS
lib/src/ios/interactive_media_ads.g.dart
ios/interactive_media_ads/Sources/interactive_media_ads/InteractiveMediaAdsLibrary.g.swift
To update a wrapper for a platform, follow the steps:
- Android: Run
flutter build apk --debug
inexample/
. - iOS: Run
flutter build ios --simulator
inexample/
- Android:
- Android SDK
- Pigeon file to update:
pigeons/interactive_media_ads_android.dart
- iOS:
- iOS SDK
- Pigeon file to update:
pigeons/interactive_media_ads_ios.dart
Once the file is updated, run pigeon to update generated code with the changes.
Running the flutter build
step from step 1 again should provide build errors and indicate what
needs to be done. Alternatively, it can be easier to update native code with the platform's specific
IDE:
- Android: Open
example/android/
in a separate Android Studio project. - iOS: Open
example/ios/
in Xcode.
Assuming a non-static method or constructor was added to the native wrapper, a native test will need to be added.
- Android native tests location:
android/src/test/kotlin/dev/flutter/packages/interactive_media_ads/
- iOS native tests location
example/ios/RunnerTests/
Tests for the platform implementations use mockito to generate mock objects of the native Dart
wrappers. To generate the mock objects in test/
, run mockito.
Code location: lib/src/
The app-facing interface shares the same structure as the platform interface and uses delegation to forward handling to the platform implementation. Note a few differences from the platform interface:
- Constructors and methods can contain more than one parameter.
- Platform classes can be instantiated with a platform implementation or creation params of
the corresponding platform interface class. See
AdsLoader.fromPlatform
andAdsLoader.fromPlatformCreationParams
.
2. In that issue add the specific native classes/methods that this feature requires for each platform:
Add a note if this feature only exist for a single platform.
If this is only supported on a single platform, add where it can be added in the platform implementation.