[Pigeon] readme updates (#3705)

Updates README's to better reflect modern pigeon and it's usage. 
Very open to feedback on this, please let me know what could be better, clearer, or is just missing from this documentation.
I don't expect to be able to cover all uses with this, if people want to see all possibilities, I think looking into our very thorough integration tests will provide them with anything they could need (all of which is linked in the example README).

fixes https://github.com/flutter/flutter/issues/66511
partial work for https://github.com/flutter/flutter/issues/123851
fixes https://github.com/flutter/flutter/issues/108531
fixes https://github.com/flutter/flutter/issues/92641
diff --git a/packages/pigeon/CHANGELOG.md b/packages/pigeon/CHANGELOG.md
index 861b58d..addd59f 100644
--- a/packages/pigeon/CHANGELOG.md
+++ b/packages/pigeon/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 10.1.1
+
+* Updates README to better reflect modern usage.
+
 ## 10.1.0
 
 * [objc] Adds macOS support to facilitate code sharing with existing iOS plugins.
diff --git a/packages/pigeon/README.md b/packages/pigeon/README.md
index 525b626..c7810ae 100644
--- a/packages/pigeon/README.md
+++ b/packages/pigeon/README.md
@@ -1,33 +1,107 @@
 # Pigeon
 
 Pigeon is a code generator tool to make communication between Flutter and the
-host platform type-safe, easier and faster.
+host platform type-safe, easier, and faster.
 
-## Supported Platforms
+Pigeon removes the necessity to manage strings across multiple platforms and languages.
+It also improves efficiency over common method channel patterns. Most importantly though,
+it removes the need to write custom platform channel code, since pigeon generates it for you.
 
-Currently Pigeon supports generating:
+For usage examples, see the [Example README](./example/README.md).
+
+## Features
+
+### Supported Platforms
+
+Currently pigeon supports generating:
 * Kotlin and Java code for Android
 * Swift and Objective-C code for iOS and macOS
 * C++ code for Windows
 
-## Runtime Requirements
+### Supported Datatypes
 
-Pigeon generates all the code that is needed to communicate between Flutter and
-the host platform, there is no extra runtime requirement.  A plugin author
-doesn't need to worry about conflicting versions of Pigeon.
+Pigeon uses the `StandardMessageCodec` so it supports 
+[[any datatype platform channels support](https://flutter.dev/docs/development/platform-integration/platform-channels#codec)].
+
+Custom classes and nested datatypes are also supported.
+
+#### Enums
+
+Pigeon currently supports enum generation in class fields only.
+See issue: [87307](https://github.com/flutter/flutter/issues/87307).
+
+### Synchronous and Asynchronous methods
+
+While all calls across platform channel APIs (such as pigeon methods) are asynchronous,
+pigeon methods can be written on the native side as synchronous methods,
+to make it simpler to always reply exactly once.
+
+If asynchronous methods are needed, the `@async` annotation can be used. This will require 
+results or errors to be returned via a provided callback. [Example](./example/README.md#HostApi_Example).
+
+### Error Handling
+
+#### Kotlin, Java and Swift
+
+All Host API exceptions are translated into Flutter `PlatformException`.
+* For synchronous methods, thrown exceptions will be caught and translated.
+* For asynchronous methods, there is no default exception handling; errors
+should be returned via the provided callback.
+
+To pass custom details into `PlatformException` for error handling, 
+use `FlutterError` in your Host API. [Example](./example/README.md#HostApi_Example).
+
+To use `FlutterError` in Swift you must first extend a standard error.
+[Example](./example/README.md#AppDelegate.swift).
+
+#### Objective-C and C++
+
+Host API errors can be sent using the provided `FlutterError` class (translated into `PlatformException`).
+
+For synchronous methods:
+* Objective-C - Set the `error` argument to a `FlutterError` reference.
+* C++ - Return a `FlutterError`.
+
+For async methods:
+* Return a `FlutterError` through the provided callback.
+
+
+### Task Queue
+
+When targeting a Flutter version that supports the
+[TaskQueue API](https://docs.flutter.dev/development/platform-integration/platform-channels?tab=type-mappings-kotlin-tab#channels-and-platform-threading)
+the threading model for handling HostApi methods can be selected with the
+`TaskQueue` annotation.
 
 ## Usage
 
-1) Add Pigeon as a `dev_dependency`.
+1) Add pigeon as a `dev_dependency`.
 1) Make a ".dart" file outside of your "lib" directory for defining the
    communication interface.
 1) Run pigeon on your ".dart" file to generate the required Dart and
    host-language code: `flutter pub get` then `flutter pub run pigeon`
-   with suitable arguments (see [example](./example)).
+   with suitable arguments. [Example](./example/README.md#Invocation).
 1) Add the generated Dart code to `./lib` for compilation.
 1) Implement the host-language code and add it to your build (see below).
 1) Call the generated Dart methods.
 
+### Rules for defining your communication interface 
+[Example](./example/README.md#HostApi_Example)
+
+1) The file should contain no method or function definitions, only declarations.
+1) Custom classes used by APIs are defined as classes with fields of the
+   supported datatypes (see the supported Datatypes section).
+1) APIs should be defined as an `abstract class` with either `@HostApi()` or
+   `@FlutterApi()` as metadata.  `@HostApi()` being for procedures that are defined
+   on the host platform and the `@FlutterApi()` for procedures that are defined in Dart.
+1) Method declarations on the API classes should have arguments and a return
+   value whose types are defined in the file, are supported datatypes, or are
+   `void`.
+1) Generics are supported, but can currently only be used with nullable types
+   (example: `List<int?>`).
+1) Objc and Swift have special naming conventions that can be utilized with the
+   `@ObjCSelector` and `@SwiftFunction` respectively. 
+
 ### Flutter calling into iOS steps
 
 1) Add the generated Objective-C or Swift code to your Xcode project for compilation
@@ -58,214 +132,12 @@
 
 ### Calling into Flutter from the host platform
 
-Flutter also supports calling in the opposite direction.  The steps are similar
+Pigeon also supports calling in the opposite direction. The steps are similar
 but reversed.  For more information look at the annotation `@FlutterApi()` which
-denotes APIs that live in Flutter but are invoked from the host platform.
-
-### Rules for defining your communication interface
-
-1) The file should contain no method or function definitions, only declarations.
-1) Custom classes used by APIs are defined as classes with fields of the
-   supported datatypes (see the supported Datatypes section).
-1) APIs should be defined as an `abstract class` with either `HostApi()` or
-   `FlutterApi()` as metadata.  The former being for procedures that are defined
-   on the host platform and the latter for procedures that are defined in Dart.
-1) Method declarations on the API classes should have arguments and a return
-   value whose types are defined in the file, are supported datatypes, or are
-   `void`.
-1) Generics are supported, but can currently only be used with nullable types
-   (example: `List<int?>`).
-
-## Supported Datatypes
-
-Pigeon uses the `StandardMessageCodec` so it supports any datatype Platform
-Channels supports
-[[documentation](https://flutter.dev/docs/development/platform-integration/platform-channels#codec)].
-Nested datatypes are supported, too.
-
-## Features
-
-### Asynchronous Handlers
-
-By default Pigeon will generate synchronous handlers for messages and
-asynchronous methods. If you want a handler to be able to respond to a message
-asynchronously you can use the @async annotation as of version 0.1.20.
-
-Example:
-
-```dart
-class Value {
-  int? number;
-}
-
-@HostApi()
-abstract class Api2Host {
-  @async
-  Value calculate(Value value);
-}
-```
-
-Generates:
-
-```objc
-// Objective-C
-@protocol Api2Host
--(void)calculate:(nullable Value *)input
-      completion:(void(^)(Value *_Nullable, FlutterError *_Nullable))completion;
-@end
-```
-
-```swift
-// Swift
-
-/** Generated interface from Pigeon that represents a handler of messages from Flutter.*/
-protocol Api2Host {
-  func calculate(value: Value, completion: @escaping (Value) -> Void)
-}
-```
-
-```java
-// Java
-public interface Result<T> {
-   void success(T result);
-}
-
-/** Generated interface from Pigeon that represents a handler of messages from Flutter.*/
-public interface Api2Host {
-   void calculate(Value arg, Result<Value> result);
-}
-```
-
-```kotlin
-// Kotlin
-
-/** Generated interface from Pigeon that represents a handler of messages from Flutter.*/
-interface Api2Host {
-   fun calculate(value: Value, callback: (Result<Value>) -> Unit)
-}
-```
-
-```c++
-// C++
-
-/** Generated class from Pigeon that represents a handler of messages from Flutter.*/
-class Api2Host {
-public:
-    virtual void calculate(Value value, flutter::MessageReply<Value> result) = 0;
-}
-```
-
-### Null Safety (NNBD)
-
-Pigeon supports generating null-safe code, but it doesn't yet support:
-
-1) Nullable generics type arguments
-1) Nullable enum arguments to methods
-
-### Enums
-
-Pigeon supports enum generation in class fields.  For example:
-```dart
-enum State {
-  pending,
-  success,
-  error,
-}
-
-class StateResult {
-  String? errorMessage;
-  State? state;
-}
-
-@HostApi()
-abstract class Api {
-  StateResult queryState();
-}
-```
-
-### Primitive Data-types
-
-Prior to version 1.0 all arguments to API methods had to be wrapped in a class, now they can be used directly.  For example:
-
-```dart
-@HostApi()
-abstract class Api {
-   Map<String?, int?> makeMap(List<String?> keys, List<String?> values);
-}
-```
-
-### TaskQueues
-
-When targeting a Flutter version that supports the
-[TaskQueue API](https://docs.flutter.dev/development/platform-integration/platform-channels?tab=type-mappings-kotlin-tab#channels-and-platform-threading)
-the threading model for handling HostApi methods can be selected with the
-`TaskQueue` annotation:
-
-```dart
-@HostApi()
-abstract class Api2Host {
-  @TaskQueue(type: TaskQueueType.serialBackgroundThread)
-  int add(int x, int y);
-}
-```
-
-### Error Handling
-
-#### Kotlin, Java and Swift
-
-All Host API exceptions are translated into Flutter `PlatformException`.
-* For synchronous methods, thrown exceptions will be caught and translated.
-* For asynchronous methods, there is no default exception handling; errors should be returned via the provided callback.
-
-To pass custom details into `PlatformException` for error handling, use `FlutterError` in your Host API.
-For example:
-
-```kotlin
-// Kotlin
-class MyApi : GeneratedApi {
-  // For synchronous methods
-  override fun doSomething() {
-    throw FlutterError('error_code', 'message', 'details')
-  }
-
-  // For async methods
-  override fun doSomethingAsync(callback: (Result<Unit>) -> Unit) {
-    callback(Result.failure(FlutterError('error_code', 'message', 'details'))
-  }
-}
-```
-
-#### Objective-C and C++
-
-Likewise, Host API errors can be sent using the provided `FlutterError` class (translated into `PlatformException`).
-
-For synchronous methods:
-* Objective-C - Assign the `error` argument to a `FlutterError` reference.
-* C++ - Return a `FlutterError` directly (for void methods) or within an `ErrorOr` instance.
-
-For async methods:
-* Both - Return a `FlutterError` through the provided callback.
-
-#### Handling the errors
-
-Then you can implement error handling on the Flutter side:
-
-```dart
-// Dart
-void doSomething() {
-  try {
-    myApi.doSomething()
-  } catch (PlatformException e) {
-    if (e.code == 'error_code') {
-      // Perform custom error handling
-      assert(e.message == 'message')
-      assert(e.details == 'details')
-    }
-  }
-}
-```
+denotes APIs that live in Flutter but are invoked from the host platform. 
+[Example](./example/README.md#FlutterApi_Example).
 
 ## Feedback
 
-File an issue in [flutter/flutter](https://github.com/flutter/flutter) with the
-word "pigeon" in the title.
+File an issue in [flutter/flutter](https://github.com/flutter/flutter) with 
+"[pigeon]" at the start of the title.
diff --git a/packages/pigeon/example/README.md b/packages/pigeon/example/README.md
index c394219..a310f46 100644
--- a/packages/pigeon/example/README.md
+++ b/packages/pigeon/example/README.md
@@ -1,155 +1,279 @@
+<?code-excerpt path-base="excerpts/packages/pigeon_example"?>
 # Pigeon Examples
 
+The examples here will cover basic usage. For a more thorough set of examples,
+check the [core_tests pigeon file](../pigeons/core_tests.dart) and 
+[platform test folder](../platform_tests/) ([shared_test_plugin_code](../platform_tests/shared_test_plugin_code/) and [alternate_language_test_plugin](../platform_tests/alternate_language_test_plugin/) especially).
+
+## Invocation
+
+Begin by configuring pigeon at the top of the `.dart` input file.
+In actual use, you would include only the languages
+needed for your project.
+
+<?code-excerpt "../../app/pigeons/messages.dart (config)"?>
+```dart
+@ConfigurePigeon(PigeonOptions(
+  dartOut: 'lib/src/messages.g.dart',
+  dartOptions: DartOptions(),
+  cppOptions: CppOptions(namespace: 'pigeon_example'),
+  cppHeaderOut: 'windows/runner/messages.g.h',
+  cppSourceOut: 'windows/runner/messages.g.cpp',
+  kotlinOut:
+      'android/app/src/main/kotlin/dev/flutter/pigeon_example_app/Messages.g.kt',
+  kotlinOptions: KotlinOptions(),
+  javaOut: 'android/app/src/main/java/io/flutter/plugins/Messages.java',
+  javaOptions: JavaOptions(),
+  swiftOut: 'ios/Runner/Messages.g.swift',
+  swiftOptions: SwiftOptions(),
+  objcHeaderOut: 'macos/Runner/messages.g.h',
+  objcSourceOut: 'macos/Runner/messages.g.m',
+  // Set this to a unique prefix for your plugin or application, per Objective-C naming conventions.
+  objcOptions: ObjcOptions(prefix: 'PGN'),
+  copyrightHeader: 'pigeons/copyright.txt',
+))
+```
+Then make a simple call to run pigeon on the Dart file containing your definitions.
+
+```sh
+flutter pub run pigeon --input path/to/input.dart
+```
+
 ## HostApi Example
 
 This example gives an overview of how to use Pigeon to call into the
-host-platform from Flutter.
+host platform from Flutter.
 
-For instructions to set up your own Pigeon usage see these [steps](https://pub.dev/packages/pigeon#usage).
-
-### message.dart
+### Dart input
 
 This is the Pigeon file that describes the interface that will be used to call
 from Flutter to the host-platform.
 
+<?code-excerpt "../../app/pigeons/messages.dart (host-definitions)"?>
 ```dart
-import 'package:pigeon/pigeon.dart';
+enum Code { one, two }
 
-class Book {
-  String? title;
-  String? author;
+class MessageData {
+  MessageData({required this.code, required this.data});
+  String? name;
+  String? description;
+  Code code;
+  Map<String?, String?> data;
 }
 
 @HostApi()
-abstract class BookApi {
-  List<Book?> search(String keyword);
+abstract class ExampleHostApi {
+  String getHostLanguage();
+
+  // These annotations create more idiomatic naming of methods in Objc and Swift.
+  @ObjCSelector('addNumber:toNumber:')
+  @SwiftFunction('add(_:to:)')
+  int add(int a, int b);
+
+  @async
+  bool sendMessage(MessageData message);
 }
 ```
 
-### invocation
+### Dart
 
-This an example call to Pigeon that would ingest a defintion file
-`pigeons/message.dart` and generate corresponding output code for each
-supported language. (In actual use, you would not normally use both Objective-C
-and Swift, or both Java and Kotlin, but instead use just the languages matching
-your project.)
+This is the code that will use the generated Dart code to make calls from Flutter to 
+the host platform.
 
-```sh
-flutter pub run pigeon \
-  --input pigeons/message.dart \
-  --dart_out lib/pigeon.dart \
-  --objc_header_out ios/Runner/pigeon.h \
-  --objc_source_out ios/Runner/pigeon.m \
-  --swift_out ios/Runner/Pigeon.swift \
-  --kotlin_out android/app/src/main/kotlin/dev/flutter/pigeon/Pigeon.kt \
-  --kotlin_package "dev.flutter.pigeon" \
-  --java_out android/app/src/main/java/dev/flutter/pigeon/Pigeon.java \
-  --java_package "dev.flutter.pigeon" \
-  --cpp_header_out windows/runner/pigeon.h \
-  --cpp_source_out windows/runner/pigeon.cpp \
-  --cpp_namespace pigeon
+<?code-excerpt "../../app/lib/main.dart (main-dart)"?>
+```dart 
+final ExampleHostApi _api = ExampleHostApi();
+
+/// Calls host method `add` with provided arguments.
+Future<int> add(int a, int b) async {
+  try {
+    return await _api.add(a, b);
+  } catch (e) {
+    // handle error.
+    return 0;
+  }
+}
+
+/// Sends message through host api using `MessageData` class
+/// and api `sendMessage` method.
+Future<bool> sendMessage(String messageText) {
+  final MessageData message = MessageData(
+    code: Code.one,
+    data: <String?, String?>{'header': 'this is a header'},
+    description: 'uri text',
+  );
+  try {
+    return _api.sendMessage(message);
+  } catch (e) {
+    // handle error.
+    return Future<bool>(() => true);
+  }
+}
 ```
 
-### AppDelegate.m
-
-This is the code that will use the generated Objective-C code to receive calls
-from Flutter.
-
-```objc
-#import "AppDelegate.h"
-#import <Flutter/Flutter.h>
-#import "pigeon.h"
-
-@interface MyApi : NSObject <BookApi>
-@end
-
-@implementation MyApi
--(NSArray<Book *> *)searchKeyword:(NSString *)keyword error:(FlutterError **)error {
-  Book *result = [[Book alloc] init];
-  result.title =
-      [NSString stringWithFormat:@"%@'s Life", request.query];
-  result.author = keyword;
-  return @[ result ];
-}
-@end
-
-@implementation AppDelegate
-- (BOOL)application:(UIApplication *)application
-didFinishLaunchingWithOptions:(NSDictionary<UIApplicationLaunchOptionsKey, id> *)launchOptions {
-  MyApi *api = [[MyApi alloc] init];
-  BookApiSetup(getFlutterEngine().binaryMessenger, api);
-  return YES;
-}
-@end
-```
-
-### AppDelegate.swift
+### Swift
 
 This is the code that will use the generated Swift code to receive calls from Flutter.
-
+packages/pigeon/example/app/ios/Runner/AppDelegate.swift
+<?code-excerpt "../../app/ios/Runner/AppDelegate.swift (swift-class)"?>
 ```swift
-import Flutter
+// This extension of Error is required to do use FlutterError in any Swift code.
+extension FlutterError: Error {}
 
-class MyApi: NSObject, BookApi {
-  func search(keyword: String) -> [Book] {
-    let result = Book(title: "\(keyword)'s Life", author: keyword)
-    return [result]
+private class PigeonApiImplementation: ExampleHostApi {
+  func getHostLanguage() throws -> String {
+    return "Swift"
   }
-}
 
-class AppDelegate {
-  override func application(
-    _ application: UIApplication,
-    didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?
-  ) -> Bool {
-    let api = MyApi()
-    BookApiSetup.setUp(getFlutterEngine().binaryMessenger, api)
-    return true
+  func add(_ a: Int64, to b: Int64) throws -> Int64 {
+    if (a < 0 || b < 0) {
+      throw FlutterError(code: "code", message: "message", details: "details");
+    }
+    return a + b
+  }
+
+  func sendMessage(message: MessageData, completion: @escaping (Result<Bool, Error>) -> Void) {
+    if (message.code == Code.one) {
+      completion(.failure(FlutterError(code: "code", message: "message", details: "details")))
+      return
+    }
+    completion(.success(true))
   }
 }
 ```
 
-### StartActivity.java
+### Kotlin
+<?code-excerpt "../../app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/MainActivity.kt (kotlin-class)"?>
+```kotlin
+private class PigeonApiImplementation: ExampleHostApi {
+  override fun getHostLanguage(): String {
+    return "Kotlin"
+  }
 
-This is the code that will use the generated Java code to receive calls from Flutter.
+  override fun add(a: Long, b: Long): Long {
+    if (a < 0L || b < 0L) {
+      throw FlutterError("code", "message", "details");
+    }
+    return a + b
+  }
 
-```java
-import dev.flutter.pigeon.Pigeon.*;
-import java.util.Collections;
+  override fun sendMessage(message: MessageData, callback: (Result<Boolean>) -> Unit) {
+    if (message.code == Code.ONE) {
+      callback(Result.failure(FlutterError("code", "message", "details")))
+      return
+    }
+    callback(Result.success(true))
+  }
+}
+```
 
-public class StartActivity extends Activity {
-  private class MyApi implements BookApi {
-    List<Book> search(String keyword) {
-      Book result = new Book();
-      result.author = keyword;
-      result.title = String.format("%s's Life", keyword);
-      return Collections.singletonList(result)
+### C++
+<?code-excerpt "../../app/windows/runner/flutter_window.cpp (cpp-class)"?>
+```c++
+class PigeonApiImplementation : public ExampleHostApi {
+ public:
+  PigeonApiImplementation() {}
+  virtual ~PigeonApiImplementation() {}
+
+  ErrorOr<std::string> GetHostLanguage() override { return "C++"; }
+  ErrorOr<int64_t> Add(int64_t a, int64_t b) {
+    if (a < 0 || b < 0) {
+      return FlutterError("code", "message", "details");
+    }
+    return a + b;
+  }
+  void SendMessage(const MessageData& message,
+                   std::function<void(ErrorOr<bool> reply)> result) {
+    if (message.code == Code.one) {
+      result(FlutterError("code", "message", "details"));
+      return;
+    }
+    result(true);
+  }
+};
+```
+
+## FlutterApi Example
+
+This example gives an overview of how to use Pigeon to call into the Flutter
+app from the host platform.
+
+### Dart input
+
+<?code-excerpt "../../app/pigeons/messages.dart (flutter-definitions)"?>
+```dart
+@FlutterApi()
+abstract class MessageFlutterApi {
+  String flutterMethod(String? aString);
+}
+```
+
+### Dart
+
+This is the code that will use the generated Dart code to handle calls made to 
+Flutter from the host platform.
+
+<?code-excerpt "../../app/lib/main.dart (main-dart-flutter)"?>
+```dart 
+class _ExampleFlutterApi implements MessageFlutterApi {
+  @override
+  String flutterMethod(String? aString) {
+    return aString ?? '';
+  }
+}
+// ···
+  MessageFlutterApi.setup(_ExampleFlutterApi());
+```
+
+### Swift
+
+<?code-excerpt "../../app/ios/Runner/AppDelegate.swift (swift-class-flutter)"?>
+```swift
+private class PigeonFlutterApi {
+  var flutterAPI: MessageFlutterApi
+
+  init(binaryMessenger: FlutterBinaryMessenger) {
+    flutterAPI = MessageFlutterApi(binaryMessenger: binaryMessenger)
+  }
+
+  func callFlutterMethod(aString aStringArg: String?, completion: @escaping (Result<String, Error>) -> Void) {
+    flutterAPI.flutterMethod(aString: aStringArg) {
+      completion(.success($0))
     }
   }
+}
+```
 
-  protected void onCreate(Bundle savedInstanceState) {
-    super.onCreate(savedInstanceState);
-    BookApi.setup(getBinaryMessenger(), new MyApi());
+### Kotlin
+
+<?code-excerpt "../../app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/MainActivity.kt (kotlin-class-flutter)"?>
+```kotlin
+private class PigeonFlutterApi {
+
+  var flutterApi: MessageFlutterApi? = null
+
+  constructor(binding: FlutterPlugin.FlutterPluginBinding) {
+    flutterApi = MessageFlutterApi(binding.getBinaryMessenger())
+  }
+
+  fun callFlutterMethod(aString: String, callback: (Result<String>) -> Unit) {
+    flutterApi!!.flutterMethod(aString) {
+      echo -> callback(Result.success(echo))
+    }
   }
 }
 ```
 
-### test.dart
+### C++
 
-This is the Dart code that will call into the host-platform using the generated
-Dart code.
-
-```dart
-import 'pigeon.dart';
-
-void main() {
-  testWidgets("test pigeon", (WidgetTester tester) async {
-    Api api = Api();
-    List<Book?> reply = await api.search("Aaron");
-    expect(reply[0].title, equals("Aaron's Life"));
-  });
+<?code-excerpt "../../app/windows/runner/flutter_window.cpp (cpp-method-flutter)"?>
+```c++
+void TestPlugin::CallFlutterMethod(
+    String aString, std::function<void(ErrorOr<int64_t> reply)> result) {
+  MessageFlutterApi->FlutterMethod(
+      aString, [result](String echo) { result(echo); },
+      [result](const FlutterError& error) { result(error); });
 }
-
 ```
 
 ## Swift / Kotlin Plugin Example
diff --git a/packages/pigeon/example/app/android/app/src/main/java/io/flutter/plugins/Messages.java b/packages/pigeon/example/app/android/app/src/main/java/io/flutter/plugins/Messages.java
new file mode 100644
index 0000000..c1c3b7a
--- /dev/null
+++ b/packages/pigeon/example/app/android/app/src/main/java/io/flutter/plugins/Messages.java
@@ -0,0 +1,343 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+// Autogenerated from Pigeon (v10.1.1), do not edit directly.
+// See also: https://pub.dev/packages/pigeon
+
+import android.util.Log;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import io.flutter.plugin.common.BasicMessageChannel;
+import io.flutter.plugin.common.BinaryMessenger;
+import io.flutter.plugin.common.MessageCodec;
+import io.flutter.plugin.common.StandardMessageCodec;
+import java.io.ByteArrayOutputStream;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Map;
+
+/** Generated class from Pigeon. */
+@SuppressWarnings({"unused", "unchecked", "CodeBlock2Expr", "RedundantSuppression", "serial"})
+public class Messages {
+
+  /** Error class for passing custom error details to Flutter via a thrown PlatformException. */
+  public static class FlutterError extends RuntimeException {
+
+    /** The error code. */
+    public final String code;
+
+    /** The error details. Must be a datatype supported by the api codec. */
+    public final Object details;
+
+    public FlutterError(@NonNull String code, @Nullable String message, @Nullable Object details) {
+      super(message);
+      this.code = code;
+      this.details = details;
+    }
+  }
+
+  @NonNull
+  protected static ArrayList<Object> wrapError(@NonNull Throwable exception) {
+    ArrayList<Object> errorList = new ArrayList<Object>(3);
+    if (exception instanceof FlutterError) {
+      FlutterError error = (FlutterError) exception;
+      errorList.add(error.code);
+      errorList.add(error.getMessage());
+      errorList.add(error.details);
+    } else {
+      errorList.add(exception.toString());
+      errorList.add(exception.getClass().getSimpleName());
+      errorList.add(
+          "Cause: " + exception.getCause() + ", Stacktrace: " + Log.getStackTraceString(exception));
+    }
+    return errorList;
+  }
+
+  public enum Code {
+    ONE(0),
+    TWO(1);
+
+    final int index;
+
+    private Code(final int index) {
+      this.index = index;
+    }
+  }
+
+  /** Generated class from Pigeon that represents data sent in messages. */
+  public static final class MessageData {
+    private @Nullable String name;
+
+    public @Nullable String getName() {
+      return name;
+    }
+
+    public void setName(@Nullable String setterArg) {
+      this.name = setterArg;
+    }
+
+    private @Nullable String description;
+
+    public @Nullable String getDescription() {
+      return description;
+    }
+
+    public void setDescription(@Nullable String setterArg) {
+      this.description = setterArg;
+    }
+
+    private @NonNull Code code;
+
+    public @NonNull Code getCode() {
+      return code;
+    }
+
+    public void setCode(@NonNull Code setterArg) {
+      if (setterArg == null) {
+        throw new IllegalStateException("Nonnull field \"code\" is null.");
+      }
+      this.code = setterArg;
+    }
+
+    private @NonNull Map<String, String> data;
+
+    public @NonNull Map<String, String> getData() {
+      return data;
+    }
+
+    public void setData(@NonNull Map<String, String> setterArg) {
+      if (setterArg == null) {
+        throw new IllegalStateException("Nonnull field \"data\" is null.");
+      }
+      this.data = setterArg;
+    }
+
+    /** Constructor is non-public to enforce null safety; use Builder. */
+    MessageData() {}
+
+    public static final class Builder {
+
+      private @Nullable String name;
+
+      public @NonNull Builder setName(@Nullable String setterArg) {
+        this.name = setterArg;
+        return this;
+      }
+
+      private @Nullable String description;
+
+      public @NonNull Builder setDescription(@Nullable String setterArg) {
+        this.description = setterArg;
+        return this;
+      }
+
+      private @Nullable Code code;
+
+      public @NonNull Builder setCode(@NonNull Code setterArg) {
+        this.code = setterArg;
+        return this;
+      }
+
+      private @Nullable Map<String, String> data;
+
+      public @NonNull Builder setData(@NonNull Map<String, String> setterArg) {
+        this.data = setterArg;
+        return this;
+      }
+
+      public @NonNull MessageData build() {
+        MessageData pigeonReturn = new MessageData();
+        pigeonReturn.setName(name);
+        pigeonReturn.setDescription(description);
+        pigeonReturn.setCode(code);
+        pigeonReturn.setData(data);
+        return pigeonReturn;
+      }
+    }
+
+    @NonNull
+    ArrayList<Object> toList() {
+      ArrayList<Object> toListResult = new ArrayList<Object>(4);
+      toListResult.add(name);
+      toListResult.add(description);
+      toListResult.add(code == null ? null : code.index);
+      toListResult.add(data);
+      return toListResult;
+    }
+
+    static @NonNull MessageData fromList(@NonNull ArrayList<Object> list) {
+      MessageData pigeonResult = new MessageData();
+      Object name = list.get(0);
+      pigeonResult.setName((String) name);
+      Object description = list.get(1);
+      pigeonResult.setDescription((String) description);
+      Object code = list.get(2);
+      pigeonResult.setCode(code == null ? null : Code.values()[(int) code]);
+      Object data = list.get(3);
+      pigeonResult.setData((Map<String, String>) data);
+      return pigeonResult;
+    }
+  }
+
+  public interface Result<T> {
+    @SuppressWarnings("UnknownNullness")
+    void success(T result);
+
+    void error(@NonNull Throwable error);
+  }
+
+  private static class ExampleHostApiCodec extends StandardMessageCodec {
+    public static final ExampleHostApiCodec INSTANCE = new ExampleHostApiCodec();
+
+    private ExampleHostApiCodec() {}
+
+    @Override
+    protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) {
+      switch (type) {
+        case (byte) 128:
+          return MessageData.fromList((ArrayList<Object>) readValue(buffer));
+        default:
+          return super.readValueOfType(type, buffer);
+      }
+    }
+
+    @Override
+    protected void writeValue(@NonNull ByteArrayOutputStream stream, Object value) {
+      if (value instanceof MessageData) {
+        stream.write(128);
+        writeValue(stream, ((MessageData) value).toList());
+      } else {
+        super.writeValue(stream, value);
+      }
+    }
+  }
+
+  /** Generated interface from Pigeon that represents a handler of messages from Flutter. */
+  public interface ExampleHostApi {
+
+    @NonNull
+    String getHostLanguage();
+
+    @NonNull
+    Long add(@NonNull Long a, @NonNull Long b);
+
+    void sendMessage(@NonNull MessageData message, @NonNull Result<Boolean> result);
+
+    /** The codec used by ExampleHostApi. */
+    static @NonNull MessageCodec<Object> getCodec() {
+      return ExampleHostApiCodec.INSTANCE;
+    }
+    /** Sets up an instance of `ExampleHostApi` to handle messages through the `binaryMessenger`. */
+    static void setup(@NonNull BinaryMessenger binaryMessenger, @Nullable ExampleHostApi api) {
+      {
+        BasicMessageChannel<Object> channel =
+            new BasicMessageChannel<>(
+                binaryMessenger, "dev.flutter.pigeon.ExampleHostApi.getHostLanguage", getCodec());
+        if (api != null) {
+          channel.setMessageHandler(
+              (message, reply) -> {
+                ArrayList<Object> wrapped = new ArrayList<Object>();
+                try {
+                  String output = api.getHostLanguage();
+                  wrapped.add(0, output);
+                } catch (Throwable exception) {
+                  ArrayList<Object> wrappedError = wrapError(exception);
+                  wrapped = wrappedError;
+                }
+                reply.reply(wrapped);
+              });
+        } else {
+          channel.setMessageHandler(null);
+        }
+      }
+      {
+        BasicMessageChannel<Object> channel =
+            new BasicMessageChannel<>(
+                binaryMessenger, "dev.flutter.pigeon.ExampleHostApi.add", getCodec());
+        if (api != null) {
+          channel.setMessageHandler(
+              (message, reply) -> {
+                ArrayList<Object> wrapped = new ArrayList<Object>();
+                ArrayList<Object> args = (ArrayList<Object>) message;
+                Number aArg = (Number) args.get(0);
+                Number bArg = (Number) args.get(1);
+                try {
+                  Long output =
+                      api.add(
+                          (aArg == null) ? null : aArg.longValue(),
+                          (bArg == null) ? null : bArg.longValue());
+                  wrapped.add(0, output);
+                } catch (Throwable exception) {
+                  ArrayList<Object> wrappedError = wrapError(exception);
+                  wrapped = wrappedError;
+                }
+                reply.reply(wrapped);
+              });
+        } else {
+          channel.setMessageHandler(null);
+        }
+      }
+      {
+        BasicMessageChannel<Object> channel =
+            new BasicMessageChannel<>(
+                binaryMessenger, "dev.flutter.pigeon.ExampleHostApi.sendMessage", getCodec());
+        if (api != null) {
+          channel.setMessageHandler(
+              (message, reply) -> {
+                ArrayList<Object> wrapped = new ArrayList<Object>();
+                ArrayList<Object> args = (ArrayList<Object>) message;
+                MessageData messageArg = (MessageData) args.get(0);
+                Result<Boolean> resultCallback =
+                    new Result<Boolean>() {
+                      public void success(Boolean result) {
+                        wrapped.add(0, result);
+                        reply.reply(wrapped);
+                      }
+
+                      public void error(Throwable error) {
+                        ArrayList<Object> wrappedError = wrapError(error);
+                        reply.reply(wrappedError);
+                      }
+                    };
+
+                api.sendMessage(messageArg, resultCallback);
+              });
+        } else {
+          channel.setMessageHandler(null);
+        }
+      }
+    }
+  }
+  /** Generated class from Pigeon that represents Flutter messages that can be called from Java. */
+  public static class MessageFlutterApi {
+    private final @NonNull BinaryMessenger binaryMessenger;
+
+    public MessageFlutterApi(@NonNull BinaryMessenger argBinaryMessenger) {
+      this.binaryMessenger = argBinaryMessenger;
+    }
+
+    /** Public interface for sending reply. */
+    @SuppressWarnings("UnknownNullness")
+    public interface Reply<T> {
+      void reply(T reply);
+    }
+    /** The codec used by MessageFlutterApi. */
+    static @NonNull MessageCodec<Object> getCodec() {
+      return new StandardMessageCodec();
+    }
+
+    public void flutterMethod(@Nullable String aStringArg, @NonNull Reply<String> callback) {
+      BasicMessageChannel<Object> channel =
+          new BasicMessageChannel<>(
+              binaryMessenger, "dev.flutter.pigeon.MessageFlutterApi.flutterMethod", getCodec());
+      channel.send(
+          new ArrayList<Object>(Collections.singletonList(aStringArg)),
+          channelReply -> {
+            @SuppressWarnings("ConstantConditions")
+            String output = (String) channelReply;
+            callback.reply(output);
+          });
+    }
+  }
+}
diff --git a/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/MainActivity.kt b/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/MainActivity.kt
index 51f7d68..3f7d3b3 100644
--- a/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/MainActivity.kt
+++ b/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/MainActivity.kt
@@ -5,21 +5,60 @@
 package dev.flutter.pigeon_example_app
 
 import ExampleHostApi
+import MessageData
+import MessageFlutterApi
+import FlutterError
+
 import androidx.annotation.NonNull
 import io.flutter.embedding.android.FlutterActivity
 import io.flutter.embedding.engine.FlutterEngine
+import io.flutter.embedding.engine.plugins.FlutterPlugin
 
+// #docregion kotlin-class
 private class PigeonApiImplementation: ExampleHostApi {
-    override fun getHostLanguage(): String {
-        return "Kotlin"
+  override fun getHostLanguage(): String {
+    return "Kotlin"
+  }
+
+  override fun add(a: Long, b: Long): Long {
+    if (a < 0L || b < 0L) {
+      throw FlutterError("code", "message", "details");
     }
+    return a + b
+  }
+
+  override fun sendMessage(message: MessageData, callback: (Result<Boolean>) -> Unit) {
+    if (message.code == Code.ONE) {
+      callback(Result.failure(FlutterError("code", "message", "details")))
+      return
+    }
+    callback(Result.success(true))
+  }
 }
+// #enddocregion kotlin-class
+
+// #docregion kotlin-class-flutter
+private class PigeonFlutterApi {
+
+  var flutterApi: MessageFlutterApi? = null
+
+  constructor(binding: FlutterPlugin.FlutterPluginBinding) {
+    flutterApi = MessageFlutterApi(binding.getBinaryMessenger())
+  }
+
+  fun callFlutterMethod(aString: String, callback: (Result<String>) -> Unit) {
+    flutterApi!!.flutterMethod(aString) {
+      echo -> callback(Result.success(echo))
+    }
+  }
+}
+// #enddocregion kotlin-class-flutter
 
 class MainActivity: FlutterActivity() {
-    override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
-        super.configureFlutterEngine(flutterEngine)
+  override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
+    super.configureFlutterEngine(flutterEngine)
 
-        val api = PigeonApiImplementation()
-        ExampleHostApi.setUp(flutterEngine.dartExecutor.binaryMessenger, api);
-    }
+    val api = PigeonApiImplementation()
+    ExampleHostApi.setUp(flutterEngine.dartExecutor.binaryMessenger, api);
+  }
 }
diff --git a/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/Messages.g.kt b/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/Messages.g.kt
index fe157fc..bc2dfa8 100644
--- a/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/Messages.g.kt
+++ b/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/Messages.g.kt
@@ -1,7 +1,7 @@
 // Copyright 2013 The Flutter Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
-// Autogenerated from Pigeon (v9.2.5), do not edit directly.
+// Autogenerated from Pigeon (v10.1.1), do not edit directly.
 // See also: https://pub.dev/packages/pigeon
 
 
@@ -44,14 +44,79 @@
   override val message: String? = null,
   val details: Any? = null
 ) : Throwable()
+
+enum class Code(val raw: Int) {
+  ONE(0),
+  TWO(1);
+
+  companion object {
+    fun ofRaw(raw: Int): Code? {
+      return values().firstOrNull { it.raw == raw }
+    }
+  }
+}
+
+/** Generated class from Pigeon that represents data sent in messages. */
+data class MessageData (
+  val name: String? = null,
+  val description: String? = null,
+  val code: Code,
+  val data: Map<String?, String?>
+
+) {
+  companion object {
+    @Suppress("UNCHECKED_CAST")
+    fun fromList(list: List<Any?>): MessageData {
+      val name = list[0] as String?
+      val description = list[1] as String?
+      val code = Code.ofRaw(list[2] as Int)!!
+      val data = list[3] as Map<String?, String?>
+      return MessageData(name, description, code, data)
+    }
+  }
+  fun toList(): List<Any?> {
+    return listOf<Any?>(
+      name,
+      description,
+      code.raw,
+      data,
+    )
+  }
+}
+
+@Suppress("UNCHECKED_CAST")
+private object ExampleHostApiCodec : StandardMessageCodec() {
+  override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? {
+    return when (type) {
+      128.toByte() -> {
+        return (readValue(buffer) as? List<Any?>)?.let {
+          MessageData.fromList(it)
+        }
+      }
+      else -> super.readValueOfType(type, buffer)
+    }
+  }
+  override fun writeValue(stream: ByteArrayOutputStream, value: Any?)   {
+    when (value) {
+      is MessageData -> {
+        stream.write(128)
+        writeValue(stream, value.toList())
+      }
+      else -> super.writeValue(stream, value)
+    }
+  }
+}
+
 /** Generated interface from Pigeon that represents a handler of messages from Flutter. */
 interface ExampleHostApi {
   fun getHostLanguage(): String
+  fun add(a: Long, b: Long): Long
+  fun sendMessage(message: MessageData, callback: (Result<Boolean>) -> Unit)
 
   companion object {
     /** The codec used by ExampleHostApi. */
     val codec: MessageCodec<Any?> by lazy {
-      StandardMessageCodec()
+      ExampleHostApiCodec
     }
     /** Sets up an instance of `ExampleHostApi` to handle messages through the `binaryMessenger`. */
     @Suppress("UNCHECKED_CAST")
@@ -72,6 +137,62 @@
           channel.setMessageHandler(null)
         }
       }
+      run {
+        val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.ExampleHostApi.add", codec)
+        if (api != null) {
+          channel.setMessageHandler { message, reply ->
+            val args = message as List<Any?>
+            val aArg = args[0].let { if (it is Int) it.toLong() else it as Long }
+            val bArg = args[1].let { if (it is Int) it.toLong() else it as Long }
+            var wrapped: List<Any?>
+            try {
+              wrapped = listOf<Any?>(api.add(aArg, bArg))
+            } catch (exception: Throwable) {
+              wrapped = wrapError(exception)
+            }
+            reply.reply(wrapped)
+          }
+        } else {
+          channel.setMessageHandler(null)
+        }
+      }
+      run {
+        val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.ExampleHostApi.sendMessage", codec)
+        if (api != null) {
+          channel.setMessageHandler { message, reply ->
+            val args = message as List<Any?>
+            val messageArg = args[0] as MessageData
+            api.sendMessage(messageArg) { result: Result<Boolean> ->
+              val error = result.exceptionOrNull()
+              if (error != null) {
+                reply.reply(wrapError(error))
+              } else {
+                val data = result.getOrNull()
+                reply.reply(wrapResult(data))
+              }
+            }
+          }
+        } else {
+          channel.setMessageHandler(null)
+        }
+      }
+    }
+  }
+}
+/** Generated class from Pigeon that represents Flutter messages that can be called from Kotlin. */
+@Suppress("UNCHECKED_CAST")
+class MessageFlutterApi(private val binaryMessenger: BinaryMessenger) {
+  companion object {
+    /** The codec used by MessageFlutterApi. */
+    val codec: MessageCodec<Any?> by lazy {
+      StandardMessageCodec()
+    }
+  }
+  fun flutterMethod(aStringArg: String?, callback: (String) -> Unit) {
+    val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.MessageFlutterApi.flutterMethod", codec)
+    channel.send(listOf(aStringArg)) {
+      val result = it as String
+      callback(result)
     }
   }
 }
diff --git a/packages/pigeon/example/app/ios/Runner/AppDelegate.swift b/packages/pigeon/example/app/ios/Runner/AppDelegate.swift
index f8319d3..3c79131 100644
--- a/packages/pigeon/example/app/ios/Runner/AppDelegate.swift
+++ b/packages/pigeon/example/app/ios/Runner/AppDelegate.swift
@@ -5,11 +5,47 @@
 import Flutter
 import UIKit
 
+// #docregion swift-class
+// This extension of Error is required to do use FlutterError in any Swift code.
+extension FlutterError: Error {}
+
 private class PigeonApiImplementation: ExampleHostApi {
   func getHostLanguage() throws -> String {
     return "Swift"
   }
+
+  func add(_ a: Int64, to b: Int64) throws -> Int64 {
+    if (a < 0 || b < 0) {
+      throw FlutterError(code: "code", message: "message", details: "details");
+    }
+    return a + b
+  }
+
+  func sendMessage(message: MessageData, completion: @escaping (Result<Bool, Error>) -> Void) {
+    if (message.code == Code.one) {
+      completion(.failure(FlutterError(code: "code", message: "message", details: "details")))
+      return
+    }
+    completion(.success(true))
+  }
 }
+// #enddocregion swift-class
+
+// #docregion swift-class-flutter
+private class PigeonFlutterApi {
+  var flutterAPI: MessageFlutterApi
+
+  init(binaryMessenger: FlutterBinaryMessenger) {
+    flutterAPI = MessageFlutterApi(binaryMessenger: binaryMessenger)
+  }
+
+  func callFlutterMethod(aString aStringArg: String?, completion: @escaping (Result<String, Error>) -> Void) {
+    flutterAPI.flutterMethod(aString: aStringArg) {
+      completion(.success($0))
+    }
+  }
+}
+// #enddocregion swift-class-flutter
 
 @UIApplicationMain
 @objc class AppDelegate: FlutterAppDelegate {
diff --git a/packages/pigeon/example/app/ios/Runner/Messages.g.swift b/packages/pigeon/example/app/ios/Runner/Messages.g.swift
index 4223267..8dd1a6b 100644
--- a/packages/pigeon/example/app/ios/Runner/Messages.g.swift
+++ b/packages/pigeon/example/app/ios/Runner/Messages.g.swift
@@ -1,17 +1,16 @@
 // Copyright 2013 The Flutter Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
-// Autogenerated from Pigeon (v9.2.5), do not edit directly.
+// Autogenerated from Pigeon (v10.1.1), do not edit directly.
 // See also: https://pub.dev/packages/pigeon
 
 import Foundation
-
 #if os(iOS)
-  import Flutter
+import Flutter
 #elseif os(macOS)
-  import FlutterMacOS
+import FlutterMacOS
 #else
-  #error("Unsupported platform.")
+#error("Unsupported platform.")
 #endif
 
 private func wrapResult(_ result: Any?) -> [Any?] {
@@ -23,32 +22,106 @@
     return [
       flutterError.code,
       flutterError.message,
-      flutterError.details,
+      flutterError.details
     ]
   }
   return [
     "\(error)",
     "\(type(of: error))",
-    "Stacktrace: \(Thread.callStackSymbols)",
+    "Stacktrace: \(Thread.callStackSymbols)"
   ]
 }
 
 private func nilOrValue<T>(_ value: Any?) -> T? {
   if value is NSNull { return nil }
-  return (value as Any) as! T?
+  return value as! T?
 }
+
+enum Code: Int {
+  case one = 0
+  case two = 1
+}
+
+/// Generated class from Pigeon that represents data sent in messages.
+struct MessageData {
+  var name: String? = nil
+  var description: String? = nil
+  var code: Code
+  var data: [String?: String?]
+
+  static func fromList(_ list: [Any?]) -> MessageData? {
+    let name: String? = nilOrValue(list[0])
+    let description: String? = nilOrValue(list[1])
+    let code = Code(rawValue: list[2] as! Int)!
+    let data = list[3] as! [String?: String?]
+
+    return MessageData(
+      name: name,
+      description: description,
+      code: code,
+      data: data
+    )
+  }
+  func toList() -> [Any?] {
+    return [
+      name,
+      description,
+      code.rawValue,
+      data,
+    ]
+  }
+}
+
+private class ExampleHostApiCodecReader: FlutterStandardReader {
+  override func readValue(ofType type: UInt8) -> Any? {
+    switch type {
+      case 128:
+        return MessageData.fromList(self.readValue() as! [Any?])
+      default:
+        return super.readValue(ofType: type)
+    }
+  }
+}
+
+private class ExampleHostApiCodecWriter: FlutterStandardWriter {
+  override func writeValue(_ value: Any) {
+    if let value = value as? MessageData {
+      super.writeByte(128)
+      super.writeValue(value.toList())
+    } else {
+      super.writeValue(value)
+    }
+  }
+}
+
+private class ExampleHostApiCodecReaderWriter: FlutterStandardReaderWriter {
+  override func reader(with data: Data) -> FlutterStandardReader {
+    return ExampleHostApiCodecReader(data: data)
+  }
+
+  override func writer(with data: NSMutableData) -> FlutterStandardWriter {
+    return ExampleHostApiCodecWriter(data: data)
+  }
+}
+
+class ExampleHostApiCodec: FlutterStandardMessageCodec {
+  static let shared = ExampleHostApiCodec(readerWriter: ExampleHostApiCodecReaderWriter())
+}
+
 /// Generated protocol from Pigeon that represents a handler of messages from Flutter.
 protocol ExampleHostApi {
   func getHostLanguage() throws -> String
+  func add(_ a: Int64, to b: Int64) throws -> Int64
+  func sendMessage(message: MessageData, completion: @escaping (Result<Bool, Error>) -> Void)
 }
 
 /// Generated setup class from Pigeon to handle messages through the `binaryMessenger`.
 class ExampleHostApiSetup {
   /// The codec used by ExampleHostApi.
+  static var codec: FlutterStandardMessageCodec { ExampleHostApiCodec.shared }
   /// Sets up an instance of `ExampleHostApi` to handle messages through the `binaryMessenger`.
   static func setUp(binaryMessenger: FlutterBinaryMessenger, api: ExampleHostApi?) {
-    let getHostLanguageChannel = FlutterBasicMessageChannel(
-      name: "dev.flutter.pigeon.ExampleHostApi.getHostLanguage", binaryMessenger: binaryMessenger)
+    let getHostLanguageChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.ExampleHostApi.getHostLanguage", binaryMessenger: binaryMessenger, codec: codec)
     if let api = api {
       getHostLanguageChannel.setMessageHandler { _, reply in
         do {
@@ -61,5 +134,52 @@
     } else {
       getHostLanguageChannel.setMessageHandler(nil)
     }
+    let addChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.ExampleHostApi.add", binaryMessenger: binaryMessenger, codec: codec)
+    if let api = api {
+      addChannel.setMessageHandler { message, reply in
+        let args = message as! [Any?]
+        let aArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32)
+        let bArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32)
+        do {
+          let result = try api.add(aArg, to: bArg)
+          reply(wrapResult(result))
+        } catch {
+          reply(wrapError(error))
+        }
+      }
+    } else {
+      addChannel.setMessageHandler(nil)
+    }
+    let sendMessageChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.ExampleHostApi.sendMessage", binaryMessenger: binaryMessenger, codec: codec)
+    if let api = api {
+      sendMessageChannel.setMessageHandler { message, reply in
+        let args = message as! [Any?]
+        let messageArg = args[0] as! MessageData
+        api.sendMessage(message: messageArg) { result in
+          switch result {
+            case .success(let res):
+              reply(wrapResult(res))
+            case .failure(let error):
+              reply(wrapError(error))
+          }
+        }
+      }
+    } else {
+      sendMessageChannel.setMessageHandler(nil)
+    }
+  }
+}
+/// Generated class from Pigeon that represents Flutter messages that can be called from Swift.
+class MessageFlutterApi {
+  private let binaryMessenger: FlutterBinaryMessenger
+  init(binaryMessenger: FlutterBinaryMessenger){
+    self.binaryMessenger = binaryMessenger
+  }
+  func flutterMethod(aString aStringArg: String?, completion: @escaping (String) -> Void) {
+    let channel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.MessageFlutterApi.flutterMethod", binaryMessenger: binaryMessenger)
+    channel.sendMessage([aStringArg] as [Any?]) { response in
+      let result = response as! String
+      completion(result)
+    }
   }
 }
diff --git a/packages/pigeon/example/app/lib/main.dart b/packages/pigeon/example/app/lib/main.dart
index 93bfea0..ea90231 100644
--- a/packages/pigeon/example/app/lib/main.dart
+++ b/packages/pigeon/example/app/lib/main.dart
@@ -9,7 +9,19 @@
 
 import 'src/messages.g.dart';
 
+// #docregion main-dart-flutter
+class _ExampleFlutterApi implements MessageFlutterApi {
+  @override
+  String flutterMethod(String? aString) {
+    return aString ?? '';
+  }
+}
+// #enddocregion main-dart-flutter
+
 void main() {
+// #docregion main-dart-flutter
+  MessageFlutterApi.setup(_ExampleFlutterApi());
+// #enddocregion main-dart-flutter
   runApp(const MyApp());
 }
 
@@ -42,6 +54,36 @@
   final ExampleHostApi _hostApi = ExampleHostApi();
   String? _hostCallResult;
 
+  // #docregion main-dart
+  final ExampleHostApi _api = ExampleHostApi();
+
+  /// Calls host method `add` with provided arguments.
+  Future<int> add(int a, int b) async {
+    try {
+      return await _api.add(a, b);
+    } catch (e) {
+      // handle error.
+      return 0;
+    }
+  }
+
+  /// Sends message through host api using `MessageData` class
+  /// and api `sendMessage` method.
+  Future<bool> sendMessage(String messageText) {
+    final MessageData message = MessageData(
+      code: Code.one,
+      data: <String?, String?>{'header': 'this is a header'},
+      description: 'uri text',
+    );
+    try {
+      return _api.sendMessage(message);
+    } catch (e) {
+      // handle error.
+      return Future<bool>(() => true);
+    }
+  }
+  // #enddocregion main-dart
+
   @override
   void initState() {
     super.initState();
diff --git a/packages/pigeon/example/app/lib/src/messages.g.dart b/packages/pigeon/example/app/lib/src/messages.g.dart
index e2cc26b..25e75e3 100644
--- a/packages/pigeon/example/app/lib/src/messages.g.dart
+++ b/packages/pigeon/example/app/lib/src/messages.g.dart
@@ -1,7 +1,7 @@
 // Copyright 2013 The Flutter Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
-// Autogenerated from Pigeon (v9.2.5), do not edit directly.
+// Autogenerated from Pigeon (v10.1.1), do not edit directly.
 // See also: https://pub.dev/packages/pigeon
 // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import
 
@@ -11,6 +11,70 @@
 import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer;
 import 'package:flutter/services.dart';
 
+enum Code {
+  one,
+  two,
+}
+
+class MessageData {
+  MessageData({
+    this.name,
+    this.description,
+    required this.code,
+    required this.data,
+  });
+
+  String? name;
+
+  String? description;
+
+  Code code;
+
+  Map<String?, String?> data;
+
+  Object encode() {
+    return <Object?>[
+      name,
+      description,
+      code.index,
+      data,
+    ];
+  }
+
+  static MessageData decode(Object result) {
+    result as List<Object?>;
+    return MessageData(
+      name: result[0] as String?,
+      description: result[1] as String?,
+      code: Code.values[result[2]! as int],
+      data: (result[3] as Map<Object?, Object?>?)!.cast<String?, String?>(),
+    );
+  }
+}
+
+class _ExampleHostApiCodec extends StandardMessageCodec {
+  const _ExampleHostApiCodec();
+  @override
+  void writeValue(WriteBuffer buffer, Object? value) {
+    if (value is MessageData) {
+      buffer.putUint8(128);
+      writeValue(buffer, value.encode());
+    } else {
+      super.writeValue(buffer, value);
+    }
+  }
+
+  @override
+  Object? readValueOfType(int type, ReadBuffer buffer) {
+    switch (type) {
+      case 128:
+        return MessageData.decode(readValue(buffer)!);
+      default:
+        return super.readValueOfType(type, buffer);
+    }
+  }
+}
+
 class ExampleHostApi {
   /// Constructor for [ExampleHostApi].  The [binaryMessenger] named argument is
   /// available for dependency injection.  If it is left null, the default
@@ -19,7 +83,7 @@
       : _binaryMessenger = binaryMessenger;
   final BinaryMessenger? _binaryMessenger;
 
-  static const MessageCodec<Object?> codec = StandardMessageCodec();
+  static const MessageCodec<Object?> codec = _ExampleHostApiCodec();
 
   Future<String> getHostLanguage() async {
     final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
@@ -46,4 +110,85 @@
       return (replyList[0] as String?)!;
     }
   }
+
+  Future<int> add(int arg_a, int arg_b) async {
+    final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+        'dev.flutter.pigeon.ExampleHostApi.add', codec,
+        binaryMessenger: _binaryMessenger);
+    final List<Object?>? replyList =
+        await channel.send(<Object?>[arg_a, arg_b]) as List<Object?>?;
+    if (replyList == null) {
+      throw PlatformException(
+        code: 'channel-error',
+        message: 'Unable to establish connection on channel.',
+      );
+    } else if (replyList.length > 1) {
+      throw PlatformException(
+        code: replyList[0]! as String,
+        message: replyList[1] as String?,
+        details: replyList[2],
+      );
+    } else if (replyList[0] == null) {
+      throw PlatformException(
+        code: 'null-error',
+        message: 'Host platform returned null value for non-null return value.',
+      );
+    } else {
+      return (replyList[0] as int?)!;
+    }
+  }
+
+  Future<bool> sendMessage(MessageData arg_message) async {
+    final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+        'dev.flutter.pigeon.ExampleHostApi.sendMessage', codec,
+        binaryMessenger: _binaryMessenger);
+    final List<Object?>? replyList =
+        await channel.send(<Object?>[arg_message]) as List<Object?>?;
+    if (replyList == null) {
+      throw PlatformException(
+        code: 'channel-error',
+        message: 'Unable to establish connection on channel.',
+      );
+    } else if (replyList.length > 1) {
+      throw PlatformException(
+        code: replyList[0]! as String,
+        message: replyList[1] as String?,
+        details: replyList[2],
+      );
+    } else if (replyList[0] == null) {
+      throw PlatformException(
+        code: 'null-error',
+        message: 'Host platform returned null value for non-null return value.',
+      );
+    } else {
+      return (replyList[0] as bool?)!;
+    }
+  }
+}
+
+abstract class MessageFlutterApi {
+  static const MessageCodec<Object?> codec = StandardMessageCodec();
+
+  String flutterMethod(String? aString);
+
+  static void setup(MessageFlutterApi? api,
+      {BinaryMessenger? binaryMessenger}) {
+    {
+      final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+          'dev.flutter.pigeon.MessageFlutterApi.flutterMethod', codec,
+          binaryMessenger: binaryMessenger);
+      if (api == null) {
+        channel.setMessageHandler(null);
+      } else {
+        channel.setMessageHandler((Object? message) async {
+          assert(message != null,
+              'Argument for dev.flutter.pigeon.MessageFlutterApi.flutterMethod was null.');
+          final List<Object?> args = (message as List<Object?>?)!;
+          final String? arg_aString = (args[0] as String?);
+          final String output = api.flutterMethod(arg_aString);
+          return output;
+        });
+      }
+    }
+  }
 }
diff --git a/packages/pigeon/example/app/macos/Runner/messages.g.h b/packages/pigeon/example/app/macos/Runner/messages.g.h
new file mode 100644
index 0000000..d789948
--- /dev/null
+++ b/packages/pigeon/example/app/macos/Runner/messages.g.h
@@ -0,0 +1,62 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+// Autogenerated from Pigeon (v10.1.1), do not edit directly.
+// See also: https://pub.dev/packages/pigeon
+
+#import <Foundation/Foundation.h>
+
+@protocol FlutterBinaryMessenger;
+@protocol FlutterMessageCodec;
+@class FlutterError;
+@class FlutterStandardTypedData;
+
+NS_ASSUME_NONNULL_BEGIN
+
+typedef NS_ENUM(NSUInteger, PGNCode) {
+  PGNCodeOne = 0,
+  PGNCodeTwo = 1,
+};
+
+@class PGNMessageData;
+
+@interface PGNMessageData : NSObject
+/// `init` unavailable to enforce nonnull fields, see the `make` class method.
+- (instancetype)init NS_UNAVAILABLE;
++ (instancetype)makeWithName:(nullable NSString *)name
+                 description:(nullable NSString *)description
+                        code:(PGNCode)code
+                        data:(NSDictionary<NSString *, NSString *> *)data;
+@property(nonatomic, copy, nullable) NSString *name;
+@property(nonatomic, copy, nullable) NSString *description;
+@property(nonatomic, assign) PGNCode code;
+@property(nonatomic, strong) NSDictionary<NSString *, NSString *> *data;
+@end
+
+/// The codec used by PGNExampleHostApi.
+NSObject<FlutterMessageCodec> *PGNExampleHostApiGetCodec(void);
+
+@protocol PGNExampleHostApi
+/// @return `nil` only when `error != nil`.
+- (nullable NSString *)getHostLanguageWithError:(FlutterError *_Nullable *_Nonnull)error;
+/// @return `nil` only when `error != nil`.
+- (nullable NSNumber *)addNumber:(NSNumber *)a
+                        toNumber:(NSNumber *)b
+                           error:(FlutterError *_Nullable *_Nonnull)error;
+- (void)sendMessageMessage:(PGNMessageData *)message
+                completion:(void (^)(NSNumber *_Nullable, FlutterError *_Nullable))completion;
+@end
+
+extern void PGNExampleHostApiSetup(id<FlutterBinaryMessenger> binaryMessenger,
+                                   NSObject<PGNExampleHostApi> *_Nullable api);
+
+/// The codec used by PGNMessageFlutterApi.
+NSObject<FlutterMessageCodec> *PGNMessageFlutterApiGetCodec(void);
+
+@interface PGNMessageFlutterApi : NSObject
+- (instancetype)initWithBinaryMessenger:(id<FlutterBinaryMessenger>)binaryMessenger;
+- (void)flutterMethodAString:(nullable NSString *)aString
+                  completion:(void (^)(NSString *_Nullable, FlutterError *_Nullable))completion;
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/packages/pigeon/example/app/macos/Runner/messages.g.m b/packages/pigeon/example/app/macos/Runner/messages.g.m
new file mode 100644
index 0000000..8aa8436
--- /dev/null
+++ b/packages/pigeon/example/app/macos/Runner/messages.g.m
@@ -0,0 +1,217 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+// Autogenerated from Pigeon (v10.1.1), do not edit directly.
+// See also: https://pub.dev/packages/pigeon
+
+#import "messages.g.h"
+
+#if TARGET_OS_OSX
+#import <FlutterMacOS/FlutterMacOS.h>
+#else
+#import <Flutter/Flutter.h>
+#endif
+
+#if !__has_feature(objc_arc)
+#error File requires ARC to be enabled.
+#endif
+
+static NSArray *wrapResult(id result, FlutterError *error) {
+  if (error) {
+    return @[
+      error.code ?: [NSNull null], error.message ?: [NSNull null], error.details ?: [NSNull null]
+    ];
+  }
+  return @[ result ?: [NSNull null] ];
+}
+static id GetNullableObjectAtIndex(NSArray *array, NSInteger key) {
+  id result = array[key];
+  return (result == [NSNull null]) ? nil : result;
+}
+
+@interface PGNMessageData ()
++ (PGNMessageData *)fromList:(NSArray *)list;
++ (nullable PGNMessageData *)nullableFromList:(NSArray *)list;
+- (NSArray *)toList;
+@end
+
+@implementation PGNMessageData
++ (instancetype)makeWithName:(nullable NSString *)name
+                 description:(nullable NSString *)description
+                        code:(PGNCode)code
+                        data:(NSDictionary<NSString *, NSString *> *)data {
+  PGNMessageData *pigeonResult = [[PGNMessageData alloc] init];
+  pigeonResult.name = name;
+  pigeonResult.description = description;
+  pigeonResult.code = code;
+  pigeonResult.data = data;
+  return pigeonResult;
+}
++ (PGNMessageData *)fromList:(NSArray *)list {
+  PGNMessageData *pigeonResult = [[PGNMessageData alloc] init];
+  pigeonResult.name = GetNullableObjectAtIndex(list, 0);
+  pigeonResult.description = GetNullableObjectAtIndex(list, 1);
+  pigeonResult.code = [GetNullableObjectAtIndex(list, 2) integerValue];
+  pigeonResult.data = GetNullableObjectAtIndex(list, 3);
+  NSAssert(pigeonResult.data != nil, @"");
+  return pigeonResult;
+}
++ (nullable PGNMessageData *)nullableFromList:(NSArray *)list {
+  return (list) ? [PGNMessageData fromList:list] : nil;
+}
+- (NSArray *)toList {
+  return @[
+    (self.name ?: [NSNull null]),
+    (self.description ?: [NSNull null]),
+    @(self.code),
+    (self.data ?: [NSNull null]),
+  ];
+}
+@end
+
+@interface PGNExampleHostApiCodecReader : FlutterStandardReader
+@end
+@implementation PGNExampleHostApiCodecReader
+- (nullable id)readValueOfType:(UInt8)type {
+  switch (type) {
+    case 128:
+      return [PGNMessageData fromList:[self readValue]];
+    default:
+      return [super readValueOfType:type];
+  }
+}
+@end
+
+@interface PGNExampleHostApiCodecWriter : FlutterStandardWriter
+@end
+@implementation PGNExampleHostApiCodecWriter
+- (void)writeValue:(id)value {
+  if ([value isKindOfClass:[PGNMessageData class]]) {
+    [self writeByte:128];
+    [self writeValue:[value toList]];
+  } else {
+    [super writeValue:value];
+  }
+}
+@end
+
+@interface PGNExampleHostApiCodecReaderWriter : FlutterStandardReaderWriter
+@end
+@implementation PGNExampleHostApiCodecReaderWriter
+- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data {
+  return [[PGNExampleHostApiCodecWriter alloc] initWithData:data];
+}
+- (FlutterStandardReader *)readerWithData:(NSData *)data {
+  return [[PGNExampleHostApiCodecReader alloc] initWithData:data];
+}
+@end
+
+NSObject<FlutterMessageCodec> *PGNExampleHostApiGetCodec(void) {
+  static FlutterStandardMessageCodec *sSharedObject = nil;
+  static dispatch_once_t sPred = 0;
+  dispatch_once(&sPred, ^{
+    PGNExampleHostApiCodecReaderWriter *readerWriter =
+        [[PGNExampleHostApiCodecReaderWriter alloc] init];
+    sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter];
+  });
+  return sSharedObject;
+}
+
+void PGNExampleHostApiSetup(id<FlutterBinaryMessenger> binaryMessenger,
+                            NSObject<PGNExampleHostApi> *api) {
+  {
+    FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc]
+           initWithName:@"dev.flutter.pigeon.ExampleHostApi.getHostLanguage"
+        binaryMessenger:binaryMessenger
+                  codec:PGNExampleHostApiGetCodec()];
+    if (api) {
+      NSCAssert(
+          [api respondsToSelector:@selector(getHostLanguageWithError:)],
+          @"PGNExampleHostApi api (%@) doesn't respond to @selector(getHostLanguageWithError:)",
+          api);
+      [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) {
+        FlutterError *error;
+        NSString *output = [api getHostLanguageWithError:&error];
+        callback(wrapResult(output, error));
+      }];
+    } else {
+      [channel setMessageHandler:nil];
+    }
+  }
+  {
+    FlutterBasicMessageChannel *channel =
+        [[FlutterBasicMessageChannel alloc] initWithName:@"dev.flutter.pigeon.ExampleHostApi.add"
+                                         binaryMessenger:binaryMessenger
+                                                   codec:PGNExampleHostApiGetCodec()];
+    if (api) {
+      NSCAssert(
+          [api respondsToSelector:@selector(addNumber:toNumber:error:)],
+          @"PGNExampleHostApi api (%@) doesn't respond to @selector(addNumber:toNumber:error:)",
+          api);
+      [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) {
+        NSArray *args = message;
+        NSNumber *arg_a = GetNullableObjectAtIndex(args, 0);
+        NSNumber *arg_b = GetNullableObjectAtIndex(args, 1);
+        FlutterError *error;
+        NSNumber *output = [api addNumber:arg_a toNumber:arg_b error:&error];
+        callback(wrapResult(output, error));
+      }];
+    } else {
+      [channel setMessageHandler:nil];
+    }
+  }
+  {
+    FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc]
+           initWithName:@"dev.flutter.pigeon.ExampleHostApi.sendMessage"
+        binaryMessenger:binaryMessenger
+                  codec:PGNExampleHostApiGetCodec()];
+    if (api) {
+      NSCAssert([api respondsToSelector:@selector(sendMessageMessage:completion:)],
+                @"PGNExampleHostApi api (%@) doesn't respond to "
+                @"@selector(sendMessageMessage:completion:)",
+                api);
+      [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) {
+        NSArray *args = message;
+        PGNMessageData *arg_message = GetNullableObjectAtIndex(args, 0);
+        [api sendMessageMessage:arg_message
+                     completion:^(NSNumber *_Nullable output, FlutterError *_Nullable error) {
+                       callback(wrapResult(output, error));
+                     }];
+      }];
+    } else {
+      [channel setMessageHandler:nil];
+    }
+  }
+}
+NSObject<FlutterMessageCodec> *PGNMessageFlutterApiGetCodec(void) {
+  static FlutterStandardMessageCodec *sSharedObject = nil;
+  sSharedObject = [FlutterStandardMessageCodec sharedInstance];
+  return sSharedObject;
+}
+
+@interface PGNMessageFlutterApi ()
+@property(nonatomic, strong) NSObject<FlutterBinaryMessenger> *binaryMessenger;
+@end
+
+@implementation PGNMessageFlutterApi
+
+- (instancetype)initWithBinaryMessenger:(NSObject<FlutterBinaryMessenger> *)binaryMessenger {
+  self = [super init];
+  if (self) {
+    _binaryMessenger = binaryMessenger;
+  }
+  return self;
+}
+- (void)flutterMethodAString:(nullable NSString *)arg_aString
+                  completion:(void (^)(NSString *_Nullable, FlutterError *_Nullable))completion {
+  FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel
+      messageChannelWithName:@"dev.flutter.pigeon.MessageFlutterApi.flutterMethod"
+             binaryMessenger:self.binaryMessenger
+                       codec:PGNMessageFlutterApiGetCodec()];
+  [channel sendMessage:@[ arg_aString ?: [NSNull null] ]
+                 reply:^(id reply) {
+                   NSString *output = reply;
+                   completion(output, nil);
+                 }];
+}
+@end
diff --git a/packages/pigeon/example/app/pigeons/messages.dart b/packages/pigeon/example/app/pigeons/messages.dart
index bb684c1..0621630 100644
--- a/packages/pigeon/example/app/pigeons/messages.dart
+++ b/packages/pigeon/example/app/pigeons/messages.dart
@@ -4,18 +4,58 @@
 
 import 'package:pigeon/pigeon.dart';
 
+// #docregion config
 @ConfigurePigeon(PigeonOptions(
   dartOut: 'lib/src/messages.g.dart',
+  dartOptions: DartOptions(),
   cppOptions: CppOptions(namespace: 'pigeon_example'),
   cppHeaderOut: 'windows/runner/messages.g.h',
   cppSourceOut: 'windows/runner/messages.g.cpp',
   kotlinOut:
       'android/app/src/main/kotlin/dev/flutter/pigeon_example_app/Messages.g.kt',
-  // This file is also used by the macOS project.
+  kotlinOptions: KotlinOptions(),
+  javaOut: 'android/app/src/main/java/io/flutter/plugins/Messages.java',
+  javaOptions: JavaOptions(),
   swiftOut: 'ios/Runner/Messages.g.swift',
+  swiftOptions: SwiftOptions(),
+  objcHeaderOut: 'macos/Runner/messages.g.h',
+  objcSourceOut: 'macos/Runner/messages.g.m',
+  // Set this to a unique prefix for your plugin or application, per Objective-C naming conventions.
+  objcOptions: ObjcOptions(prefix: 'PGN'),
   copyrightHeader: 'pigeons/copyright.txt',
 ))
+// #enddocregion config
+
+// This file and ./messages_test.dart must be identical below this line.
+
+// #docregion host-definitions
+enum Code { one, two }
+
+class MessageData {
+  MessageData({required this.code, required this.data});
+  String? name;
+  String? description;
+  Code code;
+  Map<String?, String?> data;
+}
+
 @HostApi()
 abstract class ExampleHostApi {
   String getHostLanguage();
+
+  // These annotations create more idiomatic naming of methods in Objc and Swift.
+  @ObjCSelector('addNumber:toNumber:')
+  @SwiftFunction('add(_:to:)')
+  int add(int a, int b);
+
+  @async
+  bool sendMessage(MessageData message);
 }
+// #enddocregion host-definitions
+
+// #docregion flutter-definitions
+@FlutterApi()
+abstract class MessageFlutterApi {
+  String flutterMethod(String? aString);
+}
+// #enddocregion flutter-definitions
diff --git a/packages/pigeon/example/app/pubspec.yaml b/packages/pigeon/example/app/pubspec.yaml
index 674c596..8f7089a 100644
--- a/packages/pigeon/example/app/pubspec.yaml
+++ b/packages/pigeon/example/app/pubspec.yaml
@@ -11,6 +11,7 @@
     sdk: flutter
 
 dev_dependencies:
+  build_runner: ^2.1.10
   flutter_test:
     sdk: flutter
   integration_test:
diff --git a/packages/pigeon/example/app/windows/runner/flutter_window.cpp b/packages/pigeon/example/app/windows/runner/flutter_window.cpp
index a1590d9..d4f0fec 100644
--- a/packages/pigeon/example/app/windows/runner/flutter_window.cpp
+++ b/packages/pigeon/example/app/windows/runner/flutter_window.cpp
@@ -14,13 +14,29 @@
 using pigeon_example::ErrorOr;
 using pigeon_example::ExampleHostApi;
 
+// #docregion cpp-class
 class PigeonApiImplementation : public ExampleHostApi {
  public:
   PigeonApiImplementation() {}
   virtual ~PigeonApiImplementation() {}
 
   ErrorOr<std::string> GetHostLanguage() override { return "C++"; }
+  ErrorOr<int64_t> Add(int64_t a, int64_t b) {
+    if (a < 0 || b < 0) {
+      return FlutterError("code", "message", "details");
+    }
+    return a + b;
+  }
+  void SendMessage(const MessageData& message,
+                   std::function<void(ErrorOr<bool> reply)> result) {
+    if (message.code == Code.one) {
+      result(FlutterError("code", "message", "details"));
+      return;
+    }
+    result(true);
+  }
 };
+// #enddocregion cpp-class
 }  // namespace
 
 FlutterWindow::FlutterWindow(const flutter::DartProject& project)
@@ -33,6 +49,15 @@
     return false;
   }
 
+  // #docregion cpp-method-flutter
+  void TestPlugin::CallFlutterMethod(
+      String aString, std::function<void(ErrorOr<int64_t> reply)> result) {
+    MessageFlutterApi->FlutterMethod(
+        aString, [result](String echo) { result(echo); },
+        [result](const FlutterError& error) { result(error); });
+  }
+  // #enddocregion cpp-method-flutter
+
   RECT frame = GetClientArea();
 
   // The size here must match the window dimensions to avoid unnecessary surface
diff --git a/packages/pigeon/example/app/windows/runner/messages.g.cpp b/packages/pigeon/example/app/windows/runner/messages.g.cpp
index 4a29cf2..ba7c06b 100644
--- a/packages/pigeon/example/app/windows/runner/messages.g.cpp
+++ b/packages/pigeon/example/app/windows/runner/messages.g.cpp
@@ -1,7 +1,7 @@
 // Copyright 2013 The Flutter Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
-// Autogenerated from Pigeon (v9.2.5), do not edit directly.
+// Autogenerated from Pigeon (v10.1.1), do not edit directly.
 // See also: https://pub.dev/packages/pigeon
 
 #undef _HAS_EXCEPTIONS
@@ -24,10 +24,109 @@
 using flutter::EncodableMap;
 using flutter::EncodableValue;
 
+// MessageData
+
+MessageData::MessageData(const Code& code, const EncodableMap& data)
+    : code_(code), data_(data) {}
+
+MessageData::MessageData(const std::string* name,
+                         const std::string* description, const Code& code,
+                         const EncodableMap& data)
+    : name_(name ? std::optional<std::string>(*name) : std::nullopt),
+      description_(description ? std::optional<std::string>(*description)
+                               : std::nullopt),
+      code_(code),
+      data_(data) {}
+
+const std::string* MessageData::name() const {
+  return name_ ? &(*name_) : nullptr;
+}
+
+void MessageData::set_name(const std::string_view* value_arg) {
+  name_ = value_arg ? std::optional<std::string>(*value_arg) : std::nullopt;
+}
+
+void MessageData::set_name(std::string_view value_arg) { name_ = value_arg; }
+
+const std::string* MessageData::description() const {
+  return description_ ? &(*description_) : nullptr;
+}
+
+void MessageData::set_description(const std::string_view* value_arg) {
+  description_ =
+      value_arg ? std::optional<std::string>(*value_arg) : std::nullopt;
+}
+
+void MessageData::set_description(std::string_view value_arg) {
+  description_ = value_arg;
+}
+
+const Code& MessageData::code() const { return code_; }
+
+void MessageData::set_code(const Code& value_arg) { code_ = value_arg; }
+
+const EncodableMap& MessageData::data() const { return data_; }
+
+void MessageData::set_data(const EncodableMap& value_arg) { data_ = value_arg; }
+
+EncodableList MessageData::ToEncodableList() const {
+  EncodableList list;
+  list.reserve(4);
+  list.push_back(name_ ? EncodableValue(*name_) : EncodableValue());
+  list.push_back(description_ ? EncodableValue(*description_)
+                              : EncodableValue());
+  list.push_back(EncodableValue((int)code_));
+  list.push_back(EncodableValue(data_));
+  return list;
+}
+
+MessageData MessageData::FromEncodableList(const EncodableList& list) {
+  MessageData decoded((Code)(std::get<int32_t>(list[2])),
+                      std::get<EncodableMap>(list[3]));
+  auto& encodable_name = list[0];
+  if (!encodable_name.IsNull()) {
+    decoded.set_name(std::get<std::string>(encodable_name));
+  }
+  auto& encodable_description = list[1];
+  if (!encodable_description.IsNull()) {
+    decoded.set_description(std::get<std::string>(encodable_description));
+  }
+  return decoded;
+}
+
+ExampleHostApiCodecSerializer::ExampleHostApiCodecSerializer() {}
+
+EncodableValue ExampleHostApiCodecSerializer::ReadValueOfType(
+    uint8_t type, flutter::ByteStreamReader* stream) const {
+  switch (type) {
+    case 128:
+      return CustomEncodableValue(MessageData::FromEncodableList(
+          std::get<EncodableList>(ReadValue(stream))));
+    default:
+      return flutter::StandardCodecSerializer::ReadValueOfType(type, stream);
+  }
+}
+
+void ExampleHostApiCodecSerializer::WriteValue(
+    const EncodableValue& value, flutter::ByteStreamWriter* stream) const {
+  if (const CustomEncodableValue* custom_value =
+          std::get_if<CustomEncodableValue>(&value)) {
+    if (custom_value->type() == typeid(MessageData)) {
+      stream->WriteByte(128);
+      WriteValue(
+          EncodableValue(
+              std::any_cast<MessageData>(*custom_value).ToEncodableList()),
+          stream);
+      return;
+    }
+  }
+  flutter::StandardCodecSerializer::WriteValue(value, stream);
+}
+
 /// The codec used by ExampleHostApi.
 const flutter::StandardMessageCodec& ExampleHostApi::GetCodec() {
   return flutter::StandardMessageCodec::GetInstance(
-      &flutter::StandardCodecSerializer::GetInstance());
+      &ExampleHostApiCodecSerializer::GetInstance());
 }
 
 // Sets up an instance of `ExampleHostApi` to handle messages through the
@@ -59,6 +158,78 @@
       channel->SetMessageHandler(nullptr);
     }
   }
+  {
+    auto channel = std::make_unique<BasicMessageChannel<>>(
+        binary_messenger, "dev.flutter.pigeon.ExampleHostApi.add", &GetCodec());
+    if (api != nullptr) {
+      channel->SetMessageHandler(
+          [api](const EncodableValue& message,
+                const flutter::MessageReply<EncodableValue>& reply) {
+            try {
+              const auto& args = std::get<EncodableList>(message);
+              const auto& encodable_a_arg = args.at(0);
+              if (encodable_a_arg.IsNull()) {
+                reply(WrapError("a_arg unexpectedly null."));
+                return;
+              }
+              const int64_t a_arg = encodable_a_arg.LongValue();
+              const auto& encodable_b_arg = args.at(1);
+              if (encodable_b_arg.IsNull()) {
+                reply(WrapError("b_arg unexpectedly null."));
+                return;
+              }
+              const int64_t b_arg = encodable_b_arg.LongValue();
+              ErrorOr<int64_t> output = api->Add(a_arg, b_arg);
+              if (output.has_error()) {
+                reply(WrapError(output.error()));
+                return;
+              }
+              EncodableList wrapped;
+              wrapped.push_back(EncodableValue(std::move(output).TakeValue()));
+              reply(EncodableValue(std::move(wrapped)));
+            } catch (const std::exception& exception) {
+              reply(WrapError(exception.what()));
+            }
+          });
+    } else {
+      channel->SetMessageHandler(nullptr);
+    }
+  }
+  {
+    auto channel = std::make_unique<BasicMessageChannel<>>(
+        binary_messenger, "dev.flutter.pigeon.ExampleHostApi.sendMessage",
+        &GetCodec());
+    if (api != nullptr) {
+      channel->SetMessageHandler(
+          [api](const EncodableValue& message,
+                const flutter::MessageReply<EncodableValue>& reply) {
+            try {
+              const auto& args = std::get<EncodableList>(message);
+              const auto& encodable_message_arg = args.at(0);
+              if (encodable_message_arg.IsNull()) {
+                reply(WrapError("message_arg unexpectedly null."));
+                return;
+              }
+              const auto& message_arg = std::any_cast<const MessageData&>(
+                  std::get<CustomEncodableValue>(encodable_message_arg));
+              api->SendMessage(message_arg, [reply](ErrorOr<bool>&& output) {
+                if (output.has_error()) {
+                  reply(WrapError(output.error()));
+                  return;
+                }
+                EncodableList wrapped;
+                wrapped.push_back(
+                    EncodableValue(std::move(output).TakeValue()));
+                reply(EncodableValue(std::move(wrapped)));
+              });
+            } catch (const std::exception& exception) {
+              reply(WrapError(exception.what()));
+            }
+          });
+    } else {
+      channel->SetMessageHandler(nullptr);
+    }
+  }
 }
 
 EncodableValue ExampleHostApi::WrapError(std::string_view error_message) {
@@ -73,4 +244,37 @@
                                       error.details()});
 }
 
+// Generated class from Pigeon that represents Flutter messages that can be
+// called from C++.
+MessageFlutterApi::MessageFlutterApi(flutter::BinaryMessenger* binary_messenger)
+    : binary_messenger_(binary_messenger) {}
+
+const flutter::StandardMessageCodec& MessageFlutterApi::GetCodec() {
+  return flutter::StandardMessageCodec::GetInstance(
+      &flutter::StandardCodecSerializer::GetInstance());
+}
+
+void MessageFlutterApi::FlutterMethod(
+    const std::string* a_string_arg,
+    std::function<void(const std::string&)>&& on_success,
+    std::function<void(const FlutterError&)>&& on_error) {
+  auto channel = std::make_unique<BasicMessageChannel<>>(
+      binary_messenger_, "dev.flutter.pigeon.MessageFlutterApi.flutterMethod",
+      &GetCodec());
+  EncodableValue encoded_api_arguments = EncodableValue(EncodableList{
+      a_string_arg ? EncodableValue(*a_string_arg) : EncodableValue(),
+  });
+  channel->Send(
+      encoded_api_arguments,
+      [on_success = std::move(on_success), on_error = std::move(on_error)](
+          const uint8_t* reply, size_t reply_size) {
+        std::unique_ptr<EncodableValue> response =
+            GetCodec().DecodeMessage(reply, reply_size);
+        const auto& encodable_return_value = *response;
+        const auto& return_value =
+            std::get<std::string>(encodable_return_value);
+        on_success(return_value);
+      });
+}
+
 }  // namespace pigeon_example
diff --git a/packages/pigeon/example/app/windows/runner/messages.g.h b/packages/pigeon/example/app/windows/runner/messages.g.h
index 53d659a..1bf5ddd 100644
--- a/packages/pigeon/example/app/windows/runner/messages.g.h
+++ b/packages/pigeon/example/app/windows/runner/messages.g.h
@@ -1,7 +1,7 @@
 // Copyright 2013 The Flutter Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
-// Autogenerated from Pigeon (v9.2.5), do not edit directly.
+// Autogenerated from Pigeon (v10.1.1), do not edit directly.
 // See also: https://pub.dev/packages/pigeon
 
 #ifndef PIGEON_MESSAGES_G_H_
@@ -52,12 +52,68 @@
 
  private:
   friend class ExampleHostApi;
+  friend class MessageFlutterApi;
   ErrorOr() = default;
   T TakeValue() && { return std::get<T>(std::move(v_)); }
 
   std::variant<T, FlutterError> v_;
 };
 
+enum class Code { one = 0, two = 1 };
+
+// Generated class from Pigeon that represents data sent in messages.
+class MessageData {
+ public:
+  // Constructs an object setting all non-nullable fields.
+  explicit MessageData(const Code& code, const flutter::EncodableMap& data);
+
+  // Constructs an object setting all fields.
+  explicit MessageData(const std::string* name, const std::string* description,
+                       const Code& code, const flutter::EncodableMap& data);
+
+  const std::string* name() const;
+  void set_name(const std::string_view* value_arg);
+  void set_name(std::string_view value_arg);
+
+  const std::string* description() const;
+  void set_description(const std::string_view* value_arg);
+  void set_description(std::string_view value_arg);
+
+  const Code& code() const;
+  void set_code(const Code& value_arg);
+
+  const flutter::EncodableMap& data() const;
+  void set_data(const flutter::EncodableMap& value_arg);
+
+ private:
+  static MessageData FromEncodableList(const flutter::EncodableList& list);
+  flutter::EncodableList ToEncodableList() const;
+  friend class ExampleHostApi;
+  friend class ExampleHostApiCodecSerializer;
+  friend class MessageFlutterApi;
+  friend class MessageFlutterApiCodecSerializer;
+  std::optional<std::string> name_;
+  std::optional<std::string> description_;
+  Code code_;
+  flutter::EncodableMap data_;
+};
+
+class ExampleHostApiCodecSerializer : public flutter::StandardCodecSerializer {
+ public:
+  ExampleHostApiCodecSerializer();
+  inline static ExampleHostApiCodecSerializer& GetInstance() {
+    static ExampleHostApiCodecSerializer sInstance;
+    return sInstance;
+  }
+
+  void WriteValue(const flutter::EncodableValue& value,
+                  flutter::ByteStreamWriter* stream) const override;
+
+ protected:
+  flutter::EncodableValue ReadValueOfType(
+      uint8_t type, flutter::ByteStreamReader* stream) const override;
+};
+
 // Generated interface from Pigeon that represents a handler of messages from
 // Flutter.
 class ExampleHostApi {
@@ -66,6 +122,9 @@
   ExampleHostApi& operator=(const ExampleHostApi&) = delete;
   virtual ~ExampleHostApi() {}
   virtual ErrorOr<std::string> GetHostLanguage() = 0;
+  virtual ErrorOr<int64_t> Add(int64_t a, int64_t b) = 0;
+  virtual void SendMessage(const MessageData& message,
+                           std::function<void(ErrorOr<bool> reply)> result) = 0;
 
   // The codec used by ExampleHostApi.
   static const flutter::StandardMessageCodec& GetCodec();
@@ -79,5 +138,19 @@
  protected:
   ExampleHostApi() = default;
 };
+// Generated class from Pigeon that represents Flutter messages that can be
+// called from C++.
+class MessageFlutterApi {
+ public:
+  MessageFlutterApi(flutter::BinaryMessenger* binary_messenger);
+  static const flutter::StandardMessageCodec& GetCodec();
+  void FlutterMethod(const std::string* a_string,
+                     std::function<void(const std::string&)>&& on_success,
+                     std::function<void(const FlutterError&)>&& on_error);
+
+ private:
+  flutter::BinaryMessenger* binary_messenger_;
+};
+
 }  // namespace pigeon_example
 #endif  // PIGEON_MESSAGES_G_H_
diff --git a/packages/pigeon/example/build.excerpt.yaml b/packages/pigeon/example/build.excerpt.yaml
new file mode 100644
index 0000000..c0ce017
--- /dev/null
+++ b/packages/pigeon/example/build.excerpt.yaml
@@ -0,0 +1,23 @@
+targets:
+  $default:
+    sources:
+      include:
+        - lib/**
+        - pigeons/**
+        - app/**
+        # Some default includes that aren't really used here but will prevent
+        # false-negative warnings:
+        - $package$
+        - lib/$lib$
+      exclude:
+        - '**/.*/**'
+        - '**/build/**'
+    builders:
+      code_excerpter|code_excerpter:
+        enabled: true
+        generate_for:
+          - '**.dart'
+          - '**.swift'
+          - '**.kt'
+          - '**.cpp'
+          - '**.h'
\ No newline at end of file
diff --git a/packages/pigeon/example/lib/main.dart b/packages/pigeon/example/lib/main.dart
new file mode 100644
index 0000000..e7217c7
--- /dev/null
+++ b/packages/pigeon/example/lib/main.dart
@@ -0,0 +1,3 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
diff --git a/packages/pigeon/example/pubspec.yaml b/packages/pigeon/example/pubspec.yaml
new file mode 100644
index 0000000..af03bc0
--- /dev/null
+++ b/packages/pigeon/example/pubspec.yaml
@@ -0,0 +1,11 @@
+name: pigeon_example
+description: example app to show basic usage of pigeon.
+publish_to: none
+
+environment:
+  sdk: ">=2.18.0 <4.0.0"
+
+dependencies:
+
+dev_dependencies:
+  build_runner: ^2.1.10
diff --git a/packages/pigeon/lib/generator_tools.dart b/packages/pigeon/lib/generator_tools.dart
index ce2d065..2b67bd5 100644
--- a/packages/pigeon/lib/generator_tools.dart
+++ b/packages/pigeon/lib/generator_tools.dart
@@ -11,7 +11,7 @@
 /// The current version of pigeon.
 ///
 /// This must match the version in pubspec.yaml.
-const String pigeonVersion = '10.1.0';
+const String pigeonVersion = '10.1.1';
 
 /// Read all the content from [stdin] to a String.
 String readStdin() {
diff --git a/packages/pigeon/lib/pigeon_lib.dart b/packages/pigeon/lib/pigeon_lib.dart
index 09ea913..87a150e 100644
--- a/packages/pigeon/lib/pigeon_lib.dart
+++ b/packages/pigeon/lib/pigeon_lib.dart
@@ -168,27 +168,29 @@
 /// Options used when running the code generator.
 class PigeonOptions {
   /// Creates a instance of PigeonOptions
-  const PigeonOptions(
-      {this.input,
-      this.dartOut,
-      this.dartTestOut,
-      this.objcHeaderOut,
-      this.objcSourceOut,
-      this.objcOptions,
-      this.javaOut,
-      this.javaOptions,
-      this.swiftOut,
-      this.swiftOptions,
-      this.kotlinOut,
-      this.kotlinOptions,
-      this.cppHeaderOut,
-      this.cppSourceOut,
-      this.cppOptions,
-      this.dartOptions,
-      this.copyrightHeader,
-      this.oneLanguage,
-      this.astOut,
-      this.debugGenerators});
+  const PigeonOptions({
+    this.input,
+    this.dartOut,
+    this.dartTestOut,
+    this.objcHeaderOut,
+    this.objcSourceOut,
+    this.objcOptions,
+    this.javaOut,
+    this.javaOptions,
+    this.swiftOut,
+    this.swiftOptions,
+    this.kotlinOut,
+    this.kotlinOptions,
+    this.cppHeaderOut,
+    this.cppSourceOut,
+    this.cppOptions,
+    this.dartOptions,
+    this.copyrightHeader,
+    this.oneLanguage,
+    this.astOut,
+    this.debugGenerators,
+    this.basePath,
+  });
 
   /// Path to the file which will be processed.
   final String? input;
@@ -250,6 +252,9 @@
   /// True means print out line number of generators in comments at newlines.
   final bool? debugGenerators;
 
+  /// A base path to be prepended to all provided output paths.
+  final String? basePath;
+
   /// Creates a [PigeonOptions] from a Map representation where:
   /// `x = PigeonOptions.fromMap(x.toMap())`.
   static PigeonOptions fromMap(Map<String, Object> map) {
@@ -286,6 +291,7 @@
       oneLanguage: map['oneLanguage'] as bool?,
       astOut: map['astOut'] as String?,
       debugGenerators: map['debugGenerators'] as bool?,
+      basePath: map['basePath'] as String?,
     );
   }
 
@@ -313,6 +319,7 @@
       if (astOut != null) 'astOut': astOut!,
       if (oneLanguage != null) 'oneLanguage': oneLanguage!,
       if (debugGenerators != null) 'debugGenerators': debugGenerators!,
+      if (basePath != null) 'basePath': basePath!,
     };
     return result;
   }
@@ -353,7 +360,7 @@
   }
 }
 
-IOSink? _openSink(String? output) {
+IOSink? _openSink(String? output, {String basePath = ''}) {
   if (output == null) {
     return null;
   }
@@ -362,7 +369,7 @@
   if (output == 'stdout') {
     sink = stdout;
   } else {
-    file = File(output);
+    file = File(path.posix.join(basePath, output));
     sink = file.openWrite();
   }
   return sink;
@@ -394,14 +401,19 @@
 }
 
 DartOptions _dartOptionsWithCopyrightHeader(
-    DartOptions? dartOptions, String? copyrightHeader,
-    {String? dartOutPath, String? testOutPath}) {
+  DartOptions? dartOptions,
+  String? copyrightHeader, {
+  String? dartOutPath,
+  String? testOutPath,
+  String basePath = '',
+}) {
   dartOptions = dartOptions ?? const DartOptions();
   return dartOptions.merge(DartOptions(
       sourceOutPath: dartOutPath,
       testOutPath: testOutPath,
-      copyrightHeader:
-          copyrightHeader != null ? _lineReader(copyrightHeader) : null));
+      copyrightHeader: copyrightHeader != null
+          ? _lineReader(path.posix.join(basePath, copyrightHeader))
+          : null));
 }
 
 /// A [GeneratorAdapter] that generates the AST.
@@ -420,7 +432,7 @@
 
   @override
   IOSink? shouldGenerate(PigeonOptions options, FileType _) =>
-      _openSink(options.astOut);
+      _openSink(options.astOut, basePath: options.basePath ?? '');
 
   @override
   List<Error> validate(PigeonOptions options, Root root) => <Error>[];
@@ -438,14 +450,17 @@
   void generate(
       StringSink sink, PigeonOptions options, Root root, FileType fileType) {
     final DartOptions dartOptionsWithHeader = _dartOptionsWithCopyrightHeader(
-        options.dartOptions, options.copyrightHeader);
+      options.dartOptions,
+      options.copyrightHeader,
+      basePath: options.basePath ?? '',
+    );
     const DartGenerator generator = DartGenerator();
     generator.generate(dartOptionsWithHeader, root, sink);
   }
 
   @override
   IOSink? shouldGenerate(PigeonOptions options, FileType _) =>
-      _openSink(options.dartOut);
+      _openSink(options.dartOut, basePath: options.basePath ?? '');
 
   @override
   List<Error> validate(PigeonOptions options, Root root) => <Error>[];
@@ -467,6 +482,7 @@
       options.copyrightHeader,
       dartOutPath: options.dartOut,
       testOutPath: options.dartTestOut,
+      basePath: options.basePath ?? '',
     );
     const DartGenerator testGenerator = DartGenerator();
     testGenerator.generateTest(dartOptionsWithHeader, root, sink);
@@ -475,7 +491,7 @@
   @override
   IOSink? shouldGenerate(PigeonOptions options, FileType _) {
     if (options.dartTestOut != null) {
-      return _openSink(options.dartTestOut);
+      return _openSink(options.dartTestOut, basePath: options.basePath ?? '');
     } else {
       return null;
     }
@@ -500,7 +516,8 @@
     final ObjcOptions objcOptions = options.objcOptions ?? const ObjcOptions();
     final ObjcOptions objcOptionsWithHeader = objcOptions.merge(ObjcOptions(
       copyrightHeader: options.copyrightHeader != null
-          ? _lineReader(options.copyrightHeader!)
+          ? _lineReader(
+              path.posix.join(options.basePath ?? '', options.copyrightHeader))
           : null,
     ));
     final OutputFileOptions<ObjcOptions> outputFileOptions =
@@ -513,9 +530,9 @@
   @override
   IOSink? shouldGenerate(PigeonOptions options, FileType fileType) {
     if (fileType == FileType.source) {
-      return _openSink(options.objcSourceOut);
+      return _openSink(options.objcSourceOut, basePath: options.basePath ?? '');
     } else {
-      return _openSink(options.objcHeaderOut);
+      return _openSink(options.objcHeaderOut, basePath: options.basePath ?? '');
     }
   }
 
@@ -539,7 +556,8 @@
         className: javaOptions.className ??
             path.basenameWithoutExtension(options.javaOut!),
         copyrightHeader: options.copyrightHeader != null
-            ? _lineReader(options.copyrightHeader!)
+            ? _lineReader(path.posix
+                .join(options.basePath ?? '', options.copyrightHeader))
             : null));
     const JavaGenerator generator = JavaGenerator();
     generator.generate(javaOptions, root, sink);
@@ -547,7 +565,7 @@
 
   @override
   IOSink? shouldGenerate(PigeonOptions options, FileType _) =>
-      _openSink(options.javaOut);
+      _openSink(options.javaOut, basePath: options.basePath ?? '');
 
   @override
   List<Error> validate(PigeonOptions options, Root root) => <Error>[];
@@ -567,7 +585,8 @@
     SwiftOptions swiftOptions = options.swiftOptions ?? const SwiftOptions();
     swiftOptions = swiftOptions.merge(SwiftOptions(
         copyrightHeader: options.copyrightHeader != null
-            ? _lineReader(options.copyrightHeader!)
+            ? _lineReader(path.posix
+                .join(options.basePath ?? '', options.copyrightHeader))
             : null));
     const SwiftGenerator generator = SwiftGenerator();
     generator.generate(swiftOptions, root, sink);
@@ -575,7 +594,7 @@
 
   @override
   IOSink? shouldGenerate(PigeonOptions options, FileType _) =>
-      _openSink(options.swiftOut);
+      _openSink(options.swiftOut, basePath: options.basePath ?? '');
 
   @override
   List<Error> validate(PigeonOptions options, Root root) => <Error>[];
@@ -596,7 +615,8 @@
     final CppOptions cppOptions = options.cppOptions ?? const CppOptions();
     final CppOptions cppOptionsWithHeader = cppOptions.merge(CppOptions(
       copyrightHeader: options.copyrightHeader != null
-          ? _lineReader(options.copyrightHeader!)
+          ? _lineReader(
+              path.posix.join(options.basePath ?? '', options.copyrightHeader))
           : null,
     ));
     final OutputFileOptions<CppOptions> outputFileOptions =
@@ -609,9 +629,9 @@
   @override
   IOSink? shouldGenerate(PigeonOptions options, FileType fileType) {
     if (fileType == FileType.source) {
-      return _openSink(options.cppSourceOut);
+      return _openSink(options.cppSourceOut, basePath: options.basePath ?? '');
     } else {
-      return _openSink(options.cppHeaderOut);
+      return _openSink(options.cppHeaderOut, basePath: options.basePath ?? '');
     }
   }
 
@@ -635,7 +655,8 @@
     kotlinOptions = kotlinOptions.merge(KotlinOptions(
         errorClassName: kotlinOptions.errorClassName ?? 'FlutterError',
         copyrightHeader: options.copyrightHeader != null
-            ? _lineReader(options.copyrightHeader!)
+            ? _lineReader(path.posix
+                .join(options.basePath ?? '', options.copyrightHeader))
             : null));
     const KotlinGenerator generator = KotlinGenerator();
     generator.generate(kotlinOptions, root, sink);
@@ -643,7 +664,7 @@
 
   @override
   IOSink? shouldGenerate(PigeonOptions options, FileType _) =>
-      _openSink(options.kotlinOut);
+      _openSink(options.kotlinOut, basePath: options.basePath ?? '');
 
   @override
   List<Error> validate(PigeonOptions options, Root root) => <Error>[];
@@ -897,7 +918,7 @@
     } else {
       _errors.add(Error(
         message:
-            'unrecongized expression type ${expression.runtimeType} $expression',
+            'unrecognized expression type ${expression.runtimeType} $expression',
         lineNumber: _calculateLineNumber(source, expression.offset),
       ));
       return 0;
@@ -1356,14 +1377,17 @@
         help:
             'Path to generated AST debugging info. (Warning: format subject to change)')
     ..addFlag('debug_generators',
+        help: 'Print the line number of the generator in comments at newlines.')
+    ..addOption('base_path',
         help:
-            'Print the line number of the generator in comments at newlines.');
+            'A base path to be prefixed to all outputs and copyright header path. Generally used for testing',
+        hide: true);
 
   /// Convert command-line arguments to [PigeonOptions].
   static PigeonOptions parseArgs(List<String> args) {
     // Note: This function shouldn't perform any logic, just translate the args
     // to PigeonOptions.  Synthesized values inside of the PigeonOption should
-    // get set in the `run` function to accomodate users that are using the
+    // get set in the `run` function to accommodate users that are using the
     // `configurePigeon` function.
     final ArgResults results = _argParser.parse(args);
 
@@ -1396,6 +1420,7 @@
       oneLanguage: results['one_language'] as bool?,
       astOut: results['ast_out'] as String?,
       debugGenerators: results['debug_generators'] as bool?,
+      basePath: results['base_path'] as String?,
     );
     return opts;
   }
diff --git a/packages/pigeon/pigeons/README.md b/packages/pigeon/pigeons/README.md
index bdf5ca7..9f26345 100644
--- a/packages/pigeon/pigeons/README.md
+++ b/packages/pigeon/pigeons/README.md
@@ -1,5 +1,5 @@
 This directory contains Pigeon API definitions used to generate code for tests.
 
-Please do not add new files to this directory unless absolutely neccessary;
+Please do not add new files to this directory unless absolutely necessary;
 most additions should go into core_tests.dart. See
 https://github.com/flutter/flutter/issues/115168 for context.
diff --git a/packages/pigeon/pigeons/core_tests.dart b/packages/pigeon/pigeons/core_tests.dart
index ac9fd77..15de1ff 100644
--- a/packages/pigeon/pigeons/core_tests.dart
+++ b/packages/pigeon/pigeons/core_tests.dart
@@ -92,7 +92,7 @@
 /// platform_test integration tests.
 @HostApi()
 abstract class HostIntegrationCoreApi {
-  // ========== Syncronous method tests ==========
+  // ========== Synchronous method tests ==========
 
   /// A no-op function taking no arguments and returning no value, to sanity
   /// test basic calling.
@@ -152,7 +152,7 @@
   @SwiftFunction('echo(_:)')
   Map<String?, Object?> echoMap(Map<String?, Object?> aMap);
 
-  // ========== Syncronous nullable method tests ==========
+  // ========== Synchronous nullable method tests ==========
 
   /// Returns the passed object, to test serialization and deserialization.
   @ObjCSelector('echoAllNullableTypes:')
@@ -217,7 +217,7 @@
   @SwiftFunction('echoNullable(_:)')
   Map<String?, Object?>? echoNullableMap(Map<String?, Object?>? aNullableMap);
 
-  // ========== Asyncronous method tests ==========
+  // ========== Asynchronous method tests ==========
 
   /// A no-op function taking no arguments and returning no value, to sanity
   /// test basic asynchronous calling.
@@ -342,7 +342,7 @@
   /// Returns the passed map, to test serialization and deserialization asynchronously.
   @async
   @ObjCSelector('echoAsyncNullableMap:')
-  @SwiftFunction('echAsyncoNullable(_:)')
+  @SwiftFunction('echoAsyncNullable(_:)')
   Map<String?, Object?>? echoAsyncNullableMap(Map<String?, Object?>? aMap);
 
   // ========== Flutter API test wrappers ==========
@@ -609,7 +609,7 @@
 
 /// A data class containing a List, used in unit tests.
 // TODO(stuartmorgan): Evaluate whether these unit tests are still useful; see
-// TODOs above about restructring.
+// TODOs above about restructuring.
 class TestMessage {
   // ignore: always_specify_types, strict_raw_type
   List? testList;
diff --git a/packages/pigeon/platform_tests/alternate_language_test_plugin/example/ios/Runner.xcodeproj/project.pbxproj b/packages/pigeon/platform_tests/alternate_language_test_plugin/example/ios/Runner.xcodeproj/project.pbxproj
index ec7df03..393001e 100644
--- a/packages/pigeon/platform_tests/alternate_language_test_plugin/example/ios/Runner.xcodeproj/project.pbxproj
+++ b/packages/pigeon/platform_tests/alternate_language_test_plugin/example/ios/Runner.xcodeproj/project.pbxproj
@@ -266,7 +266,7 @@
 		97C146E61CF9000F007C117D /* Project object */ = {
 			isa = PBXProject;
 			attributes = {
-				LastUpgradeCheck = 1300;
+				LastUpgradeCheck = 1430;
 				ORGANIZATIONNAME = "";
 				TargetAttributes = {
 					33AA165E291EB8B600ECBEEB = {
diff --git a/packages/pigeon/platform_tests/alternate_language_test_plugin/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/pigeon/platform_tests/alternate_language_test_plugin/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
index e283d74..28b953f 100644
--- a/packages/pigeon/platform_tests/alternate_language_test_plugin/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
+++ b/packages/pigeon/platform_tests/alternate_language_test_plugin/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1300"
+   LastUpgradeVersion = "1430"
    version = "1.3">
    <BuildAction
       parallelizeBuildables = "YES"
diff --git a/packages/pigeon/platform_tests/shared_test_plugin_code/pubspec.yaml b/packages/pigeon/platform_tests/shared_test_plugin_code/pubspec.yaml
index 3b5c66f..742b00a 100644
--- a/packages/pigeon/platform_tests/shared_test_plugin_code/pubspec.yaml
+++ b/packages/pigeon/platform_tests/shared_test_plugin_code/pubspec.yaml
@@ -1,5 +1,5 @@
 name: shared_test_plugin_code
-description: Common code for test_plugin and altenate_language_test_plugin
+description: Common code for test_plugin and alternate_language_test_plugin
 version: 0.0.1
 publish_to: none
 
diff --git a/packages/pigeon/platform_tests/test_plugin/ios/Classes/CoreTests.gen.swift b/packages/pigeon/platform_tests/test_plugin/ios/Classes/CoreTests.gen.swift
index 2590ee6..4c1dfa0 100644
--- a/packages/pigeon/platform_tests/test_plugin/ios/Classes/CoreTests.gen.swift
+++ b/packages/pigeon/platform_tests/test_plugin/ios/Classes/CoreTests.gen.swift
@@ -375,7 +375,7 @@
   /// Returns the passed list, to test serialization and deserialization asynchronously.
   func echoAsyncNullable(_ aList: [Any?]?, completion: @escaping (Result<[Any?]?, Error>) -> Void)
   /// Returns the passed map, to test serialization and deserialization asynchronously.
-  func echAsyncoNullable(_ aMap: [String?: Any?]?, completion: @escaping (Result<[String?: Any?]?, Error>) -> Void)
+  func echoAsyncNullable(_ aMap: [String?: Any?]?, completion: @escaping (Result<[String?: Any?]?, Error>) -> Void)
   func callFlutterNoop(completion: @escaping (Result<Void, Error>) -> Void)
   func callFlutterThrowError(completion: @escaping (Result<Any?, Error>) -> Void)
   func callFlutterThrowErrorFromVoid(completion: @escaping (Result<Void, Error>) -> Void)
@@ -1177,7 +1177,7 @@
       echoAsyncNullableMapChannel.setMessageHandler { message, reply in
         let args = message as! [Any?]
         let aMapArg: [String?: Any?]? = nilOrValue(args[0])
-        api.echAsyncoNullable(aMapArg) { result in
+        api.echoAsyncNullable(aMapArg) { result in
           switch result {
             case .success(let res):
               reply(wrapResult(res))
diff --git a/packages/pigeon/platform_tests/test_plugin/ios/Classes/TestPlugin.swift b/packages/pigeon/platform_tests/test_plugin/ios/Classes/TestPlugin.swift
index 36ecbfd..368dbde 100644
--- a/packages/pigeon/platform_tests/test_plugin/ios/Classes/TestPlugin.swift
+++ b/packages/pigeon/platform_tests/test_plugin/ios/Classes/TestPlugin.swift
@@ -210,7 +210,7 @@
     completion(.success(aList))
   }
 
-  func echAsyncoNullable(_ aMap: [String?: Any?]?, completion: @escaping (Result<[String?: Any?]?, Error>) -> Void) {
+  func echoAsyncNullable(_ aMap: [String?: Any?]?, completion: @escaping (Result<[String?: Any?]?, Error>) -> Void) {
     completion(.success(aMap))
   }
 
diff --git a/packages/pigeon/platform_tests/test_plugin/macos/Classes/CoreTests.gen.swift b/packages/pigeon/platform_tests/test_plugin/macos/Classes/CoreTests.gen.swift
index 2590ee6..4c1dfa0 100644
--- a/packages/pigeon/platform_tests/test_plugin/macos/Classes/CoreTests.gen.swift
+++ b/packages/pigeon/platform_tests/test_plugin/macos/Classes/CoreTests.gen.swift
@@ -375,7 +375,7 @@
   /// Returns the passed list, to test serialization and deserialization asynchronously.
   func echoAsyncNullable(_ aList: [Any?]?, completion: @escaping (Result<[Any?]?, Error>) -> Void)
   /// Returns the passed map, to test serialization and deserialization asynchronously.
-  func echAsyncoNullable(_ aMap: [String?: Any?]?, completion: @escaping (Result<[String?: Any?]?, Error>) -> Void)
+  func echoAsyncNullable(_ aMap: [String?: Any?]?, completion: @escaping (Result<[String?: Any?]?, Error>) -> Void)
   func callFlutterNoop(completion: @escaping (Result<Void, Error>) -> Void)
   func callFlutterThrowError(completion: @escaping (Result<Any?, Error>) -> Void)
   func callFlutterThrowErrorFromVoid(completion: @escaping (Result<Void, Error>) -> Void)
@@ -1177,7 +1177,7 @@
       echoAsyncNullableMapChannel.setMessageHandler { message, reply in
         let args = message as! [Any?]
         let aMapArg: [String?: Any?]? = nilOrValue(args[0])
-        api.echAsyncoNullable(aMapArg) { result in
+        api.echoAsyncNullable(aMapArg) { result in
           switch result {
             case .success(let res):
               reply(wrapResult(res))
diff --git a/packages/pigeon/platform_tests/test_plugin/macos/Classes/TestPlugin.swift b/packages/pigeon/platform_tests/test_plugin/macos/Classes/TestPlugin.swift
index 04bda07..51a68c3 100644
--- a/packages/pigeon/platform_tests/test_plugin/macos/Classes/TestPlugin.swift
+++ b/packages/pigeon/platform_tests/test_plugin/macos/Classes/TestPlugin.swift
@@ -209,7 +209,7 @@
     completion(.success(aList))
   }
 
-  func echAsyncoNullable(_ aMap: [String?: Any?]?, completion: @escaping (Result<[String?: Any?]?, Error>) -> Void) {
+  func echoAsyncNullable(_ aMap: [String?: Any?]?, completion: @escaping (Result<[String?: Any?]?, Error>) -> Void) {
     completion(.success(aMap))
   }
 
diff --git a/packages/pigeon/pubspec.yaml b/packages/pigeon/pubspec.yaml
index 3c4e077..82531c4 100644
--- a/packages/pigeon/pubspec.yaml
+++ b/packages/pigeon/pubspec.yaml
@@ -2,7 +2,7 @@
 description: Code generator tool to make communication between Flutter and the host platform type-safe and easier.
 repository: https://github.com/flutter/packages/tree/main/packages/pigeon
 issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3Apigeon
-version: 10.1.0 # This must match the version in lib/generator_tools.dart
+version: 10.1.1 # This must match the version in lib/generator_tools.dart
 
 environment:
   sdk: ">=2.19.0 <4.0.0"
diff --git a/packages/pigeon/test/pigeon_lib_test.dart b/packages/pigeon/test/pigeon_lib_test.dart
index 47c098d..7e7cc21 100644
--- a/packages/pigeon/test/pigeon_lib_test.dart
+++ b/packages/pigeon/test/pigeon_lib_test.dart
@@ -145,6 +145,12 @@
     expect(opts.astOut, equals('stdout'));
   });
 
+  test('parse args - base_path', () {
+    final PigeonOptions opts =
+        Pigeon.parseArgs(<String>['--base_path', './foo/']);
+    expect(opts.basePath, equals('./foo/'));
+  });
+
   test('simple parse api', () {
     const String code = '''
 class Input1 {
@@ -399,7 +405,7 @@
     expect(results.copyrightHeader, 'foobar.txt');
   });
 
-  test('Dart generater copyright flag', () {
+  test('Dart generator copyright flag', () {
     final Root root = Root(apis: <Api>[], classes: <Class>[], enums: <Enum>[]);
     const PigeonOptions options =
         PigeonOptions(copyrightHeader: './copyright_header.txt');
@@ -409,7 +415,7 @@
     expect(buffer.toString(), startsWith('// Copyright 2013'));
   });
 
-  test('Java generater copyright flag', () {
+  test('Java generator copyright flag', () {
     final Root root = Root(apis: <Api>[], classes: <Class>[], enums: <Enum>[]);
     const PigeonOptions options = PigeonOptions(
         javaOut: 'Foo.java', copyrightHeader: './copyright_header.txt');
@@ -419,7 +425,7 @@
     expect(buffer.toString(), startsWith('// Copyright 2013'));
   });
 
-  test('Objc header generater copyright flag', () {
+  test('Objc header generator copyright flag', () {
     final Root root = Root(apis: <Api>[], classes: <Class>[], enums: <Enum>[]);
     const PigeonOptions options =
         PigeonOptions(copyrightHeader: './copyright_header.txt');
@@ -430,7 +436,7 @@
     expect(buffer.toString(), startsWith('// Copyright 2013'));
   });
 
-  test('Objc source generater copyright flag', () {
+  test('Objc source generator copyright flag', () {
     final Root root = Root(apis: <Api>[], classes: <Class>[], enums: <Enum>[]);
     const PigeonOptions options =
         PigeonOptions(copyrightHeader: './copyright_header.txt');
@@ -441,7 +447,7 @@
     expect(buffer.toString(), startsWith('// Copyright 2013'));
   });
 
-  test('Swift generater copyright flag', () {
+  test('Swift generator copyright flag', () {
     final Root root = Root(apis: <Api>[], classes: <Class>[], enums: <Enum>[]);
     const PigeonOptions options = PigeonOptions(
         swiftOut: 'Foo.swift', copyrightHeader: './copyright_header.txt');
@@ -451,7 +457,7 @@
     expect(buffer.toString(), startsWith('// Copyright 2013'));
   });
 
-  test('C++ header generater copyright flag', () {
+  test('C++ header generator copyright flag', () {
     final Root root = Root(apis: <Api>[], classes: <Class>[], enums: <Enum>[]);
     const PigeonOptions options = PigeonOptions(
         cppHeaderOut: 'Foo.h', copyrightHeader: './copyright_header.txt');
@@ -461,7 +467,7 @@
     expect(buffer.toString(), startsWith('// Copyright 2013'));
   });
 
-  test('C++ source generater copyright flag', () {
+  test('C++ source generator copyright flag', () {
     final Root root = Root(apis: <Api>[], classes: <Class>[], enums: <Enum>[]);
     const PigeonOptions options =
         PigeonOptions(copyrightHeader: './copyright_header.txt');
diff --git a/packages/pigeon/tool/generate.dart b/packages/pigeon/tool/generate.dart
index 946a69c..02e28ad 100644
--- a/packages/pigeon/tool/generate.dart
+++ b/packages/pigeon/tool/generate.dart
@@ -38,7 +38,7 @@
   final String baseDir = p.dirname(p.dirname(Platform.script.toFilePath()));
 
   print('Generating platform_test/ output...');
-  final int generateExitCode = await generatePigeons(baseDir: baseDir);
+  final int generateExitCode = await generateTestPigeons(baseDir: baseDir);
   if (generateExitCode == 0) {
     print('Generation complete!');
   } else {
diff --git a/packages/pigeon/tool/run_tests.dart b/packages/pigeon/tool/run_tests.dart
index b68117f..4d1c4fa 100644
--- a/packages/pigeon/tool/run_tests.dart
+++ b/packages/pigeon/tool/run_tests.dart
@@ -38,8 +38,8 @@
   final String relativePigeonPath = p.relative(baseDir, from: repositoryRoot);
 
   print('Validating generated files:');
-  print('  Generating output...');
-  final int generateExitCode = await generatePigeons(baseDir: baseDir);
+  print('  Generating test output...');
+  final int generateExitCode = await generateTestPigeons(baseDir: baseDir);
   if (generateExitCode != 0) {
     print('Generation failed; see above for errors.');
     exit(generateExitCode);
@@ -82,6 +82,63 @@
   exit(1);
 }
 
+Future<void> _validateGeneratedExampleFiles() async {
+  final String baseDir = p.dirname(p.dirname(Platform.script.toFilePath()));
+  final String repositoryRoot = p.dirname(p.dirname(baseDir));
+  final String relativePigeonPath = p.relative(baseDir, from: repositoryRoot);
+
+  print('Validating generated files:');
+  print('  Generating example output...');
+
+  final int generateExitCode = await runPigeon(
+    input: './example/app/pigeons/messages.dart',
+    basePath: './example/app',
+  );
+
+  if (generateExitCode != 0) {
+    print('Generation failed; see above for errors.');
+    exit(generateExitCode);
+  }
+
+  print('  Formatting output...');
+  final int formatExitCode =
+      await formatAllFiles(repositoryRoot: repositoryRoot);
+  if (formatExitCode != 0) {
+    print('Formatting failed; see above for errors.');
+    exit(formatExitCode);
+  }
+
+  print('  Checking for changes...');
+  final List<String> modifiedFiles = await _modifiedFiles(
+      repositoryRoot: repositoryRoot, relativePigeonPath: relativePigeonPath);
+
+  if (modifiedFiles.isEmpty) {
+    return;
+  }
+
+  print(
+      'Either messages.dart and messages_test.dart have non-matching definitions or');
+  print('the following files are not updated, or not formatted correctly:');
+  modifiedFiles.map((String line) => '  $line').forEach(print);
+
+  print('\nTo fix run "dart run tool/generate.dart --format" from the pigeon/ '
+      'directory, or apply the diff with the command below.\n');
+
+  final ProcessResult diffResult = await Process.run(
+    'git',
+    <String>['diff', relativePigeonPath],
+    workingDirectory: repositoryRoot,
+  );
+  if (diffResult.exitCode != 0) {
+    print('Unable to determine diff.');
+    exit(1);
+  }
+  print('patch -p1 <<DONE');
+  print(diffResult.stdout);
+  print('DONE');
+  exit(1);
+}
+
 Future<List<String>> _modifiedFiles(
     {required String repositoryRoot,
     required String relativePigeonPath}) async {
@@ -165,6 +222,7 @@
       print('Skipping generated file validation on stable.');
     } else {
       await _validateGeneratedTestFiles();
+      await _validateGeneratedExampleFiles();
     }
   }
 
diff --git a/packages/pigeon/tool/shared/generation.dart b/packages/pigeon/tool/shared/generation.dart
index 7d1b5c9..ee54f2f 100644
--- a/packages/pigeon/tool/shared/generation.dart
+++ b/packages/pigeon/tool/shared/generation.dart
@@ -45,7 +45,7 @@
   return specialCases[inputName] ?? _snakeToPascalCase(inputName);
 }
 
-Future<int> generatePigeons({required String baseDir}) async {
+Future<int> generateTestPigeons({required String baseDir}) async {
   // TODO(stuartmorgan): Make this dynamic rather than hard-coded. Or eliminate
   // it entirely; see https://github.com/flutter/flutter/issues/115169.
   const List<String> inputs = <String>[
@@ -173,7 +173,10 @@
   String? javaPackage,
   String? objcHeaderOut,
   String? objcSourceOut,
+  String objcPrefix = '',
   bool suppressVersion = false,
+  String copyrightHeader = './copyright_header.txt',
+  String? basePath,
 }) async {
   // Temporarily suppress the version output via the global flag if requested.
   // This is done because having the version in all the generated test output
@@ -189,7 +192,7 @@
   }
   final int result = await Pigeon.runWithOptions(PigeonOptions(
     input: input,
-    copyrightHeader: './copyright_header.txt',
+    copyrightHeader: copyrightHeader,
     dartOut: dartOut,
     dartTestOut: dartTestOut,
     dartOptions: const DartOptions(),
@@ -203,9 +206,10 @@
         package: kotlinPackage, errorClassName: kotlinErrorClassName),
     objcHeaderOut: objcHeaderOut,
     objcSourceOut: objcSourceOut,
-    objcOptions: const ObjcOptions(),
+    objcOptions: ObjcOptions(prefix: objcPrefix),
     swiftOut: swiftOut,
     swiftOptions: const SwiftOptions(),
+    basePath: basePath,
   ));
   includeVersionInGeneratedWarning = originalWarningSetting;
   return result;
@@ -213,7 +217,7 @@
 
 /// Runs the repository tooling's format command on this package.
 ///
-/// This is intended for formatting generated autoput, but since there's no
+/// This is intended for formatting generated output, but since there's no
 /// way to filter to specific files in with the repo tooling it runs over the
 /// entire package.
 Future<int> formatAllFiles({required String repositoryRoot}) {
diff --git a/packages/pigeon/tool/shared/test_runner.dart b/packages/pigeon/tool/shared/test_runner.dart
index abbf5ed..ba66394 100644
--- a/packages/pigeon/tool/shared/test_runner.dart
+++ b/packages/pigeon/tool/shared/test_runner.dart
@@ -19,7 +19,7 @@
   // tests being run, as not all of them need these files.
   final String baseDir = p.dirname(p.dirname(Platform.script.toFilePath()));
   print('# Generating platform_test/ output...');
-  final int generateExitCode = await generatePigeons(baseDir: baseDir);
+  final int generateExitCode = await generateTestPigeons(baseDir: baseDir);
   if (generateExitCode == 0) {
     print('Generation complete!');
   } else {