diff --git a/.github/.cspell/words_dictionary.txt b/.github/.cspell/words_dictionary.txt
index 5ea562a6..2ed338ad 100644
--- a/.github/.cspell/words_dictionary.txt
+++ b/.github/.cspell/words_dictionary.txt
@@ -9,6 +9,7 @@ dependabot # Automated dependency updates built into GitHub.
deserializes # The process of reconstructing a data structure or object from a series of bytes or a string in order to instantiate the object for consumption.
docusaurs # An optimized site generator in React.
genhtml # Tool to generate an HTML view from LCOV coverage data files.
+gobject # Base system for the GTK library used in Linux.
gradlew # Wrapper tool that uses Gradle.
lcov # Graphical tool for displaying coverage reports.
localizable # Capable of being localized.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 04c8a012..5296713a 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -5,32 +5,30 @@ We use [`release-please-action`][release-please-action-link] to automate version
### How it works:
- ๐ **On Every Commit to main:**
- - Commits are analyzed using [Conventional Commits][conventional_commits_link].
- - If a version bump is needed, a **release PR** is automatically created or updated by [`release-please-action`][release-please-action-link].
- - The **release PR** includes:
- - An updated `CHANGELOG.md`
- - A version bump in `brick.yaml`
-
- ##### ๐ก Notes
-
- - Release PR's are created **per template**.
- - The GitHub Action workflow that automates the release process is configured in `.github/workflows/release_please.yaml`
- - release-please settings are defined in `.release-please-config.json` and `.release-please-manifest.json`
- - The release PR can be manually edited before merging.
- - The release PR should be merged **ONLY** when a new release is needed.
+ - Commits are analyzed using [Conventional Commits][conventional_commits_link].
+ - If a version bump is needed, a **release PR** is automatically created or updated by [`release-please-action`][release-please-action-link].
+ - The **release PR** includes:
+ - An updated `CHANGELOG.md`
+ - A version bump in `brick.yaml`
+
+ ##### ๐ก Notes
+ - Release PR's are created **per template**.
+ - The GitHub Action workflow that automates the release process is configured in `.github/workflows/release_please.yaml`
+ - release-please settings are defined in `.release-please-config.json` and `.release-please-manifest.json`
+ - The release PR can be manually edited before merging.
+ - The release PR should be merged **ONLY** when a new release is needed.
- โ
**When the Release PR Is Merged:**
- - A new Git tag is created.
- - A GitHub Release is published with the changelog.
- - A new version of the brick is automatically published in [brickhub.dev][brickhub_link].
+ - A new Git tag is created.
+ - A GitHub Release is published with the changelog.
+ - A new version of the brick is automatically published in [brickhub.dev][brickhub_link].
- #### ๐ก Notes
-
- - GitHub Releases are created **per template**.
- - The publishing process is automatically triggered when a version tag is created.
- - The automated publishing workflows to [brickhub.dev][brickhub_link] are defined in `.github/workflows/publish_.yaml`
+ #### ๐ก Notes
+ - GitHub Releases are created **per template**.
+ - The publishing process is automatically triggered when a version tag is created.
+ - The automated publishing workflows to [brickhub.dev][brickhub_link] are defined in `.github/workflows/publish_.yaml`
@@ -40,4 +38,4 @@ This document provides a good summary of how it works and how we use it, but we
[brickhub_publishing_link]: https://docs.brickhub.dev/mason-publish
[sem_ver_link]: https://semver.org/
[release-please-action-link]: https://github.com/googleapis/release-please-action
-[conventional_commits_link]: https://www.conventionalcommits.org/en/v1.0.0
\ No newline at end of file
+[conventional_commits_link]: https://www.conventionalcommits.org/en/v1.0.0
diff --git a/README.md b/README.md
index 7b55531a..0a5283bc 100644
--- a/README.md
+++ b/README.md
@@ -22,9 +22,9 @@ Collection of open source templates created and maintained by the [Very Good Ven
### Deprecated Templates
-| Template | Description |
-| -------------------------------------------- | ------------------------------------------------------------------ |
-| [very_good_wear_app](very_good_wear_app/) | A Very Good WearOS Flutter app. **Deprecated** -- no longer maintained. |
+| Template | Description |
+| ----------------------------------------- | ----------------------------------------------------------------------- |
+| [very_good_wear_app](very_good_wear_app/) | A Very Good WearOS Flutter app. **Deprecated** -- no longer maintained. |
@@ -94,5 +94,3 @@ Collection of open source templates created and maintained by the [Very Good Ven
[very_good_flutter_plugin_workflow_link]: https://github.com/VeryGoodOpenSource/very_good_templates/actions/workflows/very_good_flutter_plugin.yaml?query=branch%3Amain
[very_good_flutter_plugin_code_link]: https://github.com/VeryGoodOpenSource/very_good_templates/tree/main/very_good_flutter_plugin
[very_good_flutter_plugin_docs_link]: https://cli.vgv.dev/docs/templates/federated_plugin
-
-
diff --git a/very_good_dart_package/.gitignore b/very_good_dart_package/.gitignore
index e2f4add9..7991ea7e 100644
--- a/very_good_dart_package/.gitignore
+++ b/very_good_dart_package/.gitignore
@@ -6,6 +6,7 @@
.dart_tool/
.packages
pubspec.lock
+build/
# Files and directories created by mason
.mason/
diff --git a/very_good_flutter_plugin/__brick__/README.md b/very_good_flutter_plugin/__brick__/README.md
index 10170792..d05a0918 100644
--- a/very_good_flutter_plugin/__brick__/README.md
+++ b/very_good_flutter_plugin/__brick__/README.md
@@ -13,11 +13,10 @@ A Very Good Flutter Federated Plugin created by the [Very Good Ventures Team][ve
Generated by the [Very Good CLI][very_good_cli_link]. ๐ค
+## Integration tests ๐งช
-### Integration tests ๐งช
-
-Very Good Flutter Plugin uses [fluttium][fluttium_link] for integration tests. Those tests are located
-in the front facing package `{{project_name.snakeCase()}}` example.
+Very Good Flutter Plugin uses [fluttium][fluttium_link] for integration tests. Those tests are located
+in the front facing package `{{project_name.snakeCase()}}` example.
**โ In order to run the integration tests, you need to have the `fluttium_cli` installed. [See how][fluttium_install].**
@@ -40,4 +39,4 @@ fluttium test flows/test_platform_name.yaml
[very_good_ventures_link_dark]: https://verygood.ventures/?utm_source=github&utm_medium=banner&utm_campaign=core#gh-dark-mode-only
[very_good_ventures_link_light]: https://verygood.ventures/?utm_source=github&utm_medium=banner&utm_campaign=core#gh-light-mode-only
[fluttium_link]: https://fluttium.dev/
-[fluttium_install]: https://fluttium.dev/docs/getting-started/installing-cli
\ No newline at end of file
+[fluttium_install]: https://fluttium.dev/docs/getting-started/installing-cli
diff --git a/very_good_flutter_plugin/__brick__/{{#android}}{{project_name.snakeCase()}}_android{{/android}}/analysis_options.yaml b/very_good_flutter_plugin/__brick__/{{#android}}{{project_name.snakeCase()}}_android{{/android}}/analysis_options.yaml
index 9df80aa4..c5dcf888 100644
--- a/very_good_flutter_plugin/__brick__/{{#android}}{{project_name.snakeCase()}}_android{{/android}}/analysis_options.yaml
+++ b/very_good_flutter_plugin/__brick__/{{#android}}{{project_name.snakeCase()}}_android{{/android}}/analysis_options.yaml
@@ -1 +1,4 @@
include: package:very_good_analysis/analysis_options.yaml
+analyzer:
+ exclude:
+ - "**/*.g.dart"
diff --git a/very_good_flutter_plugin/__brick__/{{#android}}{{project_name.snakeCase()}}_android{{/android}}/android/src/main/kotlin/{{org_name.pathCase()}}/{{project_name.pascalCase()}}Plugin.kt b/very_good_flutter_plugin/__brick__/{{#android}}{{project_name.snakeCase()}}_android{{/android}}/android/src/main/kotlin/{{org_name.pathCase()}}/{{project_name.pascalCase()}}Plugin.kt
index 6ff70458..32bf8bff 100644
--- a/very_good_flutter_plugin/__brick__/{{#android}}{{project_name.snakeCase()}}_android{{/android}}/android/src/main/kotlin/{{org_name.pathCase()}}/{{project_name.pascalCase()}}Plugin.kt
+++ b/very_good_flutter_plugin/__brick__/{{#android}}{{project_name.snakeCase()}}_android{{/android}}/android/src/main/kotlin/{{org_name.pathCase()}}/{{project_name.pascalCase()}}Plugin.kt
@@ -1,30 +1,22 @@
package {{org_name.dotCase()}}
-import androidx.annotation.NonNull
-
+import {{project_name.pascalCase()}}Api
import io.flutter.embedding.engine.plugins.FlutterPlugin
-import io.flutter.plugin.common.MethodCall
-import io.flutter.plugin.common.MethodChannel
-import io.flutter.plugin.common.MethodChannel.MethodCallHandler
-import io.flutter.plugin.common.MethodChannel.Result
-class {{project_name.pascalCase()}}Plugin : FlutterPlugin, MethodCallHandler {
- private lateinit var channel: MethodChannel
+class {{project_name.pascalCase()}}Plugin : FlutterPlugin, {{project_name.pascalCase()}}Api {
+ companion object {
+ private const val TAG = "{{project_name.pascalCase()}}Plugin"
+ }
- override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
- channel = MethodChannel(flutterPluginBinding.binaryMessenger, "{{project_name.snakeCase()}}_android")
- channel.setMethodCallHandler(this)
+ override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {
+ {{project_name.pascalCase()}}Api.setUp(binding.binaryMessenger, this)
}
- override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
- if (call.method == "getPlatformName") {
- result.success("Android ${android.os.Build.VERSION.RELEASE}")
- } else {
- result.notImplemented()
- }
+ override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
+ {{project_name.pascalCase()}}Api.setUp(binding.binaryMessenger, null)
}
- override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
- channel.setMethodCallHandler(null)
+ override fun getPlatformName(callback: (Result) -> Unit) {
+ callback(Result.success("Android ${android.os.Build.VERSION.RELEASE}"))
}
}
\ No newline at end of file
diff --git a/very_good_flutter_plugin/__brick__/{{#android}}{{project_name.snakeCase()}}_android{{/android}}/lib/{{project_name.snakeCase()}}_android.dart b/very_good_flutter_plugin/__brick__/{{#android}}{{project_name.snakeCase()}}_android{{/android}}/lib/{{project_name.snakeCase()}}_android.dart
index 14ea9fc0..13cc7615 100644
--- a/very_good_flutter_plugin/__brick__/{{#android}}{{project_name.snakeCase()}}_android{{/android}}/lib/{{project_name.snakeCase()}}_android.dart
+++ b/very_good_flutter_plugin/__brick__/{{#android}}{{project_name.snakeCase()}}_android{{/android}}/lib/{{project_name.snakeCase()}}_android.dart
@@ -1,15 +1,18 @@
import 'package:flutter/foundation.dart';
-import 'package:flutter/services.dart';
+import 'package:{{project_name.snakeCase()}}_android/src/messages.g.dart';
import 'package:{{project_name.snakeCase()}}_platform_interface/{{project_name.snakeCase()}}_platform_interface.dart';
+/// {@template {{project_name.snakeCase()}}_android}
/// The Android implementation of [{{project_name.pascalCase()}}Platform].
-class {{project_name.pascalCase()}}Android
- extends {{project_name.pascalCase()}}Platform {
- /// The method channel used to interact with the native platform.
- @visibleForTesting
- final methodChannel = const MethodChannel(
- '{{project_name.snakeCase()}}_android',
- );
+/// {@endtemplate}
+class {{project_name.pascalCase()}}Android extends {{project_name.pascalCase()}}Platform {
+ /// {@macro {{project_name.snakeCase()}}_android}
+ {{project_name.pascalCase()}}Android({
+ @visibleForTesting {{project_name.pascalCase()}}Api? api,
+ }) : api = api ?? {{project_name.pascalCase()}}Api();
+
+ /// The API used to interact with the native platform.
+ final {{project_name.pascalCase()}}Api api;
/// Registers this class as the default instance of
/// [{{project_name.pascalCase()}}Platform].
@@ -20,6 +23,6 @@ class {{project_name.pascalCase()}}Android
@override
Future getPlatformName() {
- return methodChannel.invokeMethod('getPlatformName');
+ return api.getPlatformName();
}
}
diff --git a/very_good_flutter_plugin/__brick__/{{#android}}{{project_name.snakeCase()}}_android{{/android}}/pigeons/copyright.txt b/very_good_flutter_plugin/__brick__/{{#android}}{{project_name.snakeCase()}}_android{{/android}}/pigeons/copyright.txt
new file mode 100644
index 00000000..edc1c28e
--- /dev/null
+++ b/very_good_flutter_plugin/__brick__/{{#android}}{{project_name.snakeCase()}}_android{{/android}}/pigeons/copyright.txt
@@ -0,0 +1 @@
+Copyright (c) {{currentYear}}, {{org_name.titleCase()}}
diff --git a/very_good_flutter_plugin/__brick__/{{#android}}{{project_name.snakeCase()}}_android{{/android}}/pigeons/messages.dart b/very_good_flutter_plugin/__brick__/{{#android}}{{project_name.snakeCase()}}_android{{/android}}/pigeons/messages.dart
new file mode 100644
index 00000000..9dfaf653
--- /dev/null
+++ b/very_good_flutter_plugin/__brick__/{{#android}}{{project_name.snakeCase()}}_android{{/android}}/pigeons/messages.dart
@@ -0,0 +1,19 @@
+// {{project_name.pascalCase()}}Api must be abstract.
+// ignore_for_file: one_member_abstracts
+
+import 'package:pigeon/pigeon.dart';
+
+@ConfigurePigeon(
+ PigeonOptions(
+ dartOut: 'lib/src/messages.g.dart',
+ dartPackageName: '{{project_name.snakeCase()}}',
+ kotlinOut: 'android/src/main/kotlin/{{org_name.pathCase()}}/Messages.g.kt',
+ kotlinOptions: KotlinOptions(),
+ copyrightHeader: 'pigeons/copyright.txt',
+ ),
+)
+@HostApi()
+abstract class {{project_name.pascalCase()}}Api {
+ @async
+ String? getPlatformName();
+}
diff --git a/very_good_flutter_plugin/__brick__/{{#android}}{{project_name.snakeCase()}}_android{{/android}}/pubspec.yaml b/very_good_flutter_plugin/__brick__/{{#android}}{{project_name.snakeCase()}}_android{{/android}}/pubspec.yaml
index d0dd6078..5752451e 100644
--- a/very_good_flutter_plugin/__brick__/{{#android}}{{project_name.snakeCase()}}_android{{/android}}/pubspec.yaml
+++ b/very_good_flutter_plugin/__brick__/{{#android}}{{project_name.snakeCase()}}_android{{/android}}/pubspec.yaml
@@ -24,5 +24,7 @@ dependencies:
dev_dependencies:
flutter_test:
sdk: flutter
+ mocktail: ^1.0.4
+ pigeon: ^26.0.2
plugin_platform_interface: ^2.1.8
very_good_analysis: ^10.2.0
diff --git a/very_good_flutter_plugin/__brick__/{{#android}}{{project_name.snakeCase()}}_android{{/android}}/test/{{project_name.snakeCase()}}_android_test.dart b/very_good_flutter_plugin/__brick__/{{#android}}{{project_name.snakeCase()}}_android{{/android}}/test/{{project_name.snakeCase()}}_android_test.dart
index 5f74c50c..237619a0 100644
--- a/very_good_flutter_plugin/__brick__/{{#android}}{{project_name.snakeCase()}}_android{{/android}}/test/{{project_name.snakeCase()}}_android_test.dart
+++ b/very_good_flutter_plugin/__brick__/{{#android}}{{project_name.snakeCase()}}_android{{/android}}/test/{{project_name.snakeCase()}}_android_test.dart
@@ -1,32 +1,23 @@
-import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
+import 'package:mocktail/mocktail.dart';
+import 'package:{{project_name.snakeCase()}}_android/src/messages.g.dart';
import 'package:{{project_name.snakeCase()}}_android/{{project_name.snakeCase()}}_android.dart';
import 'package:{{project_name.snakeCase()}}_platform_interface/{{project_name.snakeCase()}}_platform_interface.dart';
+class _Mock{{project_name.pascalCase()}}Api extends Mock
+ implements {{project_name.pascalCase()}}Api {}
+
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
- group('{{project_name.pascalCase()}}Android', () {
+ group({{project_name.pascalCase()}}Android, () {
const kPlatformName = 'Android';
late {{project_name.pascalCase()}}Android {{project_name.camelCase()}};
- late List log;
-
- setUp(() async {
- {{project_name.camelCase()}} = {{project_name.pascalCase()}}Android();
-
- log = [];
- TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
- .setMockMethodCallHandler({{project_name.camelCase()}}.methodChannel, (
- methodCall,
- ) async {
- log.add(methodCall);
- switch (methodCall.method) {
- case 'getPlatformName':
- return kPlatformName;
- default:
- return null;
- }
- });
+ late {{project_name.pascalCase()}}Api api;
+
+ setUp(() {
+ api = _Mock{{project_name.pascalCase()}}Api();
+ {{project_name.camelCase()}} = {{project_name.pascalCase()}}Android(api: api);
});
test('can be registered', () {
@@ -38,12 +29,14 @@ void main() {
});
test('getPlatformName returns correct name', () async {
- final name = await {{project_name.camelCase()}}.getPlatformName();
- expect(
- log,
- [isMethodCall('getPlatformName', arguments: null)],
+ when(api.getPlatformName).thenAnswer((_) async => kPlatformName);
+
+ await expectLater(
+ {{project_name.camelCase()}}.getPlatformName(),
+ completion(equals(kPlatformName)),
);
- expect(name, equals(kPlatformName));
+
+ verify(api.getPlatformName).called(1);
});
});
}
diff --git a/very_good_flutter_plugin/__brick__/{{#ios}}{{project_name.snakeCase()}}_ios{{/ios}}/analysis_options.yaml b/very_good_flutter_plugin/__brick__/{{#ios}}{{project_name.snakeCase()}}_ios{{/ios}}/analysis_options.yaml
index 9df80aa4..c5dcf888 100644
--- a/very_good_flutter_plugin/__brick__/{{#ios}}{{project_name.snakeCase()}}_ios{{/ios}}/analysis_options.yaml
+++ b/very_good_flutter_plugin/__brick__/{{#ios}}{{project_name.snakeCase()}}_ios{{/ios}}/analysis_options.yaml
@@ -1 +1,4 @@
include: package:very_good_analysis/analysis_options.yaml
+analyzer:
+ exclude:
+ - "**/*.g.dart"
diff --git a/very_good_flutter_plugin/__brick__/{{#ios}}{{project_name.snakeCase()}}_ios{{/ios}}/ios/{{project_name.snakeCase()}}_ios/Sources/{{project_name.snakeCase()}}_ios/{{project_name.pascalCase()}}Plugin.swift b/very_good_flutter_plugin/__brick__/{{#ios}}{{project_name.snakeCase()}}_ios{{/ios}}/ios/{{project_name.snakeCase()}}_ios/Sources/{{project_name.snakeCase()}}_ios/{{project_name.pascalCase()}}Plugin.swift
index 40605ee1..7834b0a7 100644
--- a/very_good_flutter_plugin/__brick__/{{#ios}}{{project_name.snakeCase()}}_ios{{/ios}}/ios/{{project_name.snakeCase()}}_ios/Sources/{{project_name.snakeCase()}}_ios/{{project_name.pascalCase()}}Plugin.swift
+++ b/very_good_flutter_plugin/__brick__/{{#ios}}{{project_name.snakeCase()}}_ios{{/ios}}/ios/{{project_name.snakeCase()}}_ios/Sources/{{project_name.snakeCase()}}_ios/{{project_name.pascalCase()}}Plugin.swift
@@ -1,14 +1,14 @@
import Flutter
-import UIKit
-public class {{project_name.pascalCase()}}Plugin: NSObject, FlutterPlugin {
+public class {{project_name.pascalCase()}}Plugin: NSObject, FlutterPlugin, {{project_name.pascalCase()}}Api {
public static func register(with registrar: FlutterPluginRegistrar) {
- let channel = FlutterMethodChannel(name: "{{project_name.snakeCase()}}_ios", binaryMessenger: registrar.messenger())
+ let binaryMessenger = registrar.messenger()
let instance = {{project_name.pascalCase()}}Plugin()
- registrar.addMethodCallDelegate(instance, channel: channel)
+ {{project_name.pascalCase()}}ApiSetup.setUp(binaryMessenger: binaryMessenger, api: instance)
+ registrar.publish(instance)
}
- public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
- result("iOS")
+ func getPlatformName(completion: @escaping (Result) -> Void) {
+ completion(.success("iOS"))
}
}
diff --git a/very_good_flutter_plugin/__brick__/{{#ios}}{{project_name.snakeCase()}}_ios{{/ios}}/lib/{{project_name.snakeCase()}}_ios.dart b/very_good_flutter_plugin/__brick__/{{#ios}}{{project_name.snakeCase()}}_ios{{/ios}}/lib/{{project_name.snakeCase()}}_ios.dart
index 0fe4255c..2d0dd172 100644
--- a/very_good_flutter_plugin/__brick__/{{#ios}}{{project_name.snakeCase()}}_ios{{/ios}}/lib/{{project_name.snakeCase()}}_ios.dart
+++ b/very_good_flutter_plugin/__brick__/{{#ios}}{{project_name.snakeCase()}}_ios{{/ios}}/lib/{{project_name.snakeCase()}}_ios.dart
@@ -1,15 +1,18 @@
import 'package:flutter/foundation.dart';
-import 'package:flutter/services.dart';
+import 'package:{{project_name.snakeCase()}}_ios/src/messages.g.dart';
import 'package:{{project_name.snakeCase()}}_platform_interface/{{project_name.snakeCase()}}_platform_interface.dart';
+/// {@template {{project_name.snakeCase()}}_ios}
/// The iOS implementation of [{{project_name.pascalCase()}}Platform].
-class {{project_name.pascalCase()}}IOS
- extends {{project_name.pascalCase()}}Platform {
- /// The method channel used to interact with the native platform.
- @visibleForTesting
- final methodChannel = const MethodChannel(
- '{{project_name.snakeCase()}}_ios',
- );
+/// {@endtemplate}
+class {{project_name.pascalCase()}}IOS extends {{project_name.pascalCase()}}Platform {
+ /// {@macro {{project_name.snakeCase()}}_ios}
+ {{project_name.pascalCase()}}IOS({
+ @visibleForTesting {{project_name.pascalCase()}}Api? api,
+ }) : api = api ?? {{project_name.pascalCase()}}Api();
+
+ /// The API used to interact with the native platform.
+ final {{project_name.pascalCase()}}Api api;
/// Registers this class as the default instance of
/// [{{project_name.pascalCase()}}Platform].
@@ -20,6 +23,6 @@ class {{project_name.pascalCase()}}IOS
@override
Future getPlatformName() {
- return methodChannel.invokeMethod('getPlatformName');
+ return api.getPlatformName();
}
}
diff --git a/very_good_flutter_plugin/__brick__/{{#ios}}{{project_name.snakeCase()}}_ios{{/ios}}/pigeons/copyright.txt b/very_good_flutter_plugin/__brick__/{{#ios}}{{project_name.snakeCase()}}_ios{{/ios}}/pigeons/copyright.txt
new file mode 100644
index 00000000..00a4ecbb
--- /dev/null
+++ b/very_good_flutter_plugin/__brick__/{{#ios}}{{project_name.snakeCase()}}_ios{{/ios}}/pigeons/copyright.txt
@@ -0,0 +1 @@
+Copyright (c) {{currentYear}}, {{org_name.sentenceCase()}}
diff --git a/very_good_flutter_plugin/__brick__/{{#ios}}{{project_name.snakeCase()}}_ios{{/ios}}/pigeons/messages.dart b/very_good_flutter_plugin/__brick__/{{#ios}}{{project_name.snakeCase()}}_ios{{/ios}}/pigeons/messages.dart
new file mode 100644
index 00000000..f2ad49f3
--- /dev/null
+++ b/very_good_flutter_plugin/__brick__/{{#ios}}{{project_name.snakeCase()}}_ios{{/ios}}/pigeons/messages.dart
@@ -0,0 +1,19 @@
+// {{project_name.pascalCase()}}Api must be abstract.
+// ignore_for_file: one_member_abstracts
+
+import 'package:pigeon/pigeon.dart';
+
+@ConfigurePigeon(
+ PigeonOptions(
+ dartOut: 'lib/src/messages.g.dart',
+ dartPackageName: '{{project_name.snakeCase()}}',
+ swiftOut: 'ios/{{project_name.snakeCase()}}_ios/Sources/{{project_name.snakeCase()}}_ios/Messages.g.swift',
+ swiftOptions: SwiftOptions(),
+ copyrightHeader: 'pigeons/copyright.txt',
+ ),
+)
+@HostApi()
+abstract class {{project_name.pascalCase()}}Api {
+ @async
+ String? getPlatformName();
+}
diff --git a/very_good_flutter_plugin/__brick__/{{#ios}}{{project_name.snakeCase()}}_ios{{/ios}}/pubspec.yaml b/very_good_flutter_plugin/__brick__/{{#ios}}{{project_name.snakeCase()}}_ios{{/ios}}/pubspec.yaml
index 40855fe5..7a7ffbe5 100644
--- a/very_good_flutter_plugin/__brick__/{{#ios}}{{project_name.snakeCase()}}_ios{{/ios}}/pubspec.yaml
+++ b/very_good_flutter_plugin/__brick__/{{#ios}}{{project_name.snakeCase()}}_ios{{/ios}}/pubspec.yaml
@@ -23,5 +23,7 @@ dependencies:
dev_dependencies:
flutter_test:
sdk: flutter
+ mocktail: ^1.0.4
+ pigeon: ^26.0.2
plugin_platform_interface: ^2.1.8
very_good_analysis: ^10.2.0
diff --git a/very_good_flutter_plugin/__brick__/{{#ios}}{{project_name.snakeCase()}}_ios{{/ios}}/test/{{project_name.snakeCase()}}_ios_test.dart b/very_good_flutter_plugin/__brick__/{{#ios}}{{project_name.snakeCase()}}_ios{{/ios}}/test/{{project_name.snakeCase()}}_ios_test.dart
index c3b74995..5ff23cfa 100644
--- a/very_good_flutter_plugin/__brick__/{{#ios}}{{project_name.snakeCase()}}_ios{{/ios}}/test/{{project_name.snakeCase()}}_ios_test.dart
+++ b/very_good_flutter_plugin/__brick__/{{#ios}}{{project_name.snakeCase()}}_ios{{/ios}}/test/{{project_name.snakeCase()}}_ios_test.dart
@@ -1,32 +1,23 @@
-import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
+import 'package:mocktail/mocktail.dart';
+import 'package:{{project_name.snakeCase()}}_ios/src/messages.g.dart';
import 'package:{{project_name.snakeCase()}}_ios/{{project_name.snakeCase()}}_ios.dart';
import 'package:{{project_name.snakeCase()}}_platform_interface/{{project_name.snakeCase()}}_platform_interface.dart';
+class _Mock{{project_name.pascalCase()}}Api extends Mock
+ implements {{project_name.pascalCase()}}Api {}
+
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
- group('{{project_name.pascalCase()}}IOS', () {
+ group({{project_name.pascalCase()}}IOS, () {
const kPlatformName = 'iOS';
late {{project_name.pascalCase()}}IOS {{project_name.camelCase()}};
- late List log;
-
- setUp(() async {
- {{project_name.camelCase()}} = {{project_name.pascalCase()}}IOS();
-
- log = [];
- TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
- .setMockMethodCallHandler({{project_name.camelCase()}}.methodChannel, (
- methodCall,
- ) async {
- log.add(methodCall);
- switch (methodCall.method) {
- case 'getPlatformName':
- return kPlatformName;
- default:
- return null;
- }
- });
+ late {{project_name.pascalCase()}}Api api;
+
+ setUp(() {
+ api = _Mock{{project_name.pascalCase()}}Api();
+ {{project_name.camelCase()}} = {{project_name.pascalCase()}}IOS(api: api);
});
test('can be registered', () {
@@ -38,12 +29,14 @@ void main() {
});
test('getPlatformName returns correct name', () async {
- final name = await {{project_name.camelCase()}}.getPlatformName();
- expect(
- log,
- [isMethodCall('getPlatformName', arguments: null)],
+ when(api.getPlatformName).thenAnswer((_) async => kPlatformName);
+
+ await expectLater(
+ {{project_name.camelCase()}}.getPlatformName(),
+ completion(equals(kPlatformName)),
);
- expect(name, equals(kPlatformName));
+
+ verify(api.getPlatformName).called(1);
});
});
}
diff --git a/very_good_flutter_plugin/__brick__/{{#linux}}{{project_name.snakeCase()}}_linux{{/linux}}/analysis_options.yaml b/very_good_flutter_plugin/__brick__/{{#linux}}{{project_name.snakeCase()}}_linux{{/linux}}/analysis_options.yaml
index 9df80aa4..c5dcf888 100644
--- a/very_good_flutter_plugin/__brick__/{{#linux}}{{project_name.snakeCase()}}_linux{{/linux}}/analysis_options.yaml
+++ b/very_good_flutter_plugin/__brick__/{{#linux}}{{project_name.snakeCase()}}_linux{{/linux}}/analysis_options.yaml
@@ -1 +1,4 @@
include: package:very_good_analysis/analysis_options.yaml
+analyzer:
+ exclude:
+ - "**/*.g.dart"
diff --git a/very_good_flutter_plugin/__brick__/{{#linux}}{{project_name.snakeCase()}}_linux{{/linux}}/lib/src/{{project_name.snakeCase()}}_linux.dart b/very_good_flutter_plugin/__brick__/{{#linux}}{{project_name.snakeCase()}}_linux{{/linux}}/lib/src/{{project_name.snakeCase()}}_linux.dart
index f00bee73..3079b2bf 100644
--- a/very_good_flutter_plugin/__brick__/{{#linux}}{{project_name.snakeCase()}}_linux{{/linux}}/lib/src/{{project_name.snakeCase()}}_linux.dart
+++ b/very_good_flutter_plugin/__brick__/{{#linux}}{{project_name.snakeCase()}}_linux{{/linux}}/lib/src/{{project_name.snakeCase()}}_linux.dart
@@ -1,15 +1,18 @@
import 'package:flutter/foundation.dart';
-import 'package:flutter/services.dart';
+import 'package:{{project_name.snakeCase()}}_linux/src/messages.g.dart';
import 'package:{{project_name.snakeCase()}}_platform_interface/{{project_name.snakeCase()}}_platform_interface.dart';
+/// {@template {{project_name.snakeCase()}}_linux}
/// The Linux implementation of [{{project_name.pascalCase()}}Platform].
-class {{project_name.pascalCase()}}Linux
- extends {{project_name.pascalCase()}}Platform {
- /// The method channel used to interact with the native platform.
- @visibleForTesting
- final methodChannel = const MethodChannel(
- '{{project_name.snakeCase()}}_linux',
- );
+/// {@endtemplate}
+class {{project_name.pascalCase()}}Linux extends {{project_name.pascalCase()}}Platform {
+ /// {@macro {{project_name.snakeCase()}}_linux}
+ {{project_name.pascalCase()}}Linux({
+ @visibleForTesting {{project_name.pascalCase()}}Api? api,
+ }) : api = api ?? {{project_name.pascalCase()}}Api();
+
+ /// The API used to interact with the native platform.
+ final {{project_name.pascalCase()}}Api api;
/// Registers this class as the default instance of
/// [{{project_name.pascalCase()}}Platform].
@@ -20,6 +23,6 @@ class {{project_name.pascalCase()}}Linux
@override
Future getPlatformName() {
- return methodChannel.invokeMethod('getPlatformName');
+ return api.getPlatformName();
}
}
diff --git a/very_good_flutter_plugin/__brick__/{{#linux}}{{project_name.snakeCase()}}_linux{{/linux}}/linux/{{project_name.snakeCase()}}_linux_plugin.cc b/very_good_flutter_plugin/__brick__/{{#linux}}{{project_name.snakeCase()}}_linux{{/linux}}/linux/{{project_name.snakeCase()}}_linux_plugin.cc
index 968995c7..9ec247f1 100644
--- a/very_good_flutter_plugin/__brick__/{{#linux}}{{project_name.snakeCase()}}_linux{{/linux}}/linux/{{project_name.snakeCase()}}_linux_plugin.cc
+++ b/very_good_flutter_plugin/__brick__/{{#linux}}{{project_name.snakeCase()}}_linux{{/linux}}/linux/{{project_name.snakeCase()}}_linux_plugin.cc
@@ -1,42 +1,32 @@
#include "include/{{project_name.snakeCase()}}_linux/{{project_name.snakeCase()}}_plugin.h"
-
#include
#include
#include
-
#include
-
-const char kChannelName[] = "{{project_name.snakeCase()}}_linux";
-const char kGetPlatformName[] = "getPlatformName";
+#include "messages.g.h"
struct _Fl{{project_name.pascalCase()}}Plugin {
GObject parent_instance;
FlPluginRegistrar* registrar;
-
- // Connection to Flutter engine.
- FlMethodChannel* channel;
};
G_DEFINE_TYPE(Fl{{project_name.pascalCase()}}Plugin, fl_{{project_name.snakeCase()}}_plugin, g_object_get_type())
-// Called when a method call is received from Flutter.
-static void method_call_cb(FlMethodChannel* channel, FlMethodCall* method_call,
- gpointer user_data) {
- const gchar* method = fl_method_call_get_name(method_call);
-
- g_autoptr(FlMethodResponse) response = nullptr;
- if (strcmp(method, kGetPlatformName) == 0)
- response = FL_METHOD_RESPONSE(fl_method_success_response_new(fl_value_new_string("Linux")));
- else
- response = FL_METHOD_RESPONSE(fl_method_not_implemented_response_new());
-
- g_autoptr(GError) error = nullptr;
- if (!fl_method_call_respond(method_call, response, &error))
- g_warning("Failed to send method call response: %s", error->message);
+static void handle_get_platform_name(
+ {{project_name.pascalCase()}}{{project_name.pascalCase()}}ApiResponseHandle* response_handle,
+ gpointer user_data) {
+ {{project_name.snakeCase()}}_{{project_name.snakeCase()}}_api_respond_get_platform_name(response_handle, "Linux");
}
+static {{project_name.pascalCase()}}{{project_name.pascalCase()}}ApiVTable api_vtable = {
+ handle_get_platform_name,
+};
+
static void fl_{{project_name.snakeCase()}}_plugin_dispose(GObject* object) {
+ Fl{{project_name.pascalCase()}}Plugin* self = FL_{{project_name.constantCase()}}_PLUGIN(object);
+ {{project_name.snakeCase()}}_{{project_name.snakeCase()}}_api_clear_method_handlers(
+ fl_plugin_registrar_get_messenger(self->registrar), nullptr);
G_OBJECT_CLASS(fl_{{project_name.snakeCase()}}_plugin_parent_class)->dispose(object);
}
@@ -50,12 +40,9 @@ Fl{{project_name.pascalCase()}}Plugin* fl_{{project_name.snakeCase()}}_plugin_ne
self->registrar = FL_PLUGIN_REGISTRAR(g_object_ref(registrar));
- g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
- self->channel =
- fl_method_channel_new(fl_plugin_registrar_get_messenger(registrar),
- kChannelName, FL_METHOD_CODEC(codec));
- fl_method_channel_set_method_call_handler(self->channel, method_call_cb,
- g_object_ref(self), g_object_unref);
+ {{project_name.snakeCase()}}_{{project_name.snakeCase()}}_api_set_method_handlers(
+ fl_plugin_registrar_get_messenger(registrar), nullptr,
+ &api_vtable, g_object_ref(self), g_object_unref);
return self;
}
diff --git a/very_good_flutter_plugin/__brick__/{{#linux}}{{project_name.snakeCase()}}_linux{{/linux}}/pigeons/copyright.txt b/very_good_flutter_plugin/__brick__/{{#linux}}{{project_name.snakeCase()}}_linux{{/linux}}/pigeons/copyright.txt
new file mode 100644
index 00000000..00a4ecbb
--- /dev/null
+++ b/very_good_flutter_plugin/__brick__/{{#linux}}{{project_name.snakeCase()}}_linux{{/linux}}/pigeons/copyright.txt
@@ -0,0 +1 @@
+Copyright (c) {{currentYear}}, {{org_name.sentenceCase()}}
diff --git a/very_good_flutter_plugin/__brick__/{{#linux}}{{project_name.snakeCase()}}_linux{{/linux}}/pigeons/messages.dart b/very_good_flutter_plugin/__brick__/{{#linux}}{{project_name.snakeCase()}}_linux{{/linux}}/pigeons/messages.dart
new file mode 100644
index 00000000..949296c8
--- /dev/null
+++ b/very_good_flutter_plugin/__brick__/{{#linux}}{{project_name.snakeCase()}}_linux{{/linux}}/pigeons/messages.dart
@@ -0,0 +1,19 @@
+// {{project_name.pascalCase()}}Api must be abstract.
+// ignore_for_file: one_member_abstracts
+
+import 'package:pigeon/pigeon.dart';
+
+@ConfigurePigeon(
+ PigeonOptions(
+ dartOut: 'lib/src/messages.g.dart',
+ dartPackageName: '{{project_name.snakeCase()}}',
+ gobjectHeaderOut: 'linux/messages.g.h',
+ gobjectSourceOut: 'linux/messages.g.cc',
+ copyrightHeader: 'pigeons/copyright.txt',
+ ),
+)
+@HostApi()
+abstract class {{project_name.pascalCase()}}Api {
+ @async
+ String? getPlatformName();
+}
diff --git a/very_good_flutter_plugin/__brick__/{{#linux}}{{project_name.snakeCase()}}_linux{{/linux}}/pubspec.yaml b/very_good_flutter_plugin/__brick__/{{#linux}}{{project_name.snakeCase()}}_linux{{/linux}}/pubspec.yaml
index e2a1024c..f8e62602 100644
--- a/very_good_flutter_plugin/__brick__/{{#linux}}{{project_name.snakeCase()}}_linux{{/linux}}/pubspec.yaml
+++ b/very_good_flutter_plugin/__brick__/{{#linux}}{{project_name.snakeCase()}}_linux{{/linux}}/pubspec.yaml
@@ -23,4 +23,6 @@ dependencies:
dev_dependencies:
flutter_test:
sdk: flutter
+ mocktail: ^1.0.4
+ pigeon: ^26.0.2
very_good_analysis: ^10.2.0
diff --git a/very_good_flutter_plugin/__brick__/{{#linux}}{{project_name.snakeCase()}}_linux{{/linux}}/test/{{project_name.snakeCase()}}_linux_test.dart b/very_good_flutter_plugin/__brick__/{{#linux}}{{project_name.snakeCase()}}_linux{{/linux}}/test/{{project_name.snakeCase()}}_linux_test.dart
index b7b3f573..6f00fa79 100644
--- a/very_good_flutter_plugin/__brick__/{{#linux}}{{project_name.snakeCase()}}_linux{{/linux}}/test/{{project_name.snakeCase()}}_linux_test.dart
+++ b/very_good_flutter_plugin/__brick__/{{#linux}}{{project_name.snakeCase()}}_linux{{/linux}}/test/{{project_name.snakeCase()}}_linux_test.dart
@@ -1,32 +1,23 @@
-import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
+import 'package:mocktail/mocktail.dart';
+import 'package:{{project_name.snakeCase()}}_linux/src/messages.g.dart';
import 'package:{{project_name.snakeCase()}}_linux/{{project_name.snakeCase()}}_linux.dart';
import 'package:{{project_name.snakeCase()}}_platform_interface/{{project_name.snakeCase()}}_platform_interface.dart';
+class _Mock{{project_name.pascalCase()}}Api extends Mock
+ implements {{project_name.pascalCase()}}Api {}
+
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
- group('{{project_name.pascalCase()}}Linux', () {
+ group({{project_name.pascalCase()}}Linux, () {
const kPlatformName = 'Linux';
late {{project_name.pascalCase()}}Linux {{project_name.camelCase()}};
- late List log;
-
- setUp(() async {
- {{project_name.camelCase()}} = {{project_name.pascalCase()}}Linux();
-
- log = [];
- TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
- .setMockMethodCallHandler({{project_name.camelCase()}}.methodChannel, (
- methodCall,
- ) async {
- log.add(methodCall);
- switch (methodCall.method) {
- case 'getPlatformName':
- return kPlatformName;
- default:
- return null;
- }
- });
+ late {{project_name.pascalCase()}}Api api;
+
+ setUp(() {
+ api = _Mock{{project_name.pascalCase()}}Api();
+ {{project_name.camelCase()}} = {{project_name.pascalCase()}}Linux(api: api);
});
test('can be registered', () {
@@ -38,12 +29,14 @@ void main() {
});
test('getPlatformName returns correct name', () async {
- final name = await {{project_name.camelCase()}}.getPlatformName();
- expect(
- log,
- [isMethodCall('getPlatformName', arguments: null)],
+ when(api.getPlatformName).thenAnswer((_) async => kPlatformName);
+
+ await expectLater(
+ {{project_name.camelCase()}}.getPlatformName(),
+ completion(equals(kPlatformName)),
);
- expect(name, equals(kPlatformName));
+
+ verify(api.getPlatformName).called(1);
});
});
}
diff --git a/very_good_flutter_plugin/__brick__/{{#macos}}{{project_name.snakeCase()}}_macos{{/macos}}/analysis_options.yaml b/very_good_flutter_plugin/__brick__/{{#macos}}{{project_name.snakeCase()}}_macos{{/macos}}/analysis_options.yaml
index 9df80aa4..c5dcf888 100644
--- a/very_good_flutter_plugin/__brick__/{{#macos}}{{project_name.snakeCase()}}_macos{{/macos}}/analysis_options.yaml
+++ b/very_good_flutter_plugin/__brick__/{{#macos}}{{project_name.snakeCase()}}_macos{{/macos}}/analysis_options.yaml
@@ -1 +1,4 @@
include: package:very_good_analysis/analysis_options.yaml
+analyzer:
+ exclude:
+ - "**/*.g.dart"
diff --git a/very_good_flutter_plugin/__brick__/{{#macos}}{{project_name.snakeCase()}}_macos{{/macos}}/lib/{{project_name.snakeCase()}}_macos.dart b/very_good_flutter_plugin/__brick__/{{#macos}}{{project_name.snakeCase()}}_macos{{/macos}}/lib/{{project_name.snakeCase()}}_macos.dart
index c0e814a9..cc1e8353 100644
--- a/very_good_flutter_plugin/__brick__/{{#macos}}{{project_name.snakeCase()}}_macos{{/macos}}/lib/{{project_name.snakeCase()}}_macos.dart
+++ b/very_good_flutter_plugin/__brick__/{{#macos}}{{project_name.snakeCase()}}_macos{{/macos}}/lib/{{project_name.snakeCase()}}_macos.dart
@@ -1,15 +1,18 @@
import 'package:flutter/foundation.dart';
-import 'package:flutter/services.dart';
+import 'package:{{project_name.snakeCase()}}_macos/src/messages.g.dart';
import 'package:{{project_name.snakeCase()}}_platform_interface/{{project_name.snakeCase()}}_platform_interface.dart';
+/// {@template {{project_name.snakeCase()}}_macos}
/// The MacOS implementation of [{{project_name.pascalCase()}}Platform].
-class {{project_name.pascalCase()}}MacOS
- extends {{project_name.pascalCase()}}Platform {
- /// The method channel used to interact with the native platform.
- @visibleForTesting
- final methodChannel = const MethodChannel(
- '{{project_name.snakeCase()}}_macos',
- );
+/// {@endtemplate}
+class {{project_name.pascalCase()}}MacOS extends {{project_name.pascalCase()}}Platform {
+ /// {@macro {{project_name.snakeCase()}}_macos}
+ {{project_name.pascalCase()}}MacOS({
+ @visibleForTesting {{project_name.pascalCase()}}Api? api,
+ }) : api = api ?? {{project_name.pascalCase()}}Api();
+
+ /// The API used to interact with the native platform.
+ final {{project_name.pascalCase()}}Api api;
/// Registers this class as the default instance of
/// [{{project_name.pascalCase()}}Platform].
@@ -20,6 +23,6 @@ class {{project_name.pascalCase()}}MacOS
@override
Future getPlatformName() {
- return methodChannel.invokeMethod('getPlatformName');
+ return api.getPlatformName();
}
}
diff --git a/very_good_flutter_plugin/__brick__/{{#macos}}{{project_name.snakeCase()}}_macos{{/macos}}/macos/{{project_name.snakeCase()}}_macos/Sources/{{project_name.snakeCase()}}_macos/{{project_name.pascalCase()}}Plugin.swift b/very_good_flutter_plugin/__brick__/{{#macos}}{{project_name.snakeCase()}}_macos{{/macos}}/macos/{{project_name.snakeCase()}}_macos/Sources/{{project_name.snakeCase()}}_macos/{{project_name.pascalCase()}}Plugin.swift
index 81ad8c16..b9381994 100644
--- a/very_good_flutter_plugin/__brick__/{{#macos}}{{project_name.snakeCase()}}_macos{{/macos}}/macos/{{project_name.snakeCase()}}_macos/Sources/{{project_name.snakeCase()}}_macos/{{project_name.pascalCase()}}Plugin.swift
+++ b/very_good_flutter_plugin/__brick__/{{#macos}}{{project_name.snakeCase()}}_macos{{/macos}}/macos/{{project_name.snakeCase()}}_macos/Sources/{{project_name.snakeCase()}}_macos/{{project_name.pascalCase()}}Plugin.swift
@@ -1,14 +1,15 @@
import FlutterMacOS
import Foundation
-public class {{project_name.pascalCase()}}Plugin: NSObject, FlutterPlugin {
- public static func register(with registrar: FlutterPluginRegistrar) {
- let channel = FlutterMethodChannel(name: "{{project_name.snakeCase()}}_macos", binaryMessenger: registrar.messenger)
+public class {{project_name.pascalCase()}}Plugin: NSObject, FlutterPlugin, {{project_name.pascalCase()}}Api {
+ public static func register(with registrar: FlutterPluginRegistrar) {
+ let binaryMessenger = registrar.messenger
let instance = {{project_name.pascalCase()}}Plugin()
- registrar.addMethodCallDelegate(instance, channel: channel)
+ {{project_name.pascalCase()}}ApiSetup.setUp(binaryMessenger: binaryMessenger, api: instance)
+ registrar.publish(instance)
}
- public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
- result("macOS")
+ func getPlatformName(completion: @escaping (Result) -> Void) {
+ completion(.success("macOS"))
}
}
diff --git a/very_good_flutter_plugin/__brick__/{{#macos}}{{project_name.snakeCase()}}_macos{{/macos}}/pigeons/copyright.txt b/very_good_flutter_plugin/__brick__/{{#macos}}{{project_name.snakeCase()}}_macos{{/macos}}/pigeons/copyright.txt
new file mode 100644
index 00000000..00a4ecbb
--- /dev/null
+++ b/very_good_flutter_plugin/__brick__/{{#macos}}{{project_name.snakeCase()}}_macos{{/macos}}/pigeons/copyright.txt
@@ -0,0 +1 @@
+Copyright (c) {{currentYear}}, {{org_name.sentenceCase()}}
diff --git a/very_good_flutter_plugin/__brick__/{{#macos}}{{project_name.snakeCase()}}_macos{{/macos}}/pigeons/messages.dart b/very_good_flutter_plugin/__brick__/{{#macos}}{{project_name.snakeCase()}}_macos{{/macos}}/pigeons/messages.dart
new file mode 100644
index 00000000..3a9a53df
--- /dev/null
+++ b/very_good_flutter_plugin/__brick__/{{#macos}}{{project_name.snakeCase()}}_macos{{/macos}}/pigeons/messages.dart
@@ -0,0 +1,19 @@
+// {{project_name.pascalCase()}}Api must be abstract.
+// ignore_for_file: one_member_abstracts
+
+import 'package:pigeon/pigeon.dart';
+
+@ConfigurePigeon(
+ PigeonOptions(
+ dartOut: 'lib/src/messages.g.dart',
+ dartPackageName: '{{project_name.snakeCase()}}',
+ swiftOut: 'macos/{{project_name.snakeCase()}}_macos/Sources/{{project_name.snakeCase()}}_macos/Messages.g.swift',
+ swiftOptions: SwiftOptions(),
+ copyrightHeader: 'pigeons/copyright.txt',
+ ),
+)
+@HostApi()
+abstract class {{project_name.pascalCase()}}Api {
+ @async
+ String? getPlatformName();
+}
diff --git a/very_good_flutter_plugin/__brick__/{{#macos}}{{project_name.snakeCase()}}_macos{{/macos}}/pubspec.yaml b/very_good_flutter_plugin/__brick__/{{#macos}}{{project_name.snakeCase()}}_macos{{/macos}}/pubspec.yaml
index d470ff5f..52d5bf4f 100644
--- a/very_good_flutter_plugin/__brick__/{{#macos}}{{project_name.snakeCase()}}_macos{{/macos}}/pubspec.yaml
+++ b/very_good_flutter_plugin/__brick__/{{#macos}}{{project_name.snakeCase()}}_macos{{/macos}}/pubspec.yaml
@@ -23,4 +23,6 @@ dependencies:
dev_dependencies:
flutter_test:
sdk: flutter
+ mocktail: ^1.0.4
+ pigeon: ^26.0.2
very_good_analysis: ^10.2.0
diff --git a/very_good_flutter_plugin/__brick__/{{#macos}}{{project_name.snakeCase()}}_macos{{/macos}}/test/{{project_name.snakeCase()}}_macos_test.dart b/very_good_flutter_plugin/__brick__/{{#macos}}{{project_name.snakeCase()}}_macos{{/macos}}/test/{{project_name.snakeCase()}}_macos_test.dart
index ea675ef0..19cfaccd 100644
--- a/very_good_flutter_plugin/__brick__/{{#macos}}{{project_name.snakeCase()}}_macos{{/macos}}/test/{{project_name.snakeCase()}}_macos_test.dart
+++ b/very_good_flutter_plugin/__brick__/{{#macos}}{{project_name.snakeCase()}}_macos{{/macos}}/test/{{project_name.snakeCase()}}_macos_test.dart
@@ -1,32 +1,23 @@
-import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
+import 'package:mocktail/mocktail.dart';
+import 'package:{{project_name.snakeCase()}}_macos/src/messages.g.dart';
import 'package:{{project_name.snakeCase()}}_macos/{{project_name.snakeCase()}}_macos.dart';
import 'package:{{project_name.snakeCase()}}_platform_interface/{{project_name.snakeCase()}}_platform_interface.dart';
+class _Mock{{project_name.pascalCase()}}Api extends Mock
+ implements {{project_name.pascalCase()}}Api {}
+
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
- group('{{project_name.pascalCase()}}MacOS', () {
+ group({{project_name.pascalCase()}}MacOS, () {
const kPlatformName = 'MacOS';
late {{project_name.pascalCase()}}MacOS {{project_name.camelCase()}};
- late List log;
-
- setUp(() async {
- {{project_name.camelCase()}} = {{project_name.pascalCase()}}MacOS();
-
- log = [];
- TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
- .setMockMethodCallHandler({{project_name.camelCase()}}.methodChannel, (
- methodCall,
- ) async {
- log.add(methodCall);
- switch (methodCall.method) {
- case 'getPlatformName':
- return kPlatformName;
- default:
- return null;
- }
- });
+ late {{project_name.pascalCase()}}Api api;
+
+ setUp(() {
+ api = _Mock{{project_name.pascalCase()}}Api();
+ {{project_name.camelCase()}} = {{project_name.pascalCase()}}MacOS(api: api);
});
test('can be registered', () {
@@ -38,12 +29,14 @@ void main() {
});
test('getPlatformName returns correct name', () async {
- final name = await {{project_name.camelCase()}}.getPlatformName();
- expect(
- log,
- [isMethodCall('getPlatformName', arguments: null)],
+ when(api.getPlatformName).thenAnswer((_) async => kPlatformName);
+
+ await expectLater(
+ {{project_name.camelCase()}}.getPlatformName(),
+ completion(equals(kPlatformName)),
);
- expect(name, equals(kPlatformName));
+
+ verify(api.getPlatformName).called(1);
});
});
}
diff --git a/very_good_flutter_plugin/__brick__/{{#web}}{{project_name.snakeCase()}}_web{{/web}}/lib/{{project_name.snakeCase()}}_web.dart b/very_good_flutter_plugin/__brick__/{{#web}}{{project_name.snakeCase()}}_web{{/web}}/lib/{{project_name.snakeCase()}}_web.dart
index 61f08d8f..518443e2 100644
--- a/very_good_flutter_plugin/__brick__/{{#web}}{{project_name.snakeCase()}}_web{{/web}}/lib/{{project_name.snakeCase()}}_web.dart
+++ b/very_good_flutter_plugin/__brick__/{{#web}}{{project_name.snakeCase()}}_web{{/web}}/lib/{{project_name.snakeCase()}}_web.dart
@@ -1,8 +1,7 @@
import 'package:{{project_name.snakeCase()}}_platform_interface/{{project_name.snakeCase()}}_platform_interface.dart';
/// The Web implementation of [{{project_name.pascalCase()}}Platform].
-class {{project_name.pascalCase()}}Web
- extends {{project_name.pascalCase()}}Platform {
+class {{project_name.pascalCase()}}Web extends {{project_name.pascalCase()}}Platform {
/// Registers this class as the default instance of
/// [{{project_name.pascalCase()}}Platform].
static void registerWith([Object? registrar]) {
diff --git a/very_good_flutter_plugin/__brick__/{{#web}}{{project_name.snakeCase()}}_web{{/web}}/test/{{project_name.snakeCase()}}_web_test.dart b/very_good_flutter_plugin/__brick__/{{#web}}{{project_name.snakeCase()}}_web{{/web}}/test/{{project_name.snakeCase()}}_web_test.dart
index 3b5affb5..f3991e01 100644
--- a/very_good_flutter_plugin/__brick__/{{#web}}{{project_name.snakeCase()}}_web{{/web}}/test/{{project_name.snakeCase()}}_web_test.dart
+++ b/very_good_flutter_plugin/__brick__/{{#web}}{{project_name.snakeCase()}}_web{{/web}}/test/{{project_name.snakeCase()}}_web_test.dart
@@ -9,7 +9,7 @@ void main() {
const kPlatformName = 'Web';
late {{project_name.pascalCase()}}Web {{project_name.camelCase()}};
- setUp(() async {
+ setUp(() {
{{project_name.camelCase()}} = {{project_name.pascalCase()}}Web();
});
diff --git a/very_good_flutter_plugin/__brick__/{{#windows}}{{project_name.snakeCase()}}_windows{{/windows}}/analysis_options.yaml b/very_good_flutter_plugin/__brick__/{{#windows}}{{project_name.snakeCase()}}_windows{{/windows}}/analysis_options.yaml
index 9df80aa4..c5dcf888 100644
--- a/very_good_flutter_plugin/__brick__/{{#windows}}{{project_name.snakeCase()}}_windows{{/windows}}/analysis_options.yaml
+++ b/very_good_flutter_plugin/__brick__/{{#windows}}{{project_name.snakeCase()}}_windows{{/windows}}/analysis_options.yaml
@@ -1 +1,4 @@
include: package:very_good_analysis/analysis_options.yaml
+analyzer:
+ exclude:
+ - "**/*.g.dart"
diff --git a/very_good_flutter_plugin/__brick__/{{#windows}}{{project_name.snakeCase()}}_windows{{/windows}}/lib/{{project_name.snakeCase()}}_windows.dart b/very_good_flutter_plugin/__brick__/{{#windows}}{{project_name.snakeCase()}}_windows{{/windows}}/lib/{{project_name.snakeCase()}}_windows.dart
index 46a5cc82..87a69847 100644
--- a/very_good_flutter_plugin/__brick__/{{#windows}}{{project_name.snakeCase()}}_windows{{/windows}}/lib/{{project_name.snakeCase()}}_windows.dart
+++ b/very_good_flutter_plugin/__brick__/{{#windows}}{{project_name.snakeCase()}}_windows{{/windows}}/lib/{{project_name.snakeCase()}}_windows.dart
@@ -1,15 +1,18 @@
import 'package:flutter/foundation.dart';
-import 'package:flutter/services.dart';
+import 'package:{{project_name.snakeCase()}}_windows/src/messages.g.dart';
import 'package:{{project_name.snakeCase()}}_platform_interface/{{project_name.snakeCase()}}_platform_interface.dart';
+/// {@template {{project_name.snakeCase()}}_windows}
/// The Windows implementation of [{{project_name.pascalCase()}}Platform].
-class {{project_name.pascalCase()}}Windows
- extends {{project_name.pascalCase()}}Platform {
- /// The method channel used to interact with the native platform.
- @visibleForTesting
- final methodChannel = const MethodChannel(
- '{{project_name.snakeCase()}}_windows',
- );
+/// {@endtemplate}
+class {{project_name.pascalCase()}}Windows extends {{project_name.pascalCase()}}Platform {
+ /// {@macro {{project_name.snakeCase()}}_windows}
+ {{project_name.pascalCase()}}Windows({
+ @visibleForTesting {{project_name.pascalCase()}}Api? api,
+ }) : api = api ?? {{project_name.pascalCase()}}Api();
+
+ /// The API used to interact with the native platform.
+ final {{project_name.pascalCase()}}Api api;
/// Registers this class as the default instance of
/// [{{project_name.pascalCase()}}Platform].
@@ -20,6 +23,6 @@ class {{project_name.pascalCase()}}Windows
@override
Future getPlatformName() {
- return methodChannel.invokeMethod('getPlatformName');
+ return api.getPlatformName();
}
}
diff --git a/very_good_flutter_plugin/__brick__/{{#windows}}{{project_name.snakeCase()}}_windows{{/windows}}/pigeons/copyright.txt b/very_good_flutter_plugin/__brick__/{{#windows}}{{project_name.snakeCase()}}_windows{{/windows}}/pigeons/copyright.txt
new file mode 100644
index 00000000..00a4ecbb
--- /dev/null
+++ b/very_good_flutter_plugin/__brick__/{{#windows}}{{project_name.snakeCase()}}_windows{{/windows}}/pigeons/copyright.txt
@@ -0,0 +1 @@
+Copyright (c) {{currentYear}}, {{org_name.sentenceCase()}}
diff --git a/very_good_flutter_plugin/__brick__/{{#windows}}{{project_name.snakeCase()}}_windows{{/windows}}/pigeons/messages.dart b/very_good_flutter_plugin/__brick__/{{#windows}}{{project_name.snakeCase()}}_windows{{/windows}}/pigeons/messages.dart
new file mode 100644
index 00000000..f14c738d
--- /dev/null
+++ b/very_good_flutter_plugin/__brick__/{{#windows}}{{project_name.snakeCase()}}_windows{{/windows}}/pigeons/messages.dart
@@ -0,0 +1,20 @@
+// {{project_name.pascalCase()}}Api must be abstract.
+// ignore_for_file: one_member_abstracts
+
+import 'package:pigeon/pigeon.dart';
+
+@ConfigurePigeon(
+ PigeonOptions(
+ dartOut: 'lib/src/messages.g.dart',
+ dartPackageName: '{{project_name.snakeCase()}}',
+ cppOptions: CppOptions(namespace: '{{project_name.snakeCase()}}'),
+ cppHeaderOut: 'windows/include/{{project_name.snakeCase()}}_windows/messages.g.h',
+ cppSourceOut: 'windows/messages.g.cpp',
+ copyrightHeader: 'pigeons/copyright.txt',
+ ),
+)
+@HostApi()
+abstract class {{project_name.pascalCase()}}Api {
+ @async
+ String? getPlatformName();
+}
diff --git a/very_good_flutter_plugin/__brick__/{{#windows}}{{project_name.snakeCase()}}_windows{{/windows}}/pubspec.yaml b/very_good_flutter_plugin/__brick__/{{#windows}}{{project_name.snakeCase()}}_windows{{/windows}}/pubspec.yaml
index c98854ec..4c4d6e20 100644
--- a/very_good_flutter_plugin/__brick__/{{#windows}}{{project_name.snakeCase()}}_windows{{/windows}}/pubspec.yaml
+++ b/very_good_flutter_plugin/__brick__/{{#windows}}{{project_name.snakeCase()}}_windows{{/windows}}/pubspec.yaml
@@ -23,4 +23,6 @@ dependencies:
dev_dependencies:
flutter_test:
sdk: flutter
+ mocktail: ^1.0.4
+ pigeon: ^26.0.2
very_good_analysis: ^10.2.0
diff --git a/very_good_flutter_plugin/__brick__/{{#windows}}{{project_name.snakeCase()}}_windows{{/windows}}/test/{{project_name.snakeCase()}}_windows_test.dart b/very_good_flutter_plugin/__brick__/{{#windows}}{{project_name.snakeCase()}}_windows{{/windows}}/test/{{project_name.snakeCase()}}_windows_test.dart
index 7a324c8b..2ddb96e1 100644
--- a/very_good_flutter_plugin/__brick__/{{#windows}}{{project_name.snakeCase()}}_windows{{/windows}}/test/{{project_name.snakeCase()}}_windows_test.dart
+++ b/very_good_flutter_plugin/__brick__/{{#windows}}{{project_name.snakeCase()}}_windows{{/windows}}/test/{{project_name.snakeCase()}}_windows_test.dart
@@ -1,32 +1,23 @@
-import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
+import 'package:mocktail/mocktail.dart';
+import 'package:{{project_name.snakeCase()}}_windows/src/messages.g.dart';
import 'package:{{project_name.snakeCase()}}_platform_interface/{{project_name.snakeCase()}}_platform_interface.dart';
import 'package:{{project_name.snakeCase()}}_windows/{{project_name.snakeCase()}}_windows.dart';
+class _Mock{{project_name.pascalCase()}}Api extends Mock
+ implements {{project_name.pascalCase()}}Api {}
+
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
- group('{{project_name.pascalCase()}}Windows', () {
+ group({{project_name.pascalCase()}}Windows, () {
const kPlatformName = 'Windows';
late {{project_name.pascalCase()}}Windows {{project_name.camelCase()}};
- late List log;
-
- setUp(() async {
- {{project_name.camelCase()}} = {{project_name.pascalCase()}}Windows();
-
- log = [];
- TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
- .setMockMethodCallHandler({{project_name.camelCase()}}.methodChannel, (
- methodCall,
- ) async {
- log.add(methodCall);
- switch (methodCall.method) {
- case 'getPlatformName':
- return kPlatformName;
- default:
- return null;
- }
- });
+ late {{project_name.pascalCase()}}Api api;
+
+ setUp(() {
+ api = _Mock{{project_name.pascalCase()}}Api();
+ {{project_name.camelCase()}} = {{project_name.pascalCase()}}Windows(api: api);
});
test('can be registered', () {
@@ -38,12 +29,14 @@ void main() {
});
test('getPlatformName returns correct name', () async {
- final name = await {{project_name.camelCase()}}.getPlatformName();
- expect(
- log,
- [isMethodCall('getPlatformName', arguments: null)],
+ when(api.getPlatformName).thenAnswer((_) async => kPlatformName);
+
+ await expectLater(
+ {{project_name.camelCase()}}.getPlatformName(),
+ completion(equals(kPlatformName)),
);
- expect(name, equals(kPlatformName));
+
+ verify(api.getPlatformName).called(1);
});
});
}
diff --git a/very_good_flutter_plugin/__brick__/{{project_name.snakeCase()}}_platform_interface/test/src/method_channel_{{project_name.snakeCase()}}_test.dart b/very_good_flutter_plugin/__brick__/{{project_name.snakeCase()}}_platform_interface/test/src/method_channel_{{project_name.snakeCase()}}_test.dart
index a49c4ac3..90df9ad8 100644
--- a/very_good_flutter_plugin/__brick__/{{project_name.snakeCase()}}_platform_interface/test/src/method_channel_{{project_name.snakeCase()}}_test.dart
+++ b/very_good_flutter_plugin/__brick__/{{project_name.snakeCase()}}_platform_interface/test/src/method_channel_{{project_name.snakeCase()}}_test.dart
@@ -11,9 +11,8 @@ void main() {
methodChannel{{project_name.pascalCase()}};
final log = [];
- setUp(() async {
- methodChannel{{project_name.pascalCase()}} =
- MethodChannel{{project_name.pascalCase()}}();
+ setUp(() {
+ methodChannel{{project_name.pascalCase()}} = MethodChannel{{project_name.pascalCase()}}();
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(
methodChannel{{project_name.pascalCase()}}.methodChannel,
diff --git a/very_good_flutter_plugin/hooks/lib/src/cli/dart_cli.dart b/very_good_flutter_plugin/hooks/lib/src/cli/dart_cli.dart
index 06e69af4..f0cc1f66 100644
--- a/very_good_flutter_plugin/hooks/lib/src/cli/dart_cli.dart
+++ b/very_good_flutter_plugin/hooks/lib/src/cli/dart_cli.dart
@@ -28,12 +28,16 @@ class DartCli {
}
/// Idiomatically format Dart source code.
- Future format({required Logger logger, String cwd = '.'}) async {
+ Future format({
+ required Logger logger,
+ String cwd = '.',
+ String path = '.',
+ }) async {
await CommandLine.run(
_executableName,
- ['format'],
- workingDirectory: cwd,
+ ['format', path],
logger: logger,
+ workingDirectory: cwd,
);
}
@@ -48,8 +52,23 @@ class DartCli {
await CommandLine.run(
_executableName,
['fix', if (apply) '--apply'],
+ logger: logger,
workingDirectory: cwd,
+ );
+ }
+
+ /// Run a command.
+ Future run({
+ required Logger logger,
+ required String command,
+ List args = const [],
+ String cwd = '.',
+ }) async {
+ await CommandLine.run(
+ _executableName,
+ ['run', command, ...args],
logger: logger,
+ workingDirectory: cwd,
);
}
}
diff --git a/very_good_flutter_plugin/hooks/lib/version.dart b/very_good_flutter_plugin/hooks/lib/version.dart
index 2e8d8cf9..740c5fe1 100644
--- a/very_good_flutter_plugin/hooks/lib/version.dart
+++ b/very_good_flutter_plugin/hooks/lib/version.dart
@@ -20,3 +20,25 @@ const $flutterVersion = '3.41.1';
///
/// * [Flutter SDK archive](https://docs.flutter.dev/release/archive)
const $minDartVersion = '3.11.0';
+
+/// The available platforms that can be generated by the template.
+const $availablePlatforms = [
+ 'android',
+ 'ios',
+ 'macos',
+ 'linux',
+ 'web',
+ 'windows',
+];
+
+/// The platforms that use Pigeon for native bindings.
+///
+/// Web is excluded because it does not use native code and therefore does not
+/// require Pigeon code generation.
+const $pigeonPlatforms = [
+ 'android',
+ 'ios',
+ 'macos',
+ 'linux',
+ 'windows',
+];
diff --git a/very_good_flutter_plugin/hooks/post_gen.dart b/very_good_flutter_plugin/hooks/post_gen.dart
index 017a7de1..094c399e 100644
--- a/very_good_flutter_plugin/hooks/post_gen.dart
+++ b/very_good_flutter_plugin/hooks/post_gen.dart
@@ -3,29 +3,16 @@ import 'dart:io';
import 'package:mason/mason.dart';
import 'package:meta/meta.dart';
import 'package:very_good_flutter_plugin_hooks/src/cli/cli.dart';
+import 'package:very_good_flutter_plugin_hooks/version.dart';
+
+/// The key for the `projectName` context variable.
+@visibleForTesting
+const projectNameVariableKey = 'project_name';
/// The key for the `dartFixOutput` context variable.
@visibleForTesting
const dartFixOutputVariableKey = 'dart_fix_output';
-Future run(
- HookContext context, {
- @visibleForTesting VeryGoodCli veryGoodCli = VeryGoodCli.instance,
- @visibleForTesting DartCli dartCli = DartCli.instance,
-}) async {
- final dartFixOutput =
- context.vars.containsKey(dartFixOutputVariableKey) &&
- context.vars[dartFixOutputVariableKey] as bool;
- if (dartFixOutput) {
- await _dartFixOutput(
- logger: context.logger,
- workingDirectory: Directory.current.path,
- veryGoodCli: veryGoodCli,
- dartCli: dartCli,
- );
- }
-}
-
/// Attempts to `dart` fix and format the output.
///
/// Since the template includes Dart files with templated variables, generating
@@ -41,37 +28,88 @@ Future run(
///
/// If the [DartCli] or [VeryGoodCli] is not installed, this function will log
/// a warning and return immediately.
-Future _dartFixOutput({
- required Logger logger,
- required String workingDirectory,
- required VeryGoodCli veryGoodCli,
- required DartCli dartCli,
+Future run(
+ HookContext context, {
+ @visibleForTesting DartCli dartCli = DartCli.instance,
+ @visibleForTesting VeryGoodCli veryGoodCli = VeryGoodCli.instance,
}) async {
+ final logger = context.logger;
+ final cwd = Directory.current.path;
+
if (!await dartCli.isInstalled(logger: logger)) {
- logger.warn('''Could not fix output because Dart CLI is not installed.''');
- return;
+ return logger.warn(
+ '''Could not fix output because Dart CLI is not installed.''',
+ );
}
+
if (!await veryGoodCli.isInstalled(logger: logger)) {
- logger.warn(
+ return logger.warn(
'''Could not fix output because Very Good CLI is not installed.''',
);
- return;
}
- try {
- await veryGoodCli.packagesGet(
+ final progress = logger.progress('Getting dependencies ๐ฆ');
+
+ await _wrapWithProcessError(
+ () => veryGoodCli.packagesGet(
+ cwd: cwd,
logger: logger,
recursive: true,
- cwd: workingDirectory,
+ ),
+ logger: logger,
+ );
+
+ final projectName = context.vars[projectNameVariableKey] as String;
+ final directories = $pigeonPlatforms
+ .where((platform) => context.vars[platform] as bool)
+ .map((platform) => '$cwd/${projectName}_$platform');
+
+ progress.update('Generating Pigeon bindings ๐ฆพ');
+
+ await Future.wait(
+ directories.map(
+ (directory) => _wrapWithProcessError(
+ () => dartCli.run(
+ cwd: directory,
+ logger: logger,
+ command: 'pigeon',
+ args: ['--input', 'pigeons/messages.dart'],
+ ),
+ logger: logger,
+ ),
+ ),
+ );
+
+ final dartFixOutput =
+ context.vars.containsKey(dartFixOutputVariableKey) &&
+ context.vars[dartFixOutputVariableKey] as bool;
+
+ if (dartFixOutput) {
+ progress.update('Fixing Dart imports ordering ๐จ');
+ await _wrapWithProcessError(
+ () async {
+ await dartCli.fix(logger: logger, cwd: cwd, apply: true);
+ await dartCli.format(logger: logger, cwd: cwd);
+ },
+ logger: logger,
);
- await dartCli.fix(logger: logger, apply: true, cwd: workingDirectory);
- await dartCli.format(logger: logger, cwd: workingDirectory);
+ }
+
+ progress.complete('Completed post generation โ
');
+}
+
+/// Wraps a process in a try-catch block and logs any errors.
+Future _wrapWithProcessError(
+ Future Function() process, {
+ required Logger logger,
+}) async {
+ try {
+ await process();
} on ProcessException catch (e) {
- logger.err('''
-Running process ${e.executable} with ${e.arguments} failed:
-${e.message}
-''');
+ logger.err(
+ '''\n\nRunning process ${e.executable} with ${e.arguments} failed: ${e.message}''',
+ );
} on Exception catch (e) {
- logger.err('Unknown error occurred when fixing output: $e');
+ logger.err('Unknown error occurred: $e');
}
}
diff --git a/very_good_flutter_plugin/hooks/pre_gen.dart b/very_good_flutter_plugin/hooks/pre_gen.dart
index d6555eb6..01df3725 100644
--- a/very_good_flutter_plugin/hooks/pre_gen.dart
+++ b/very_good_flutter_plugin/hooks/pre_gen.dart
@@ -3,15 +3,6 @@ import 'package:mason/mason.dart';
import 'package:very_good_flutter_plugin_hooks/version.dart';
void run(HookContext context) {
- const availablePlatforms = [
- 'android',
- 'ios',
- 'macos',
- 'linux',
- 'web',
- 'windows',
- ];
-
final selectedPlatformsVar = context.vars['platforms'] as Object?;
final selectedPlatforms = switch (selectedPlatformsVar) {
@@ -28,7 +19,8 @@ void run(HookContext context) {
'current_year': clock.now().year.toString(),
'flutterVersion': $flutterVersion,
'dartSdkVersionBounds': '^${$minDartVersion}',
- for (final platform in availablePlatforms)
+ 'currentYear': DateTime.now().year.toString(),
+ for (final platform in $availablePlatforms)
platform: selectedPlatforms.contains(platform),
});
}
diff --git a/very_good_flutter_plugin/hooks/test/cli/dart_cli_test.dart b/very_good_flutter_plugin/hooks/test/cli/dart_cli_test.dart
index b5c8697b..d9eb4059 100644
--- a/very_good_flutter_plugin/hooks/test/cli/dart_cli_test.dart
+++ b/very_good_flutter_plugin/hooks/test/cli/dart_cli_test.dart
@@ -21,7 +21,7 @@ class _MockProcess extends Mock implements _TestProcess {}
class _MockLogger extends Mock implements Logger {}
void main() {
- group('$DartCli', () {
+ group(DartCli, () {
final processResult = ProcessResult(42, ExitCode.success.code, '', '');
late _TestProcess process;
late Logger logger;
@@ -78,14 +78,14 @@ void main() {
});
test('calls with given working directory', () async {
- await DartCli.instance.format(logger: logger, cwd: 'foo');
+ await DartCli.instance.format(logger: logger, path: 'foo');
verify(
() => process.run(
'dart',
- ['format'],
+ ['format', 'foo'],
runInShell: true,
- workingDirectory: 'foo',
+ workingDirectory: '.',
),
).called(1);
});
@@ -96,7 +96,7 @@ void main() {
verify(
() => process.run(
'dart',
- ['format'],
+ ['format', '.'],
runInShell: true,
workingDirectory: '.',
),
diff --git a/very_good_flutter_plugin/hooks/test/post_gen_test.dart b/very_good_flutter_plugin/hooks/test/post_gen_test.dart
index 0e08e95c..ab54bd1f 100644
--- a/very_good_flutter_plugin/hooks/test/post_gen_test.dart
+++ b/very_good_flutter_plugin/hooks/test/post_gen_test.dart
@@ -4,6 +4,7 @@ import 'package:mason/mason.dart';
import 'package:mocktail/mocktail.dart';
import 'package:test/test.dart';
import 'package:very_good_flutter_plugin_hooks/src/cli/cli.dart';
+import 'package:very_good_flutter_plugin_hooks/version.dart';
import '../post_gen.dart' as post_gen;
@@ -11,6 +12,8 @@ class _MockHookContext extends Mock implements HookContext {}
class _MockLogger extends Mock implements Logger {}
+class _MockProgress extends Mock implements Progress {}
+
class _MockDartCli extends Mock implements DartCli {}
class _MockVeryGoodCli extends Mock implements VeryGoodCli {}
@@ -19,200 +22,154 @@ void main() {
group('post gen', () {
late HookContext context;
late Logger logger;
+ late Progress progress;
+ late DartCli dartCli;
+ late VeryGoodCli veryGoodCli;
+
+ setUpAll(() {
+ registerFallbackValue('');
+ });
setUp(() {
context = _MockHookContext();
logger = _MockLogger();
+ progress = _MockProgress();
+ dartCli = _MockDartCli();
+ veryGoodCli = _MockVeryGoodCli();
when(() => context.vars).thenReturn({});
when(() => context.logger).thenReturn(logger);
- });
-
- group('dart fix output', () {
- late DartCli dartCli;
- late VeryGoodCli veryGoodCli;
-
- setUpAll(() {
- registerFallbackValue('');
+ when(
+ () => logger.progress(
+ any(),
+ options: any(named: 'options'),
+ ),
+ ).thenReturn(progress);
+
+ when(() => context.vars).thenReturn({
+ post_gen.dartFixOutputVariableKey: true,
+ post_gen.projectNameVariableKey: 'project_name',
+ for (final platform in $availablePlatforms) platform: true,
});
- setUp(() {
- dartCli = _MockDartCli();
- veryGoodCli = _MockVeryGoodCli();
-
- when(
- () => dartCli.isInstalled(logger: logger),
- ).thenAnswer((_) => Future.value(true));
-
- when(
- () => veryGoodCli.isInstalled(logger: logger),
- ).thenAnswer((_) => Future.value(true));
+ when(
+ () => dartCli.isInstalled(logger: logger),
+ ).thenAnswer((_) => Future.value(true));
+
+ when(
+ () => veryGoodCli.isInstalled(logger: logger),
+ ).thenAnswer((_) => Future.value(true));
+
+ when(
+ () => veryGoodCli.packagesGet(
+ logger: logger,
+ recursive: true,
+ cwd: any(named: 'cwd'),
+ ),
+ ).thenAnswer((_) async {});
+
+ when(
+ () => dartCli.fix(
+ logger: logger,
+ apply: true,
+ cwd: any(named: 'cwd'),
+ ),
+ ).thenAnswer((_) async {});
+
+ when(
+ () => dartCli.format(
+ logger: logger,
+ cwd: any(named: 'cwd'),
+ path: any(named: 'path'),
+ ),
+ ).thenAnswer((_) async {});
+
+ when(
+ () => dartCli.run(
+ logger: logger,
+ cwd: any(named: 'cwd'),
+ args: any(named: 'args'),
+ command: any(named: 'command'),
+ ),
+ ).thenAnswer((_) async {});
+ });
- when(
- () => context.vars,
- ).thenReturn({post_gen.dartFixOutputVariableKey: true});
+ test('runs when enabled', () async {
+ await post_gen.run(context, dartCli: dartCli, veryGoodCli: veryGoodCli);
+
+ verify(
+ () => veryGoodCli.packagesGet(
+ logger: logger,
+ recursive: true,
+ cwd: any(named: 'cwd'),
+ ),
+ ).called(1);
+ verify(
+ () => dartCli.fix(
+ logger: logger,
+ apply: true,
+ cwd: any(named: 'cwd'),
+ ),
+ ).called(1);
+ verify(
+ () => dartCli.format(
+ logger: logger,
+ cwd: any(named: 'cwd'),
+ ),
+ ).called(1);
+ verify(
+ () => dartCli.run(
+ logger: logger,
+ cwd: any(named: 'cwd'),
+ args: any(named: 'args'),
+ command: any(named: 'command'),
+ ),
+ ).called($pigeonPlatforms.length);
+ });
+ group('warns', () {
+ test('if Dart CLI is not installed', () async {
when(
- () => veryGoodCli.packagesGet(
- logger: logger,
- recursive: true,
- cwd: any(named: 'cwd'),
- ),
- ).thenAnswer((_) async {});
- when(
- () => dartCli.fix(
- logger: logger,
- apply: true,
- cwd: any(named: 'cwd'),
- ),
- ).thenAnswer((_) async {});
- when(
- () => dartCli.format(
- logger: logger,
- cwd: any(named: 'cwd'),
- ),
- ).thenAnswer((_) async {});
- });
+ () => dartCli.isInstalled(logger: logger),
+ ).thenAnswer((_) => Future.value(false));
- test('runs when enabled', () async {
- await post_gen.run(context, dartCli: dartCli, veryGoodCli: veryGoodCli);
+ await post_gen.run(context, dartCli: dartCli);
verify(
- () => veryGoodCli.packagesGet(
- logger: logger,
- recursive: true,
- cwd: any(named: 'cwd'),
- ),
- ).called(1);
- verify(
- () => dartCli.fix(
- logger: logger,
- apply: true,
- cwd: any(named: 'cwd'),
- ),
- ).called(1);
- verify(
- () => dartCli.format(
- logger: logger,
- cwd: any(named: 'cwd'),
+ () => logger.warn(
+ 'Could not fix output because Dart CLI is not installed.',
),
).called(1);
});
- test('does not run when disabled', () async {
+ test('if Very Good CLI is not installed', () async {
when(
- () => context.vars,
- ).thenReturn({post_gen.dartFixOutputVariableKey: false});
-
- await post_gen.run(context, veryGoodCli: veryGoodCli, dartCli: dartCli);
-
- verifyZeroInteractions(dartCli);
- verifyZeroInteractions(veryGoodCli);
- });
-
- test('does not run when no key exists', () async {
- when(() => context.vars).thenReturn({});
-
- await post_gen.run(context, veryGoodCli: veryGoodCli, dartCli: dartCli);
-
- verifyZeroInteractions(dartCli);
- verifyZeroInteractions(veryGoodCli);
- });
-
- group('warns', () {
- test('if Dart CLI is not installed', () async {
- when(
- () => dartCli.isInstalled(logger: logger),
- ).thenAnswer((_) => Future.value(false));
-
- await post_gen.run(context, dartCli: dartCli);
-
- verify(
- () => logger.warn(
- 'Could not fix output because Dart CLI is not installed.',
- ),
- ).called(1);
- });
-
- test('if Very Good CLI is not installed', () async {
- when(
- () => veryGoodCli.isInstalled(logger: logger),
- ).thenAnswer((_) => Future.value(false));
-
- await post_gen.run(
- context,
- dartCli: dartCli,
- veryGoodCli: veryGoodCli,
- );
-
- verify(
- () => logger.warn(
- 'Could not fix output because Very Good CLI is not installed.',
- ),
- ).called(1);
- });
- });
+ () => veryGoodCli.isInstalled(logger: logger),
+ ).thenAnswer((_) => Future.value(false));
- group('errors', () {
- test(
- 'if a $ProcessException is thrown by VeryGoodCLI.packagesGet',
- () async {
- const exception = ProcessException('executable', ['arguments']);
- when(
- () => veryGoodCli.packagesGet(
- logger: logger,
- recursive: true,
- cwd: any(named: 'cwd'),
- ),
- ).thenAnswer((_) => Future.error(exception));
-
- await post_gen.run(
- context,
- dartCli: dartCli,
- veryGoodCli: veryGoodCli,
- );
-
- verify(
- () => logger.err('''
-Running process ${exception.executable} with ${exception.arguments} failed:
-${exception.message}
-'''),
- ).called(1);
- },
+ await post_gen.run(
+ context,
+ dartCli: dartCli,
+ veryGoodCli: veryGoodCli,
);
- test(
- 'if an unknown error is thrown by VeryGoodCLI.packagesGet',
- () async {
- final exception = Exception('error');
- when(
- () => veryGoodCli.packagesGet(
- logger: logger,
- recursive: true,
- cwd: any(named: 'cwd'),
- ),
- ).thenAnswer((_) => Future.error(exception));
-
- await post_gen.run(
- context,
- dartCli: dartCli,
- veryGoodCli: veryGoodCli,
- );
-
- verify(
- () => logger.err(
- '''Unknown error occurred when fixing output: $exception''',
- ),
- ).called(1);
- },
- );
+ verify(
+ () => logger.warn(
+ 'Could not fix output because Very Good CLI is not installed.',
+ ),
+ ).called(1);
+ });
+ });
- test('if a $ProcessException is thrown by DartCli.fix', () async {
+ group('errors', () {
+ test(
+ 'if a $ProcessException is thrown by VeryGoodCLI.packagesGet',
+ () async {
const exception = ProcessException('executable', ['arguments']);
when(
- () => dartCli.fix(
+ () => veryGoodCli.packagesGet(
logger: logger,
- apply: true,
+ recursive: true,
cwd: any(named: 'cwd'),
),
).thenAnswer((_) => Future.error(exception));
@@ -224,19 +181,21 @@ ${exception.message}
);
verify(
- () => logger.err('''
-Running process ${exception.executable} with ${exception.arguments} failed:
-${exception.message}
-'''),
+ () => logger.err(
+ '''\n\nRunning process ${exception.executable} with ${exception.arguments} failed: ${exception.message}''',
+ ),
).called(1);
- });
+ },
+ );
- test('if an unknown error is thrown by DartCli.fix', () async {
+ test(
+ 'if an unknown error is thrown by VeryGoodCLI.packagesGet',
+ () async {
final exception = Exception('error');
when(
- () => dartCli.fix(
+ () => veryGoodCli.packagesGet(
logger: logger,
- apply: true,
+ recursive: true,
cwd: any(named: 'cwd'),
),
).thenAnswer((_) => Future.error(exception));
@@ -248,56 +207,148 @@ ${exception.message}
);
verify(
- () => logger.err(
- '''Unknown error occurred when fixing output: $exception''',
- ),
+ () => logger.err('Unknown error occurred: $exception'),
).called(1);
+ },
+ );
+ });
+
+ group('dart fix output', () {
+ test('does not run when disabled', () async {
+ when(() => context.vars).thenReturn({
+ post_gen.dartFixOutputVariableKey: false,
+ post_gen.projectNameVariableKey: 'project_name',
+ for (final platform in $availablePlatforms) platform: true,
});
- test('if a $ProcessException is thrown by DartCli.format', () async {
- const exception = ProcessException('executable', ['arguments']);
- when(
- () => dartCli.format(
- logger: logger,
- cwd: any(named: 'cwd'),
- ),
- ).thenAnswer((_) => Future.error(exception));
+ await post_gen.run(context, veryGoodCli: veryGoodCli, dartCli: dartCli);
- await post_gen.run(
- context,
- dartCli: dartCli,
- veryGoodCli: veryGoodCli,
- );
+ verifyNever(
+ () => dartCli.fix(
+ logger: logger,
+ apply: true,
+ cwd: any(named: 'cwd'),
+ ),
+ );
+ verifyNever(
+ () => dartCli.format(
+ logger: logger,
+ cwd: any(named: 'cwd'),
+ path: any(named: 'path'),
+ ),
+ );
+ });
- verify(
- () => logger.err('''
-Running process ${exception.executable} with ${exception.arguments} failed:
-${exception.message}
-'''),
- ).called(1);
+ test('does not run when no key exists', () async {
+ when(() => context.vars).thenReturn({
+ post_gen.projectNameVariableKey: 'project_name',
+ for (final platform in $availablePlatforms) platform: true,
});
- test('if an unknown error is thrown by DartCli.format', () async {
- final exception = Exception('error');
- when(
- () => dartCli.format(
- logger: logger,
- cwd: any(named: 'cwd'),
- ),
- ).thenAnswer((_) => Future.error(exception));
+ await post_gen.run(context, veryGoodCli: veryGoodCli, dartCli: dartCli);
- await post_gen.run(
- context,
- dartCli: dartCli,
- veryGoodCli: veryGoodCli,
- );
+ verifyNever(
+ () => dartCli.fix(
+ logger: logger,
+ apply: true,
+ cwd: any(named: 'cwd'),
+ ),
+ );
+ verifyNever(
+ () => dartCli.format(
+ logger: logger,
+ cwd: any(named: 'cwd'),
+ path: any(named: 'path'),
+ ),
+ );
+ });
- verify(
- () => logger.err(
- '''Unknown error occurred when fixing output: $exception''',
- ),
- ).called(1);
- });
+ test('if a $ProcessException is thrown by DartCli.fix', () async {
+ const exception = ProcessException('executable', ['arguments']);
+ when(
+ () => dartCli.fix(
+ logger: logger,
+ apply: true,
+ cwd: any(named: 'cwd'),
+ ),
+ ).thenAnswer((_) => Future.error(exception));
+
+ await post_gen.run(
+ context,
+ dartCli: dartCli,
+ veryGoodCli: veryGoodCli,
+ );
+
+ verify(
+ () => logger.err(
+ '''\n\nRunning process ${exception.executable} with ${exception.arguments} failed: ${exception.message}''',
+ ),
+ ).called(1);
+ });
+
+ test('if an unknown error is thrown by DartCli.fix', () async {
+ final exception = Exception('error');
+ when(
+ () => dartCli.fix(
+ logger: logger,
+ apply: true,
+ cwd: any(named: 'cwd'),
+ ),
+ ).thenAnswer((_) => Future.error(exception));
+
+ await post_gen.run(
+ context,
+ dartCli: dartCli,
+ veryGoodCli: veryGoodCli,
+ );
+
+ verify(
+ () => logger.err('Unknown error occurred: $exception'),
+ ).called(1);
+ });
+
+ test('if a $ProcessException is thrown by DartCli.format', () async {
+ const exception = ProcessException('executable', ['arguments']);
+ when(
+ () => dartCli.format(
+ logger: logger,
+ cwd: any(named: 'cwd'),
+ path: any(named: 'path'),
+ ),
+ ).thenAnswer((_) => Future.error(exception));
+
+ await post_gen.run(
+ context,
+ dartCli: dartCli,
+ veryGoodCli: veryGoodCli,
+ );
+
+ verify(
+ () => logger.err(
+ '''\n\nRunning process ${exception.executable} with ${exception.arguments} failed: ${exception.message}''',
+ ),
+ ).called(1);
+ });
+
+ test('if an unknown error is thrown by DartCli.format', () async {
+ final exception = Exception('error');
+ when(
+ () => dartCli.format(
+ logger: logger,
+ cwd: any(named: 'cwd'),
+ path: any(named: 'path'),
+ ),
+ ).thenAnswer((_) => Future.error(exception));
+
+ await post_gen.run(
+ context,
+ dartCli: dartCli,
+ veryGoodCli: veryGoodCli,
+ );
+
+ verify(
+ () => logger.err('Unknown error occurred: $exception'),
+ ).called(1);
});
});
});