[pigeon] primitive enums (#4580)

Adds support for enums as parameters and return types.

Dart, C++, Java, Kotlin, Objective-C, and swift:
Primitive synchronous non-null
Primitive synchronous nullable
Primitive asynchronous non-null
Primitive asynchronous nullable
Primitive Flutter api non-null
Primitive flutter api nullable

Objective-C:
Class property nullable 

Also fixes an issue with nullable enums in classes on objc. This fix required a breaking change that nested all nullable enums in a wrapper class to allow nullability. 

Also adds the ability to format files before tests run (to make my life better)

Also replaces https://github.com/flutter/packages/pull/4756

Also adds objc prefixes to enums

fixes: https://github.com/flutter/flutter/issues/87307
fixes: https://github.com/flutter/flutter/issues/118733
diff --git a/packages/pigeon/.gitignore b/packages/pigeon/.gitignore
index 5af65c5..ae69a69 100644
--- a/packages/pigeon/.gitignore
+++ b/packages/pigeon/.gitignore
@@ -10,4 +10,4 @@
 local.properties
 gradle-wrapper.jar
 bin/pigeon.dart.dill
-
+**/subdirectory/does/not/exist/**
diff --git a/packages/pigeon/CHANGELOG.md b/packages/pigeon/CHANGELOG.md
index bec0d70..e0c7d40 100644
--- a/packages/pigeon/CHANGELOG.md
+++ b/packages/pigeon/CHANGELOG.md
@@ -1,5 +1,9 @@
-## NEXT
+## 11.0.0
 
+* Adds primitive enum support.
+* Fixes Objective-C nullable enums.
+* **Breaking Change** Changes all nullable enums in Objective-C to be wrapped in custom classes.
+* **Breaking Change** Changes all enums names in Objective-C to have class prefix.
 * Updates minimum supported SDK version to Flutter 3.7/Dart 2.19.
 
 ## 10.1.6
@@ -91,7 +95,7 @@
 ## 9.1.1
 
 * [swift] Removes experimental tags.
-* [kotin] Removes experimental tags.
+* [kotlin] Removes experimental tags.
 
 ## 9.1.0
 
diff --git a/packages/pigeon/README.md b/packages/pigeon/README.md
index 26ee5bd..213e650 100644
--- a/packages/pigeon/README.md
+++ b/packages/pigeon/README.md
@@ -23,12 +23,9 @@
 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.
+Custom classes, nested datatypes, and enums are also supported. 
 
-#### Enums
-
-Pigeon currently supports enum generation in class fields only.
-See issue: [87307](https://github.com/flutter/flutter/issues/87307).
+Nullable enums in Objective-C generated code will be wrapped in a class to allow for nullability.
 
 ### Synchronous and Asynchronous methods
 
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
index f358afe..25bcda1 100644
--- 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
@@ -173,7 +173,7 @@
       Object description = list.get(1);
       pigeonResult.setDescription((String) description);
       Object code = list.get(2);
-      pigeonResult.setCode(code == null ? null : Code.values()[(int) code]);
+      pigeonResult.setCode(Code.values()[(int) code]);
       Object data = list.get(3);
       pigeonResult.setData((Map<String, String>) data);
       return pigeonResult;
diff --git a/packages/pigeon/example/app/ios/Runner/Messages.g.swift b/packages/pigeon/example/app/ios/Runner/Messages.g.swift
index c95fc3e..9c12267 100644
--- a/packages/pigeon/example/app/ios/Runner/Messages.g.swift
+++ b/packages/pigeon/example/app/ios/Runner/Messages.g.swift
@@ -13,6 +13,10 @@
 #error("Unsupported platform.")
 #endif
 
+private func isNullish(_ value: Any?) -> Bool {
+  return value is NSNull || value == nil
+}
+
 private func wrapResult(_ result: Any?) -> [Any?] {
   return [result]
 }
diff --git a/packages/pigeon/example/app/macos/Runner/messages.g.h b/packages/pigeon/example/app/macos/Runner/messages.g.h
index f7cbe39..64cf6e2 100644
--- a/packages/pigeon/example/app/macos/Runner/messages.g.h
+++ b/packages/pigeon/example/app/macos/Runner/messages.g.h
@@ -18,6 +18,12 @@
   PGNCodeTwo = 1,
 };
 
+/// Wrapper for PGNCode to allow for nullability.
+@interface PGNCodeBox : NSObject
+@property(nonatomic, assign) PGNCode value;
+- (instancetype)initWithValue:(PGNCode)value;
+@end
+
 @class PGNMessageData;
 
 @interface PGNMessageData : NSObject
diff --git a/packages/pigeon/example/app/macos/Runner/messages.g.m b/packages/pigeon/example/app/macos/Runner/messages.g.m
index 37b75fa..6610097 100644
--- a/packages/pigeon/example/app/macos/Runner/messages.g.m
+++ b/packages/pigeon/example/app/macos/Runner/messages.g.m
@@ -16,6 +16,16 @@
 #error File requires ARC to be enabled.
 #endif
 
+@implementation PGNCodeBox
+- (instancetype)initWithValue:(PGNCode)value {
+  self = [super init];
+  if (self) {
+    _value = value;
+  }
+  return self;
+}
+@end
+
 static NSArray *wrapResult(id result, FlutterError *error) {
   if (error) {
     return @[
diff --git a/packages/pigeon/lib/cpp_generator.dart b/packages/pigeon/lib/cpp_generator.dart
index 6284f53..67b7f50 100644
--- a/packages/pigeon/lib/cpp_generator.dart
+++ b/packages/pigeon/lib/cpp_generator.dart
@@ -888,9 +888,14 @@
             indent.writeln(
                 'std::unique_ptr<EncodableValue> response = GetCodec().DecodeMessage(reply, reply_size);');
             indent.writeln('const auto& $encodedReplyName = *response;');
-            _writeEncodableValueArgumentUnwrapping(indent, returnType,
-                argName: successCallbackArgument,
-                encodableArgName: encodedReplyName);
+            _writeEncodableValueArgumentUnwrapping(
+              indent,
+              root,
+              returnType,
+              argName: successCallbackArgument,
+              encodableArgName: encodedReplyName,
+              apiType: ApiType.flutter,
+            );
           }
           indent.writeln('on_success($successCallbackArgument);');
         });
@@ -968,9 +973,19 @@
                         indent.writeln('return;');
                       });
                     }
-                    _writeEncodableValueArgumentUnwrapping(indent, hostType,
-                        argName: argName, encodableArgName: encodableArgName);
-                    methodArgument.add(argName);
+                    _writeEncodableValueArgumentUnwrapping(
+                      indent,
+                      root,
+                      hostType,
+                      argName: argName,
+                      encodableArgName: encodableArgName,
+                      apiType: ApiType.host,
+                    );
+                    final String unwrapEnum =
+                        isEnum(root, arg.type) && arg.type.isNullable
+                            ? ' ? &(*$argName) : nullptr'
+                            : '';
+                    methodArgument.add('$argName$unwrapEnum');
                   });
                 }
 
@@ -1198,6 +1213,10 @@
     final String errorGetter;
 
     const String nullValue = 'EncodableValue()';
+    String enumPrefix = '';
+    if (isEnum(root, returnType)) {
+      enumPrefix = '(int) ';
+    }
     if (returnType.isVoid) {
       nonErrorPath = '${prefix}wrapped.push_back($nullValue);';
       errorCondition = 'output.has_value()';
@@ -1205,22 +1224,24 @@
     } else {
       final HostDatatype hostType = getHostDatatype(returnType, root.classes,
           root.enums, _shortBaseCppTypeForBuiltinDartType);
+
       const String extractedValue = 'std::move(output).TakeValue()';
-      final String wrapperType =
-          hostType.isBuiltin ? 'EncodableValue' : 'CustomEncodableValue';
+      final String wrapperType = hostType.isBuiltin || isEnum(root, returnType)
+          ? 'EncodableValue'
+          : 'CustomEncodableValue';
       if (returnType.isNullable) {
         // The value is a std::optional, so needs an extra layer of
         // handling.
         nonErrorPath = '''
 ${prefix}auto output_optional = $extractedValue;
 ${prefix}if (output_optional) {
-$prefix\twrapped.push_back($wrapperType(std::move(output_optional).value()));
+$prefix\twrapped.push_back($wrapperType(${enumPrefix}std::move(output_optional).value()));
 $prefix} else {
 $prefix\twrapped.push_back($nullValue);
 $prefix}''';
       } else {
         nonErrorPath =
-            '${prefix}wrapped.push_back($wrapperType($extractedValue));';
+            '${prefix}wrapped.push_back($wrapperType($enumPrefix$extractedValue));';
       }
       errorCondition = 'output.has_error()';
       errorGetter = 'error';
@@ -1297,9 +1318,11 @@
   /// existing EncodableValue variable called [encodableArgName].
   void _writeEncodableValueArgumentUnwrapping(
     Indent indent,
+    Root root,
     HostDatatype hostType, {
     required String argName,
     required String encodableArgName,
+    required ApiType apiType,
   }) {
     if (hostType.isNullable) {
       // Nullable arguments are always pointers, with nullptr corresponding to
@@ -1320,6 +1343,22 @@
       } else if (hostType.isBuiltin) {
         indent.writeln(
             'const auto* $argName = std::get_if<${hostType.datatype}>(&$encodableArgName);');
+      } else if (hostType.isEnum) {
+        if (hostType.isNullable) {
+          final String valueVarName = '${argName}_value';
+          indent.writeln(
+              'const int64_t $valueVarName = $encodableArgName.IsNull() ? 0 : $encodableArgName.LongValue();');
+          if (apiType == ApiType.flutter) {
+            indent.writeln(
+                'const auto* $argName = $encodableArgName.IsNull() ? nullptr : &(${hostType.datatype})$valueVarName;');
+          } else {
+            indent.writeln(
+                'const auto $argName = $encodableArgName.IsNull() ? std::nullopt : std::make_optional<${hostType.datatype}>(static_cast<${hostType.datatype}>(${argName}_value));');
+          }
+        } else {
+          indent.writeln(
+              'const auto* $argName = &((${hostType.datatype})std::get<int>($encodableArgName));');
+        }
       } else {
         indent.writeln(
             'const auto* $argName = &(std::any_cast<const ${hostType.datatype}&>(std::get<CustomEncodableValue>($encodableArgName)));');
@@ -1342,6 +1381,9 @@
       } else if (hostType.isBuiltin) {
         indent.writeln(
             'const auto& $argName = std::get<${hostType.datatype}>($encodableArgName);');
+      } else if (hostType.isEnum) {
+        indent.writeln(
+            'const ${hostType.datatype}& $argName = (${hostType.datatype})$encodableArgName.LongValue();');
       } else {
         indent.writeln(
             'const auto& $argName = std::any_cast<const ${hostType.datatype}&>(std::get<CustomEncodableValue>($encodableArgName));');
@@ -1390,7 +1432,11 @@
 /// Returns a non-nullable variant of [type].
 HostDatatype _nonNullableType(HostDatatype type) {
   return HostDatatype(
-      datatype: type.datatype, isBuiltin: type.isBuiltin, isNullable: false);
+    datatype: type.datatype,
+    isBuiltin: type.isBuiltin,
+    isNullable: false,
+    isEnum: type.isEnum,
+  );
 }
 
 String _pascalCaseFromCamelCase(String camelCase) =>
diff --git a/packages/pigeon/lib/dart_generator.dart b/packages/pigeon/lib/dart_generator.dart
index d712ee5..fe13c75 100644
--- a/packages/pigeon/lib/dart_generator.dart
+++ b/packages/pigeon/lib/dart_generator.dart
@@ -411,7 +411,7 @@
                     final String leftHandSide = 'final $argType? $argName';
                     if (customEnumNames.contains(arg.type.baseName)) {
                       indent.writeln(
-                          '$leftHandSide = $argsArray[$count] == null ? null : $argType.values[$argsArray[$count] as int];');
+                          '$leftHandSide = $argsArray[$count] == null ? null : $argType.values[$argsArray[$count]! as int];');
                     } else {
                       indent.writeln(
                           '$leftHandSide = ($argsArray[$count] as $genericArgType?)${castCall.isEmpty ? '' : '?$castCall'};');
@@ -442,10 +442,15 @@
                   } else {
                     indent.writeln('final $returnType output = $call;');
                   }
+
                   const String returnExpression = 'output';
+                  final String nullability =
+                      func.returnType.isNullable ? '?' : '';
+                  final String valueExtraction =
+                      isEnum(root, func.returnType) ? '$nullability.index' : '';
                   final String returnStatement = isMockHandler
-                      ? 'return <Object?>[$returnExpression];'
-                      : 'return $returnExpression;';
+                      ? 'return <Object?>[$returnExpression$valueExtraction];'
+                      : 'return $returnExpression$valueExtraction;';
                   indent.writeln(returnStatement);
                 }
               });
@@ -487,6 +492,8 @@
       codecName = _getCodecName(api);
       _writeCodec(indent, codecName, api, root);
     }
+    final List<String> customEnumNames =
+        root.enums.map((Enum x) => x.name).toList();
     indent.newln();
     bool first = true;
     addDocumentationComments(
@@ -553,9 +560,21 @@
           final String nullHandler = func.returnType.isNullable
               ? (genericCastCall.isEmpty ? '' : '?')
               : '!';
-          final String returnStatement = func.returnType.isVoid
-              ? 'return;'
-              : 'return $nullablyTypedAccessor$nullHandler$genericCastCall;';
+          String returnStatement = 'return';
+          if (customEnumNames.contains(returnType)) {
+            if (func.returnType.isNullable) {
+              returnStatement =
+                  '$returnStatement ($accessor as int?) == null ? null : $returnType.values[$accessor! as int]';
+            } else {
+              returnStatement =
+                  '$returnStatement $returnType.values[$accessor! as int]';
+            }
+          } else if (!func.returnType.isVoid) {
+            returnStatement =
+                '$returnStatement $nullablyTypedAccessor$nullHandler$genericCastCall';
+          }
+          returnStatement = '$returnStatement;';
+
           indent.format('''
 final List<Object?>? replyList =
 \t\tawait channel.send($sendArgument) as List<Object?>?;
diff --git a/packages/pigeon/lib/generator_tools.dart b/packages/pigeon/lib/generator_tools.dart
index 19e0570..b6e6f72 100644
--- a/packages/pigeon/lib/generator_tools.dart
+++ b/packages/pigeon/lib/generator_tools.dart
@@ -13,7 +13,7 @@
 /// The current version of pigeon.
 ///
 /// This must match the version in pubspec.yaml.
-const String pigeonVersion = '10.1.6';
+const String pigeonVersion = '11.0.0';
 
 /// Read all the content from [stdin] to a String.
 String readStdin() {
@@ -176,6 +176,7 @@
     required this.datatype,
     required this.isBuiltin,
     required this.isNullable,
+    required this.isEnum,
   });
 
   /// The [String] that can be printed into host code to represent the type.
@@ -186,6 +187,9 @@
 
   /// `true` if the type corresponds to a nullable Dart datatype.
   final bool isNullable;
+
+  /// `true if the type is a custom enum.
+  final bool isEnum;
 }
 
 /// Calculates the [HostDatatype] for the provided [NamedType].
@@ -226,20 +230,32 @@
           ? customResolver(type.baseName)
           : type.baseName;
       return HostDatatype(
-          datatype: customName, isBuiltin: false, isNullable: type.isNullable);
+        datatype: customName,
+        isBuiltin: false,
+        isNullable: type.isNullable,
+        isEnum: false,
+      );
     } else if (enums.map((Enum x) => x.name).contains(type.baseName)) {
       final String customName = customResolver != null
           ? customResolver(type.baseName)
           : type.baseName;
       return HostDatatype(
-          datatype: customName, isBuiltin: false, isNullable: type.isNullable);
+        datatype: customName,
+        isBuiltin: false,
+        isNullable: type.isNullable,
+        isEnum: true,
+      );
     } else {
       throw Exception(
           'unrecognized datatype ${fieldName == null ? '' : 'for field:"$fieldName" '}of type:"${type.baseName}"');
     }
   } else {
     return HostDatatype(
-        datatype: datatype, isBuiltin: true, isNullable: type.isNullable);
+      datatype: datatype,
+      isBuiltin: true,
+      isNullable: type.isNullable,
+      isEnum: false,
+    );
   }
 }
 
@@ -574,6 +590,15 @@
   }
 }
 
+/// Enum to specify api type when generating code.
+enum ApiType {
+  /// Flutter api.
+  flutter,
+
+  /// Host api.
+  host,
+}
+
 /// Enum to specify which file will be generated for multi-file generators
 enum FileType {
   /// header file.
diff --git a/packages/pigeon/lib/java_generator.dart b/packages/pigeon/lib/java_generator.dart
index ac8d69c..8d58f37 100644
--- a/packages/pigeon/lib/java_generator.dart
+++ b/packages/pigeon/lib/java_generator.dart
@@ -382,7 +382,7 @@
         indent.writeln('Object $fieldVariable = list.get($index);');
         if (customEnumNames.contains(field.type.baseName)) {
           indent.writeln(
-              '$result.$setter(${_intToEnum(fieldVariable, field.type.baseName)});');
+              '$result.$setter(${_intToEnum(fieldVariable, field.type.baseName, field.type.isNullable)});');
         } else {
           indent.writeln(
               '$result.$setter(${_castObject(field, root.classes, root.enums, fieldVariable)});');
@@ -452,6 +452,17 @@
         }
       });
 
+      /// Returns an argument name that can be used in a context where it is possible to collide
+      /// and append `.index` to enums.
+      String getEnumSafeArgumentExpression(int count, NamedType argument) {
+        if (isEnum(root, argument.type)) {
+          return argument.type.isNullable
+              ? '${_getArgumentName(count, argument)}Arg == null ? null : ${_getArgumentName(count, argument)}Arg.index'
+              : '${_getArgumentName(count, argument)}Arg.index';
+        }
+        return '${_getArgumentName(count, argument)}Arg';
+      }
+
       for (final Method func in api.methods) {
         final String channelName = makeChannelName(api, func, dartPackageName);
         final String returnType = func.returnType.isVoid
@@ -469,12 +480,14 @@
               .map((NamedType e) => _nullsafeJavaTypeForDartType(e.type));
           final Iterable<String> argNames =
               indexMap(func.arguments, _getSafeArgumentName);
+          final Iterable<String> enumSafeArgNames =
+              indexMap(func.arguments, getEnumSafeArgumentExpression);
           if (func.arguments.length == 1) {
             sendArgument =
-                'new ArrayList<Object>(Collections.singletonList(${argNames.first}))';
+                'new ArrayList<Object>(Collections.singletonList(${enumSafeArgNames.first}))';
           } else {
             sendArgument =
-                'new ArrayList<Object>(Arrays.asList(${argNames.join(', ')}))';
+                'new ArrayList<Object>(Arrays.asList(${enumSafeArgNames.join(', ')}))';
           }
           final String argsSignature =
               map2(argTypes, argNames, (String x, String y) => '$x $y')
@@ -504,6 +517,14 @@
                 if (func.returnType.baseName == 'int') {
                   indent.writeln(
                       '$returnType $output = channelReply == null ? null : ((Number) channelReply).longValue();');
+                } else if (isEnum(root, func.returnType)) {
+                  if (func.returnType.isNullable) {
+                    indent.writeln(
+                        '$returnType $output = channelReply == null ? null : $returnType.values()[(int) channelReply];');
+                  } else {
+                    indent.writeln(
+                        '$returnType $output = $returnType.values()[(int) channelReply];');
+                  }
                 } else {
                   indent.writeln(
                       '$returnType $output = ${_cast('channelReply', javaType: returnType)};');
@@ -673,6 +694,7 @@
         indent.nest(2, () {
           indent.write('(message, reply) -> ');
           indent.addScoped('{', '});', () {
+            String enumTag = '';
             final String returnType = method.returnType.isVoid
                 ? 'Void'
                 : _javaTypeForDartType(method.returnType);
@@ -695,7 +717,8 @@
                     : argName;
                 String accessor = 'args.get($index)';
                 if (isEnum(root, arg.type)) {
-                  accessor = _intToEnum(accessor, arg.type.baseName);
+                  accessor = _intToEnum(
+                      accessor, arg.type.baseName, arg.type.isNullable);
                 } else if (argType != 'Object') {
                   accessor = _cast(accessor, javaType: argType);
                 }
@@ -706,12 +729,17 @@
             if (method.isAsynchronous) {
               final String resultValue =
                   method.returnType.isVoid ? 'null' : 'result';
+              if (isEnum(root, method.returnType)) {
+                enumTag = method.returnType.isNullable
+                    ? ' == null ? null : $resultValue.index'
+                    : '.index';
+              }
               const String resultName = 'resultCallback';
               indent.format('''
 Result<$returnType> $resultName =
 \t\tnew Result<$returnType>() {
 \t\t\tpublic void success($returnType result) {
-\t\t\t\twrapped.add(0, $resultValue);
+\t\t\t\twrapped.add(0, $resultValue$enumTag);
 \t\t\t\treply.reply(wrapped);
 \t\t\t}
 
@@ -734,8 +762,13 @@
                   indent.writeln('$call;');
                   indent.writeln('wrapped.add(0, null);');
                 } else {
+                  if (isEnum(root, method.returnType)) {
+                    enumTag = method.returnType.isNullable
+                        ? ' == null ? null : output.index'
+                        : '.index';
+                  }
                   indent.writeln('$returnType output = $call;');
-                  indent.writeln('wrapped.add(0, output);');
+                  indent.writeln('wrapped.add(0, output$enumTag);');
                 }
               });
               indent.add(' catch (Throwable exception) ');
@@ -906,8 +939,9 @@
 
 /// Converts an expression that evaluates to an nullable int to an expression
 /// that evaluates to a nullable enum.
-String _intToEnum(String expression, String enumName) =>
-    '$expression == null ? null : $enumName.values()[(int) $expression]';
+String _intToEnum(String expression, String enumName, bool nullable) => nullable
+    ? '$expression == null ? null : $enumName.values()[(int) $expression]'
+    : '$enumName.values()[(int) $expression]';
 
 String _getArgumentName(int count, NamedType argument) =>
     argument.name.isEmpty ? 'arg$count' : argument.name;
diff --git a/packages/pigeon/lib/kotlin_generator.dart b/packages/pigeon/lib/kotlin_generator.dart
index 78472df..da6faf4 100644
--- a/packages/pigeon/lib/kotlin_generator.dart
+++ b/packages/pigeon/lib/kotlin_generator.dart
@@ -282,10 +282,11 @@
               });
             } else if (isInt) {
               indent.write('val ${field.name} = $listValue');
-              indent.addln('.let { ${_cast(listValue, type: field.type)} }');
+              indent.addln(
+                  '.let { ${_cast(root, indent, listValue, type: field.type)} }');
             } else {
               indent.writeln(
-                  'val ${field.name} = ${_cast(listValue, type: field.type)}');
+                  'val ${field.name} = ${_cast(root, indent, listValue, type: field.type)}');
             }
           } else {
             if (!hostDatatype.isBuiltin &&
@@ -298,10 +299,11 @@
                   'val ${field.name} = $fieldType.ofRaw($listValue as Int)!!');
             } else if (isInt) {
               indent.write('val ${field.name} = $listValue');
-              indent.addln('.let { ${_cast(listValue, type: field.type)} }');
+              indent.addln(
+                  '.let { ${_cast(root, indent, listValue, type: field.type)} }');
             } else {
               indent.writeln(
-                  'val ${field.name} = ${_cast(listValue, type: field.type)}');
+                  'val ${field.name} = ${_cast(root, indent, listValue, type: field.type)}');
             }
           }
         });
@@ -403,7 +405,11 @@
               .map((NamedType e) => _nullsafeKotlinTypeForDartType(e.type));
           final Iterable<String> argNames =
               indexMap(func.arguments, _getSafeArgumentName);
-          sendArgument = 'listOf(${argNames.join(', ')})';
+          final Iterable<String> enumSafeArgNames = indexMap(
+              func.arguments,
+              (int count, NamedType type) =>
+                  _getEnumSafeArgumentExpression(root, count, type));
+          sendArgument = 'listOf(${enumSafeArgNames.join(', ')})';
           final String argsSignature = map2(argTypes, argNames,
               (String type, String name) => '$name: $type').join(', ');
           if (func.returnType.isVoid) {
@@ -425,8 +431,15 @@
             });
           } else {
             indent.addScoped('{', '}', () {
-              indent.writeln(
-                  'val result = ${_cast('it', type: func.returnType)}');
+              // Nullable enums require special handling.
+              if (isEnum(root, func.returnType) && func.returnType.isNullable) {
+                indent.writeScoped('val result = (it as Int?)?.let {', '}', () {
+                  indent.writeln('${func.returnType.baseName}.ofRaw(it)');
+                });
+              } else {
+                indent.writeln(
+                    'val result = ${_cast(root, indent, 'it', type: func.returnType)}');
+              }
               indent.writeln('callback(result)');
             });
           }
@@ -556,7 +569,7 @@
                       final String argName = _getSafeArgumentName(index, arg);
                       final String argIndex = 'args[$index]';
                       indent.writeln(
-                          'val $argName = ${_castForceUnwrap(argIndex, arg.type, root)}');
+                          'val $argName = ${_castForceUnwrap(argIndex, arg.type, root, indent)}');
                       methodArguments.add(argName);
                     });
                   }
@@ -575,11 +588,17 @@
                         indent.writeln('reply.reply(wrapError(error))');
                       }, addTrailingNewline: false);
                       indent.addScoped(' else {', '}', () {
+                        final String enumTagNullablePrefix =
+                            method.returnType.isNullable ? '?' : '!!';
+                        final String enumTag = isEnum(root, method.returnType)
+                            ? '$enumTagNullablePrefix.raw'
+                            : '';
                         if (method.returnType.isVoid) {
                           indent.writeln('reply.reply(wrapResult(null))');
                         } else {
                           indent.writeln('val data = result.getOrNull()');
-                          indent.writeln('reply.reply(wrapResult(data))');
+                          indent
+                              .writeln('reply.reply(wrapResult(data$enumTag))');
                         }
                       });
                     });
@@ -591,7 +610,13 @@
                         indent.writeln(call);
                         indent.writeln('wrapped = listOf<Any?>(null)');
                       } else {
-                        indent.writeln('wrapped = listOf<Any?>($call)');
+                        String enumTag = '';
+                        if (isEnum(root, method.returnType)) {
+                          final String safeUnwrap =
+                              method.returnType.isNullable ? '?' : '';
+                          enumTag = '$safeUnwrap.raw';
+                        }
+                        indent.writeln('wrapped = listOf<Any?>($call$enumTag)');
                       }
                     }, addTrailingNewline: false);
                     indent.add(' catch (exception: Throwable) ');
@@ -736,24 +761,37 @@
 String _getArgumentName(int count, NamedType argument) =>
     argument.name.isEmpty ? 'arg$count' : argument.name;
 
+/// Returns an argument name that can be used in a context where it is possible to collide
+/// and append `.index` to enums.
+String _getEnumSafeArgumentExpression(
+    Root root, int count, NamedType argument) {
+  if (isEnum(root, argument.type)) {
+    return argument.type.isNullable
+        ? '${_getArgumentName(count, argument)}Arg?.raw'
+        : '${_getArgumentName(count, argument)}Arg.raw';
+  }
+  return '${_getArgumentName(count, argument)}Arg';
+}
+
 /// Returns an argument name that can be used in a context where it is possible to collide.
 String _getSafeArgumentName(int count, NamedType argument) =>
     '${_getArgumentName(count, argument)}Arg';
 
-String _castForceUnwrap(String value, TypeDeclaration type, Root root) {
+String _castForceUnwrap(
+    String value, TypeDeclaration type, Root root, Indent indent) {
   if (isEnum(root, type)) {
     final String forceUnwrap = type.isNullable ? '' : '!!';
     final String nullableConditionPrefix =
-        type.isNullable ? '$value == null ? null : ' : '';
+        type.isNullable ? 'if ($value == null) null else ' : '';
     return '$nullableConditionPrefix${_kotlinTypeForDartType(type)}.ofRaw($value as Int)$forceUnwrap';
   } else {
     // The StandardMessageCodec can give us [Integer, Long] for
     // a Dart 'int'.  To keep things simple we just use 64bit
     // longs in Pigeon with Kotlin.
     if (type.baseName == 'int') {
-      return '$value.let { ${_cast(value, type: type)} }';
+      return '$value.let { ${_cast(root, indent, value, type: type)} }';
     } else {
-      return _cast(value, type: type);
+      return _cast(root, indent, value, type: type);
     }
   }
 }
@@ -819,7 +857,8 @@
 }
 
 /// Returns an expression to cast [variable] to [kotlinType].
-String _cast(String variable, {required TypeDeclaration type}) {
+String _cast(Root root, Indent indent, String variable,
+    {required TypeDeclaration type}) {
   // Special-case Any, since no-op casts cause warnings.
   final String typeString = _kotlinTypeForDartType(type);
   if (type.isNullable && typeString == 'Any') {
@@ -828,6 +867,14 @@
   if (typeString == 'Int' || typeString == 'Long') {
     return _castInt(type.isNullable);
   }
+  if (isEnum(root, type)) {
+    if (type.isNullable) {
+      return '($variable as Int?)?.let {\n'
+          '${indent.str}  $typeString.ofRaw(it)\n'
+          '${indent.str}}';
+    }
+    return '${type.baseName}.ofRaw($variable as Int)!!';
+  }
   return '$variable as ${_nullsafeKotlinTypeForDartType(type)}';
 }
 
diff --git a/packages/pigeon/lib/objc_generator.dart b/packages/pigeon/lib/objc_generator.dart
index 53e4672..baac4e3 100644
--- a/packages/pigeon/lib/objc_generator.dart
+++ b/packages/pigeon/lib/objc_generator.dart
@@ -141,7 +141,8 @@
     Enum anEnum, {
     required String dartPackageName,
   }) {
-    final String enumName = _className(generatorOptions.prefix, anEnum.name);
+    final String enumName =
+        _enumName(anEnum.name, prefix: generatorOptions.prefix);
     indent.newln();
     addDocumentationComments(
         indent, anEnum.documentationComments, _docCommentSpec);
@@ -156,6 +157,17 @@
             '$enumName${member.name[0].toUpperCase()}${member.name.substring(1)} = $index,');
       });
     });
+    _writeEnumWrapper(indent, enumName);
+  }
+
+  void _writeEnumWrapper(Indent indent, String enumName) {
+    indent.newln();
+    indent.writeln('/// Wrapper for $enumName to allow for nullability.');
+    indent.writeln(
+        '@interface ${_enumName(enumName, prefix: '', box: true)} : NSObject');
+    indent.writeln('@property(nonatomic, assign) $enumName value;');
+    indent.writeln('- (instancetype)initWithValue:($enumName)value;');
+    indent.writeln('@end');
   }
 
   @override
@@ -205,7 +217,14 @@
         indent.writeln('- (instancetype)init NS_UNAVAILABLE;');
       }
       _writeObjcSourceClassInitializerDeclaration(
-          indent, klass, classes, enums, prefix);
+        indent,
+        generatorOptions,
+        root,
+        klass,
+        classes,
+        enums,
+        prefix,
+      );
       indent.addln(';');
     }
     for (final NamedType field in getFieldsInSerializationOrder(klass)) {
@@ -215,20 +234,26 @@
           enums,
           (TypeDeclaration x) => _objcTypePtrForPrimitiveDartType(prefix, x),
           customResolver: customEnumNames.contains(field.type.baseName)
-              ? (String x) => _className(prefix, x)
+              ? (String x) => _enumName(x, prefix: prefix)
               : (String x) => '${_className(prefix, x)} *');
       late final String propertyType;
       addDocumentationComments(
           indent, field.documentationComments, _docCommentSpec);
-      if (customEnumNames.contains(field.type.baseName)) {
+      if (customEnumNames.contains(field.type.baseName) &&
+          !field.type.isNullable) {
         propertyType = 'assign';
       } else {
         propertyType = _propertyTypeForDartType(field);
       }
-      final String nullability =
-          _isNullable(hostDatatype, field.type) ? ', nullable' : '';
+      final String nullability = field.type.isNullable ? ', nullable' : '';
+      final String fieldType = isEnum(root, field.type) && field.type.isNullable
+          ? _enumName(field.type.baseName,
+              suffix: ' *',
+              prefix: generatorOptions.prefix,
+              box: field.type.isNullable)
+          : hostDatatype.datatype;
       indent.writeln(
-          '@property(nonatomic, $propertyType$nullability) ${hostDatatype.datatype} ${field.name};');
+          '@property(nonatomic, $propertyType$nullability) $fieldType ${field.name};');
     }
     indent.writeln('@end');
     indent.newln();
@@ -291,7 +316,8 @@
     for (final Method func in api.methods) {
       final _ObjcPtr returnType =
           _objcTypeForDartType(generatorOptions.prefix, func.returnType);
-      final String callbackType = _callbackForType(func.returnType, returnType);
+      final String callbackType =
+          _callbackForType(root, func.returnType, returnType, generatorOptions);
       addDocumentationComments(
           indent, func.documentationComments, _docCommentSpec);
 
@@ -302,6 +328,7 @@
         lastArgName: 'completion',
         lastArgType: callbackType,
         isEnum: (TypeDeclaration t) => isEnum(root, t),
+        root: root,
       )};');
     }
     indent.writeln('@end');
@@ -333,20 +360,32 @@
       String? lastArgName;
       String? lastArgType;
       String? returnType;
+      final String enumReturnType = _enumName(
+        returnTypeName.baseName,
+        suffix: func.returnType.isNullable ? ' *_Nullable' : '',
+        prefix: generatorOptions.prefix,
+        box: func.returnType.isNullable,
+      );
       if (func.isAsynchronous) {
         returnType = 'void';
+        lastArgName = 'completion';
         if (func.returnType.isVoid) {
           lastArgType = 'void (^)(FlutterError *_Nullable)';
-          lastArgName = 'completion';
+        } else if (isEnum(root, func.returnType)) {
+          lastArgType = 'void (^)($enumReturnType, FlutterError *_Nullable)';
         } else {
           lastArgType =
-              'void (^)(${returnTypeName.ptr}_Nullable, FlutterError *_Nullable)';
-          lastArgName = 'completion';
+              'void (^)(${returnTypeName.withPtr}_Nullable, FlutterError *_Nullable)';
         }
       } else {
-        returnType = func.returnType.isVoid
-            ? 'void'
-            : 'nullable ${returnTypeName.ptr.trim()}';
+        if (func.returnType.isVoid) {
+          returnType = 'void';
+        } else if (isEnum(root, func.returnType)) {
+          returnType = enumReturnType;
+        } else {
+          returnType = 'nullable ${returnTypeName.withPtr.trim()}';
+        }
+
         lastArgType = 'FlutterError *_Nullable *_Nonnull';
         lastArgName = 'error';
       }
@@ -367,6 +406,7 @@
         lastArgName: lastArgName,
         lastArgType: lastArgType,
         isEnum: (TypeDeclaration t) => isEnum(root, t),
+        root: root,
       );
       indent.writeln('$signature;');
     }
@@ -421,6 +461,33 @@
   }
 
   @override
+  void writeEnum(
+    ObjcOptions generatorOptions,
+    Root root,
+    Indent indent,
+    Enum anEnum, {
+    required String dartPackageName,
+  }) {
+    final String enumName =
+        _enumName(anEnum.name, prefix: generatorOptions.prefix);
+    addDocumentationComments(
+        indent, anEnum.documentationComments, _docCommentSpec);
+    indent.writeln(
+        '@implementation ${_enumName(enumName, prefix: '', box: true)}');
+    indent.writeScoped('- (instancetype)initWithValue:($enumName)value {', '}',
+        () {
+      indent.writeln('self = [super init];');
+      indent.writeScoped('if (self) {', '}', () {
+        indent.writeln('_value = value;');
+      });
+
+      indent.writeln('return self;');
+    });
+    indent.writeln('@end');
+    indent.newln();
+  }
+
+  @override
   void writeDataClasses(
     ObjcOptions generatorOptions,
     Root root,
@@ -522,8 +589,16 @@
       enumerate(getFieldsInSerializationOrder(klass),
           (int index, final NamedType field) {
         if (customEnumNames.contains(field.type.baseName)) {
-          indent.writeln(
-              '$resultName.${field.name} = [${_listGetter(customClassNames, 'list', field, index, generatorOptions.prefix)} integerValue];');
+          if (field.type.isNullable) {
+            indent.writeln(
+                'NSNumber *${field.name}AsNumber = GetNullableObjectAtIndex(list, $index);');
+            indent.writeln(
+                '${_enumName(field.type.baseName, suffix: ' *', prefix: generatorOptions.prefix, box: true)}${field.name} = ${field.name}AsNumber == nil ? nil : [[${_enumName(field.type.baseName, prefix: generatorOptions.prefix, box: true)} alloc] initWithValue: [${field.name}AsNumber integerValue]];');
+            indent.writeln('$resultName.${field.name} = ${field.name};');
+          } else {
+            indent.writeln(
+                '$resultName.${field.name} = [${_listGetter(customClassNames, 'list', field, index, generatorOptions.prefix)} integerValue];');
+          }
         } else {
           indent.writeln(
               '$resultName.${field.name} = ${_listGetter(customClassNames, 'list', field, index, generatorOptions.prefix)};');
@@ -637,18 +712,31 @@
 
   void _writeChannelApiBinding(ObjcOptions generatorOptions, Root root,
       Indent indent, String apiName, Method func, String channel) {
-    void unpackArgs(String variable, Iterable<String> argNames) {
+    void unpackArgs(String variable) {
       indent.writeln('NSArray *args = $variable;');
-      map3(wholeNumbers.take(func.arguments.length), argNames, func.arguments,
-          (int count, String argName, NamedType arg) {
+      int count = 0;
+      for (final NamedType arg in func.arguments) {
+        final String argName = _getSafeArgName(count, arg);
         if (isEnum(root, arg.type)) {
-          return '${_className(generatorOptions.prefix, arg.type.baseName)} $argName = [GetNullableObjectAtIndex(args, $count) integerValue];';
+          final String className =
+              _className(generatorOptions.prefix, arg.type.baseName);
+          if (arg.type.isNullable) {
+            indent.writeln(
+                'NSNumber *${argName}AsNumber = GetNullableObjectAtIndex(args, $count);');
+            indent.writeln(
+                '${_enumName(arg.type.baseName, suffix: ' *', prefix: '', box: true)}$argName = ${argName}AsNumber == nil ? nil : [[${_enumName(arg.type.baseName, prefix: generatorOptions.prefix, box: true)} alloc] initWithValue: [${argName}AsNumber integerValue]];');
+          } else {
+            indent.writeln(
+                '$className $argName = [GetNullableObjectAtIndex(args, $count) integerValue];');
+          }
         } else {
           final _ObjcPtr argType =
               _objcTypeForDartType(generatorOptions.prefix, arg.type);
-          return '${argType.ptr}$argName = GetNullableObjectAtIndex(args, $count);';
+          indent.writeln(
+              '${argType.withPtr}$argName = GetNullableObjectAtIndex(args, $count);');
         }
-      }).forEach(indent.writeln);
+        count++;
+      }
     }
 
     void writeAsyncBindings(Iterable<String> selectorComponents,
@@ -670,16 +758,35 @@
         }
       } else {
         const String callback = 'callback(wrapResult(output, error));';
+        String returnTypeString = '${returnType.withPtr}_Nullable output';
+        const String numberOutput = 'NSNumber *output =';
+        final String enumConversionExpression = func.returnType.isNullable
+            ? 'enumValue == nil ? nil : [NSNumber numberWithInteger:enumValue.value];'
+            : '[NSNumber numberWithInteger:enumValue];';
+        if (isEnum(root, func.returnType)) {
+          if (func.returnType.isNullable) {
+            returnTypeString =
+                '${_enumName(returnType.baseName, suffix: ' *_Nullable', prefix: generatorOptions.prefix, box: true)} enumValue';
+          } else {
+            returnTypeString = '${returnType.baseName} enumValue';
+          }
+        }
         if (func.arguments.isEmpty) {
           indent.writeScoped(
-              '[api ${selectorComponents.first}:^(${returnType.ptr}_Nullable output, FlutterError *_Nullable error) {',
+              '[api ${selectorComponents.first}:^($returnTypeString, FlutterError *_Nullable error) {',
               '}];', () {
+            if (isEnum(root, func.returnType)) {
+              indent.writeln('$numberOutput $enumConversionExpression');
+            }
             indent.writeln(callback);
           });
         } else {
           indent.writeScoped(
-              '[api $callSignature ${selectorComponents.last}:^(${returnType.ptr}_Nullable output, FlutterError *_Nullable error) {',
+              '[api $callSignature ${selectorComponents.last}:^($returnTypeString, FlutterError *_Nullable error) {',
               '}];', () {
+            if (isEnum(root, func.returnType)) {
+              indent.writeln('$numberOutput $enumConversionExpression');
+            }
             indent.writeln(callback);
           });
         }
@@ -692,7 +799,20 @@
         indent.writeln('$call;');
         indent.writeln('callback(wrapResult(nil, error));');
       } else {
-        indent.writeln('${returnType.ptr}output = $call;');
+        if (isEnum(root, func.returnType)) {
+          if (func.returnType.isNullable) {
+            indent.writeln(
+                '${_enumName(func.returnType.baseName, suffix: ' *', prefix: generatorOptions.prefix, box: true)} enumBox = $call;');
+            indent.writeln(
+                'NSNumber *output = enumBox == nil ? nil : [NSNumber numberWithInteger:enumBox.value];');
+          } else {
+            indent.writeln('${returnType.baseName} enumValue = $call;');
+            indent.writeln(
+                'NSNumber *output = [NSNumber numberWithInteger:enumValue];');
+          }
+        } else {
+          indent.writeln('${returnType.withPtr}output = $call;');
+        }
         indent.writeln('callback(wrapResult(output, error));');
       }
     }
@@ -718,7 +838,7 @@
         return '$selectorComponent:$argName';
       }).join(' ');
       if (func.arguments.isNotEmpty) {
-        unpackArgs('message', argNames);
+        unpackArgs('message');
       }
       if (func.isAsynchronous) {
         writeAsyncBindings(selectorComponents, callSignature, returnType);
@@ -803,7 +923,14 @@
     String className,
   ) {
     _writeObjcSourceClassInitializerDeclaration(
-        indent, klass, root.classes, root.enums, languageOptions.prefix);
+      indent,
+      languageOptions,
+      root,
+      klass,
+      root.classes,
+      root.enums,
+      languageOptions.prefix,
+    );
     indent.writeScoped(' {', '}', () {
       const String result = 'pigeonResult';
       indent.writeln('$className* $result = [[$className alloc] init];');
@@ -927,16 +1054,32 @@
   }) {
     final _ObjcPtr returnType =
         _objcTypeForDartType(languageOptions.prefix, func.returnType);
-    final String callbackType = _callbackForType(func.returnType, returnType);
+    final String callbackType =
+        _callbackForType(root, func.returnType, returnType, languageOptions);
 
     String argNameFunc(int count, NamedType arg) => _getSafeArgName(count, arg);
-    final Iterable<String> argNames = indexMap(func.arguments, argNameFunc);
     String sendArgument;
     if (func.arguments.isEmpty) {
       sendArgument = 'nil';
     } else {
-      String makeVarOrNSNullExpression(String x) => '$x ?: [NSNull null]';
-      sendArgument = '@[${argNames.map(makeVarOrNSNullExpression).join(', ')}]';
+      int count = 0;
+      String makeVarOrNSNullExpression(NamedType arg) {
+        String varExpression = '${argNameFunc(count, arg)} ?: [NSNull null]';
+        if (isEnum(root, arg.type)) {
+          if (arg.type.isNullable) {
+            varExpression =
+                '${argNameFunc(count, arg)} == nil ? [NSNull null] : [NSNumber numberWithInteger:${argNameFunc(count, arg)}.value]';
+          } else {
+            varExpression =
+                '[NSNumber numberWithInteger: ${argNameFunc(count, arg)}]';
+          }
+        }
+        count++;
+        return varExpression;
+      }
+
+      sendArgument =
+          '@[${func.arguments.map(makeVarOrNSNullExpression).join(', ')}]';
     }
     indent.write(_makeObjcSignature(
       func: func,
@@ -946,6 +1089,7 @@
       lastArgType: callbackType,
       argNameFunc: argNameFunc,
       isEnum: (TypeDeclaration t) => isEnum(root, t),
+      root: root,
     ));
     indent.addScoped(' {', '}', () {
       indent.writeln('FlutterBasicMessageChannel *channel =');
@@ -965,7 +1109,19 @@
         if (func.returnType.isVoid) {
           indent.writeln('completion(nil);');
         } else {
-          indent.writeln('${returnType.ptr}output = reply;');
+          if (isEnum(root, func.returnType)) {
+            if (func.returnType.isNullable) {
+              indent.writeln(
+                  'NSNumber *outputAsNumber = reply == [NSNull null] ? nil : reply;');
+              indent.writeln(
+                  '${_enumName(returnType.baseName, suffix: ' *', prefix: languageOptions.prefix, box: true)}output = outputAsNumber == nil ? nil : [[${_enumName(returnType.baseName, prefix: languageOptions.prefix, box: true)} alloc] initWithValue: [outputAsNumber integerValue]];');
+            } else {
+              indent.writeln(
+                  '${returnType.baseName} output = [reply integerValue];');
+            }
+          } else {
+            indent.writeln('${returnType.withPtr}output = reply;');
+          }
           indent.writeln('completion(output, nil);');
         }
       });
@@ -976,8 +1132,14 @@
 /// Writes the method declaration for the initializer.
 ///
 /// Example '+ (instancetype)makeWithFoo:(NSString *)foo'
-void _writeObjcSourceClassInitializerDeclaration(Indent indent, Class klass,
-    List<Class> classes, List<Enum> enums, String? prefix) {
+void _writeObjcSourceClassInitializerDeclaration(
+    Indent indent,
+    ObjcOptions generatorOptions,
+    Root root,
+    Class klass,
+    List<Class> classes,
+    List<Enum> enums,
+    String? prefix) {
   final List<String> customEnumNames = enums.map((Enum x) => x.name).toList();
   indent.write('+ (instancetype)makeWith');
   bool isFirst = true;
@@ -997,15 +1159,20 @@
           enums,
           (TypeDeclaration x) => _objcTypePtrForPrimitiveDartType(prefix, x),
           customResolver: customEnumNames.contains(field.type.baseName)
-              ? (String x) => _className(prefix, x)
+              ? (String x) => field.type.isNullable
+                  ? _enumName(x, suffix: ' *', prefix: prefix, box: true)
+                  : _enumName(x, prefix: prefix)
               : (String x) => '${_className(prefix, x)} *');
-      final String nullable =
-          _isNullable(hostDatatype, field.type) ? 'nullable ' : '';
+      final String nullable = field.type.isNullable ? 'nullable ' : '';
       printer('$label:($nullable${hostDatatype.datatype})${field.name}');
     }
   });
 }
 
+String _enumName(String name,
+        {required String? prefix, String suffix = '', bool box = false}) =>
+    '${prefix ?? ''}$name${box ? 'Box' : ''}$suffix';
+
 /// Calculates the ObjC class name, possibly prefixed.
 String _className(String? prefix, String className) {
   if (prefix != null) {
@@ -1016,10 +1183,18 @@
 }
 
 /// Calculates callback block signature for async methods.
-String _callbackForType(TypeDeclaration type, _ObjcPtr objcType) {
-  return type.isVoid
-      ? 'void (^)(FlutterError *_Nullable)'
-      : 'void (^)(${objcType.ptr}_Nullable, FlutterError *_Nullable)';
+String _callbackForType(
+    Root root, TypeDeclaration type, _ObjcPtr objcType, ObjcOptions options) {
+  if (type.isVoid) {
+    return 'void (^)(FlutterError *_Nullable)';
+  } else if (isEnum(root, type)) {
+    if (type.isNullable) {
+      return 'void (^)(${_enumName(objcType.baseName, suffix: ' *_Nullable', prefix: options.prefix, box: true)}, FlutterError *_Nullable)';
+    }
+    return 'void (^)(${_enumName(objcType.baseName, prefix: options.prefix)}, FlutterError *_Nullable)';
+  } else {
+    return 'void (^)(${objcType.withPtr}_Nullable, FlutterError *_Nullable)';
+  }
 }
 
 /// Represents an ObjC pointer (ex 'id', 'NSString *').
@@ -1027,7 +1202,8 @@
   const _ObjcPtr({required this.baseName}) : hasAsterisk = baseName != 'id';
   final String baseName;
   final bool hasAsterisk;
-  String get ptr => '$baseName${hasAsterisk ? ' *' : ' '}';
+  String get withPtr => '$baseName${hasAsterisk ? ' *' : ' '}';
+  String get ptr => hasAsterisk ? '*' : '';
 }
 
 /// Maps between Dart types to ObjC pointer types (ex 'String' => 'NSString *').
@@ -1051,7 +1227,7 @@
 String _flattenTypeArguments(String? classPrefix, List<TypeDeclaration> args) {
   final String result = args
       .map<String>((TypeDeclaration e) =>
-          _objcTypeForDartType(classPrefix, e).ptr.trim())
+          _objcTypeForDartType(classPrefix, e).withPtr.trim())
       .join(', ');
   return result;
 }
@@ -1059,7 +1235,7 @@
 String? _objcTypePtrForPrimitiveDartType(
     String? classPrefix, TypeDeclaration type) {
   return _objcTypeForDartTypeMap.containsKey(type.baseName)
-      ? _objcTypeForDartType(classPrefix, type).ptr
+      ? _objcTypeForDartType(classPrefix, type).withPtr
       : null;
 }
 
@@ -1099,9 +1275,6 @@
   }
 }
 
-bool _isNullable(HostDatatype hostDatatype, TypeDeclaration type) =>
-    hostDatatype.datatype.contains('*') && type.isNullable;
-
 /// Generates the name of the codec that will be generated.
 String _getCodecName(String? prefix, String className) =>
     '${_className(prefix, className)}Codec';
@@ -1160,9 +1333,12 @@
   required String lastArgType,
   required String lastArgName,
   required bool Function(TypeDeclaration) isEnum,
+  required Root root,
   String Function(int, NamedType)? argNameFunc,
 }) {
-  argNameFunc = argNameFunc ?? (int _, NamedType e) => e.name;
+  argNameFunc = argNameFunc ??
+      (int _, NamedType e) =>
+          e.type.isNullable && isEnum(e.type) ? '${e.name}Boxed' : e.name;
   final Iterable<String> argNames =
       followedByOne(indexMap(func.arguments, argNameFunc), lastArgName);
   final Iterable<String> selectorComponents =
@@ -1170,11 +1346,11 @@
   final Iterable<String> argTypes = followedByOne(
     func.arguments.map((NamedType arg) {
       if (isEnum(arg.type)) {
-        return _className(options.prefix, arg.type.baseName);
+        return '${arg.type.isNullable ? 'nullable ' : ''}${_enumName(arg.type.baseName, suffix: arg.type.isNullable ? ' *' : '', prefix: options.prefix, box: arg.type.isNullable)}';
       } else {
         final String nullable = arg.type.isNullable ? 'nullable ' : '';
         final _ObjcPtr argType = _objcTypeForDartType(options.prefix, arg.type);
-        return '$nullable${argType.ptr.trim()}';
+        return '$nullable${argType.withPtr.trim()}';
       }
     }),
     lastArgType,
@@ -1212,6 +1388,9 @@
   if (customClassNames.contains(field.type.baseName)) {
     return '(self.${field.name} ? [self.${field.name} toList] : [NSNull null])';
   } else if (customEnumNames.contains(field.type.baseName)) {
+    if (field.type.isNullable) {
+      return '(self.${field.name} == nil ? [NSNull null] : [NSNumber numberWithInteger:self.${field.name}.value])';
+    }
     return '@(self.${field.name})';
   } else {
     return '(self.${field.name} ?: [NSNull null])';
diff --git a/packages/pigeon/lib/pigeon_lib.dart b/packages/pigeon/lib/pigeon_lib.dart
index 10a6af5..81bbb90 100644
--- a/packages/pigeon/lib/pigeon_lib.dart
+++ b/packages/pigeon/lib/pigeon_lib.dart
@@ -787,32 +787,6 @@
   }
   for (final Api api in root.apis) {
     for (final Method method in api.methods) {
-      if (api.location == ApiLocation.flutter &&
-          method.arguments.isNotEmpty &&
-          method.arguments.any((NamedType element) =>
-              customEnums.contains(element.type.baseName))) {
-        result.add(Error(
-          message:
-              'Enums aren\'t yet supported for primitive arguments in FlutterApis: "${method.arguments[0]}" in API: "${api.name}" method: "${method.name}" (https://github.com/flutter/flutter/issues/87307)',
-          lineNumber: _calculateLineNumberNullable(source, method.offset),
-        ));
-      }
-      if (customEnums.contains(method.returnType.baseName)) {
-        result.add(Error(
-          message:
-              'Enums aren\'t yet supported for primitive return types: "${method.returnType}" in API: "${api.name}" method: "${method.name}" (https://github.com/flutter/flutter/issues/87307)',
-        ));
-      }
-      if (method.arguments.any((NamedType arg) =>
-          (arg.type.baseName == 'List' || arg.type.baseName == 'Map') &&
-          arg.type.typeArguments.any(
-              (TypeDeclaration genericType) => isEnum(root, genericType)))) {
-        result.add(Error(
-          message:
-              'Enums aren\'t yet supported for collection types: "${method.arguments[0]}" in API: "${api.name}" method: "${method.name}" (https://github.com/flutter/flutter/issues/87307)',
-          lineNumber: _calculateLineNumberNullable(source, method.offset),
-        ));
-      }
       for (final NamedType unnamedType in method.arguments
           .where((NamedType element) => element.type.baseName.isEmpty)) {
         result.add(Error(
@@ -1594,14 +1568,16 @@
 
     if (options.objcHeaderOut != null) {
       options = options.merge(PigeonOptions(
-          objcOptions: options.objcOptions!.merge(ObjcOptions(
-              headerIncludePath: path.basename(options.objcHeaderOut!)))));
+          objcOptions: (options.objcOptions ?? const ObjcOptions()).merge(
+              ObjcOptions(
+                  headerIncludePath: path.basename(options.objcHeaderOut!)))));
     }
 
     if (options.cppHeaderOut != null) {
       options = options.merge(PigeonOptions(
-          cppOptions: options.cppOptions!.merge(CppOptions(
-              headerIncludePath: path.basename(options.cppHeaderOut!)))));
+          cppOptions: (options.cppOptions ?? const CppOptions()).merge(
+              CppOptions(
+                  headerIncludePath: path.basename(options.cppHeaderOut!)))));
     }
 
     for (final GeneratorAdapter adapter in safeGeneratorAdapters) {
diff --git a/packages/pigeon/lib/swift_generator.dart b/packages/pigeon/lib/swift_generator.dart
index e996a77..d382fff 100644
--- a/packages/pigeon/lib/swift_generator.dart
+++ b/packages/pigeon/lib/swift_generator.dart
@@ -275,6 +275,18 @@
     required String dartPackageName,
   }) {
     assert(api.location == ApiLocation.flutter);
+
+    /// Returns an argument name that can be used in a context where it is possible to collide.
+    String getEnumSafeArgumentExpression(
+        Root root, int count, NamedType argument) {
+      String enumTag = '';
+      if (isEnum(root, argument.type)) {
+        enumTag = argument.type.isNullable ? '?.rawValue' : '.rawValue';
+      }
+
+      return '${_getArgumentName(count, argument)}Arg$enumTag';
+    }
+
     final bool isCustomCodec = getCodecClasses(api, root).isNotEmpty;
     if (isCustomCodec) {
       _writeCodec(indent, api, root);
@@ -327,7 +339,12 @@
           });
           final Iterable<String> argNames =
               indexMap(func.arguments, _getSafeArgumentName);
-          sendArgument = '[${argNames.join(', ')}] as [Any?]';
+          final Iterable<String> enumSafeArgNames = func.arguments
+              .asMap()
+              .entries
+              .map((MapEntry<int, NamedType> e) =>
+                  getEnumSafeArgumentExpression(root, e.key, e.value));
+          sendArgument = '[${enumSafeArgNames.join(', ')}] as [Any?]';
           final String argsSignature = map3(
               argTypes,
               argLabels,
@@ -503,9 +520,14 @@
                 indent.addScoped('{ result in', '}', () {
                   indent.write('switch result ');
                   indent.addScoped('{', '}', () {
+                    final String nullsafe =
+                        method.returnType.isNullable ? '?' : '';
+                    final String enumTag = isEnum(root, method.returnType)
+                        ? '$nullsafe.rawValue'
+                        : '';
                     indent.writeln('case .success$successVariableInit:');
                     indent.nest(1, () {
-                      indent.writeln('reply(wrapResult($resultName))');
+                      indent.writeln('reply(wrapResult($resultName$enumTag))');
                     });
                     indent.writeln('case .failure(let error):');
                     indent.nest(1, () {
@@ -520,8 +542,16 @@
                     indent.writeln(call);
                     indent.writeln('reply(wrapResult(nil))');
                   } else {
+                    String enumTag = '';
+                    if (isEnum(root, method.returnType)) {
+                      enumTag = '.rawValue';
+                    }
+                    enumTag = method.returnType.isNullable &&
+                            isEnum(root, method.returnType)
+                        ? '?$enumTag'
+                        : enumTag;
                     indent.writeln('let result = $call');
-                    indent.writeln('reply(wrapResult(result))');
+                    indent.writeln('reply(wrapResult(result$enumTag))');
                   }
                 }, addTrailingNewline: false);
                 indent.addScoped(' catch {', '}', () {
@@ -640,16 +670,19 @@
   }) {
     String castForceUnwrap(String value, TypeDeclaration type, Root root) {
       if (isEnum(root, type)) {
-        assert(!type.isNullable,
-            'nullable enums require special code that this helper does not supply');
-        return '${_swiftTypeForDartType(type)}(rawValue: $value as! Int)!';
+        String output =
+            '${_swiftTypeForDartType(type)}(rawValue: $value as! Int)!';
+        if (type.isNullable) {
+          output = 'isNullish($value) ? nil : $output';
+        }
+        return output;
       } else if (type.baseName == 'Object') {
         return value + (type.isNullable ? '' : '!');
       } else if (type.baseName == 'int') {
         if (type.isNullable) {
           // Nullable ints need to check for NSNull, and Int32 before casting can be done safely.
           // This nested ternary is a necessary evil to avoid less efficient conversions.
-          return '$value is NSNull ? nil : ($value is Int64? ? $value as! Int64? : Int64($value as! Int32))';
+          return 'isNullish($value) ? nil : ($value is Int64? ? $value as! Int64? : Int64($value as! Int32))';
         } else {
           return '$value is Int64 ? $value as! Int64 : Int64($value as! Int32)';
         }
@@ -699,6 +732,14 @@
     }
   }
 
+  void _writeIsNullish(Indent indent) {
+    indent.newln();
+    indent.write('private func isNullish(_ value: Any?) -> Bool ');
+    indent.addScoped('{', '}', () {
+      indent.writeln('return value is NSNull || value == nil');
+    });
+  }
+
   void _writeWrapResult(Indent indent) {
     indent.newln();
     indent.write('private func wrapResult(_ result: Any?) -> [Any?] ');
@@ -745,6 +786,7 @@
     Indent indent, {
     required String dartPackageName,
   }) {
+    _writeIsNullish(indent);
     _writeWrapResult(indent);
     _writeWrapError(indent);
     _writeNilOrValue(indent);
diff --git a/packages/pigeon/pigeons/core_tests.dart b/packages/pigeon/pigeons/core_tests.dart
index f590f87..ffe94ac 100644
--- a/packages/pigeon/pigeons/core_tests.dart
+++ b/packages/pigeon/pigeons/core_tests.dart
@@ -166,6 +166,11 @@
   @SwiftFunction('echo(_:)')
   AllClassesWrapper echoClassWrapper(AllClassesWrapper wrapper);
 
+  /// Returns the passed enum to test serialization and deserialization.
+  @ObjCSelector('echoEnum:')
+  @SwiftFunction('echo(_:)')
+  AnEnum echoEnum(AnEnum anEnum);
+
   // ========== Synchronous nullable method tests ==========
 
   /// Returns the passed object, to test serialization and deserialization.
@@ -231,6 +236,10 @@
   @SwiftFunction('echoNullable(_:)')
   Map<String?, Object?>? echoNullableMap(Map<String?, Object?>? aNullableMap);
 
+  @ObjCSelector('echoNullableEnum:')
+  @SwiftFunction('echoNullable(_:)')
+  AnEnum? echoNullableEnum(AnEnum? anEnum);
+
   // ========== Asynchronous method tests ==========
 
   /// A no-op function taking no arguments and returning no value, to sanity
@@ -274,18 +283,24 @@
   @SwiftFunction('echoAsync(_:)')
   Object echoAsyncObject(Object anObject);
 
-  /// Returns the passed list, to test serialization and deserialization asynchronously.
+  /// Returns the passed list, to test asynchronous serialization and deserialization.
   @async
   @ObjCSelector('echoAsyncList:')
   @SwiftFunction('echoAsync(_:)')
   List<Object?> echoAsyncList(List<Object?> aList);
 
-  /// Returns the passed map, to test serialization and deserialization asynchronously.
+  /// Returns the passed map, to test asynchronous serialization and deserialization.
   @async
   @ObjCSelector('echoAsyncMap:')
   @SwiftFunction('echoAsync(_:)')
   Map<String?, Object?> echoAsyncMap(Map<String?, Object?> aMap);
 
+  /// Returns the passed enum, to test asynchronous serialization and deserialization.
+  @async
+  @ObjCSelector('echoAsyncEnum:')
+  @SwiftFunction('echoAsync(_:)')
+  AnEnum echoAsyncEnum(AnEnum anEnum);
+
   /// Responds with an error from an async function returning a value.
   @async
   Object? throwAsyncError();
@@ -347,18 +362,24 @@
   @SwiftFunction('echoAsyncNullable(_:)')
   Object? echoAsyncNullableObject(Object? anObject);
 
-  /// Returns the passed list, to test serialization and deserialization asynchronously.
+  /// Returns the passed list, to test asynchronous serialization and deserialization.
   @async
   @ObjCSelector('echoAsyncNullableList:')
   @SwiftFunction('echoAsyncNullable(_:)')
   List<Object?>? echoAsyncNullableList(List<Object?>? aList);
 
-  /// Returns the passed map, to test serialization and deserialization asynchronously.
+  /// Returns the passed map, to test asynchronous serialization and deserialization.
   @async
   @ObjCSelector('echoAsyncNullableMap:')
   @SwiftFunction('echoAsyncNullable(_:)')
   Map<String?, Object?>? echoAsyncNullableMap(Map<String?, Object?>? aMap);
 
+  /// Returns the passed enum, to test asynchronous serialization and deserialization.
+  @async
+  @ObjCSelector('echoAsyncNullableEnum:')
+  @SwiftFunction('echoAsyncNullable(_:)')
+  AnEnum? echoAsyncNullableEnum(AnEnum? anEnum);
+
   // ========== Flutter API test wrappers ==========
 
   @async
@@ -375,10 +396,11 @@
   @SwiftFunction('callFlutterEcho(_:)')
   AllTypes callFlutterEchoAllTypes(AllTypes everything);
 
-  // TODO(stuartmorgan): Add callFlutterEchoAllNullableTypes and the associated
-  // test once either https://github.com/flutter/flutter/issues/116117 is fixed,
-  // or the problematic type is moved out of AllNullableTypes and into its own
-  // test, since the type mismatch breaks the second `encode` round.
+  @async
+  @ObjCSelector('callFlutterEchoAllNullableTypes:')
+  @SwiftFunction('callFlutterEcho(_:)')
+  AllNullableTypes? callFlutterEchoAllNullableTypes(
+      AllNullableTypes? everything);
 
   @async
   @ObjCSelector('callFlutterSendMultipleNullableTypesABool:anInt:aString:')
@@ -422,6 +444,11 @@
   Map<String?, Object?> callFlutterEchoMap(Map<String?, Object?> aMap);
 
   @async
+  @ObjCSelector('callFlutterEchoEnum:')
+  @SwiftFunction('callFlutterEcho(_:)')
+  AnEnum callFlutterEchoEnum(AnEnum anEnum);
+
+  @async
   @ObjCSelector('callFlutterEchoNullableBool:')
   @SwiftFunction('callFlutterEchoNullable(_:)')
   bool? callFlutterEchoNullableBool(bool? aBool);
@@ -456,6 +483,11 @@
   @SwiftFunction('callFlutterEchoNullable(_:)')
   Map<String?, Object?>? callFlutterEchoNullableMap(
       Map<String?, Object?>? aMap);
+
+  @async
+  @ObjCSelector('callFlutterEchoNullableEnum:')
+  @SwiftFunction('callFlutterNullableEcho(_:)')
+  AnEnum? callFlutterEchoNullableEnum(AnEnum? anEnum);
 }
 
 /// The core interface that the Dart platform_test code implements for host
@@ -480,7 +512,7 @@
   /// Returns the passed object, to test serialization and deserialization.
   @ObjCSelector('echoAllNullableTypes:')
   @SwiftFunction('echoNullable(_:)')
-  AllNullableTypes echoAllNullableTypes(AllNullableTypes everything);
+  AllNullableTypes? echoAllNullableTypes(AllNullableTypes? everything);
 
   /// Returns passed in arguments of multiple types.
   ///
@@ -527,6 +559,11 @@
   @SwiftFunction('echo(_:)')
   Map<String?, Object?> echoMap(Map<String?, Object?> aMap);
 
+  /// Returns the passed enum to test serialization and deserialization.
+  @ObjCSelector('echoEnum:')
+  @SwiftFunction('echo(_:)')
+  AnEnum echoEnum(AnEnum anEnum);
+
   // ========== Nullable argument/return type tests ==========
 
   /// Returns the passed boolean, to test serialization and deserialization.
@@ -564,6 +601,11 @@
   @SwiftFunction('echoNullable(_:)')
   Map<String?, Object?>? echoNullableMap(Map<String?, Object?>? aMap);
 
+  /// Returns the passed enum to test serialization and deserialization.
+  @ObjCSelector('echoNullableEnum:')
+  @SwiftFunction('echoNullable(_:)')
+  AnEnum? echoNullableEnum(AnEnum? anEnum);
+
   // ========== Async tests ==========
   // These are minimal since async FlutterApi only changes Dart generation.
   // Currently they aren't integration tested, but having them here ensures
diff --git a/packages/pigeon/pigeons/enum.dart b/packages/pigeon/pigeons/enum.dart
index d15bb56..7a39e0b 100644
--- a/packages/pigeon/pigeons/enum.dart
+++ b/packages/pigeon/pigeons/enum.dart
@@ -4,6 +4,10 @@
 
 import 'package:pigeon/pigeon.dart';
 
+@ConfigurePigeon(PigeonOptions(
+  objcOptions: ObjcOptions(prefix: 'PGN'),
+))
+
 /// This comment is to test enum documentation comments.
 enum EnumState {
   /// This comment is to test enum member (Pending) documentation comments.
diff --git a/packages/pigeon/platform_tests/alternate_language_test_plugin/android/src/main/java/com/example/alternate_language_test_plugin/AlternateLanguageTestPlugin.java b/packages/pigeon/platform_tests/alternate_language_test_plugin/android/src/main/java/com/example/alternate_language_test_plugin/AlternateLanguageTestPlugin.java
index c9c4406..08d1a93 100644
--- a/packages/pigeon/platform_tests/alternate_language_test_plugin/android/src/main/java/com/example/alternate_language_test_plugin/AlternateLanguageTestPlugin.java
+++ b/packages/pigeon/platform_tests/alternate_language_test_plugin/android/src/main/java/com/example/alternate_language_test_plugin/AlternateLanguageTestPlugin.java
@@ -9,6 +9,7 @@
 import com.example.alternate_language_test_plugin.CoreTests.AllClassesWrapper;
 import com.example.alternate_language_test_plugin.CoreTests.AllNullableTypes;
 import com.example.alternate_language_test_plugin.CoreTests.AllTypes;
+import com.example.alternate_language_test_plugin.CoreTests.AnEnum;
 import com.example.alternate_language_test_plugin.CoreTests.FlutterIntegrationCoreApi;
 import com.example.alternate_language_test_plugin.CoreTests.HostIntegrationCoreApi;
 import com.example.alternate_language_test_plugin.CoreTests.Result;
@@ -99,12 +100,17 @@
     return aMap;
   }
 
-  @NonNull
-  public AllClassesWrapper echoClassWrapper(@NonNull AllClassesWrapper wrapper) {
+  @Override
+  public @NonNull AllClassesWrapper echoClassWrapper(@NonNull AllClassesWrapper wrapper) {
     return wrapper;
   }
 
   @Override
+  public @NonNull AnEnum echoEnum(@NonNull AnEnum anEnum) {
+    return anEnum;
+  }
+
+  @Override
   public @Nullable String extractNestedNullableString(@NonNull AllClassesWrapper wrapper) {
     return wrapper.getAllNullableTypes().getANullableString();
   }
@@ -171,6 +177,11 @@
   }
 
   @Override
+  public @Nullable AnEnum echoNullableEnum(@Nullable AnEnum anEnum) {
+    return anEnum;
+  }
+
+  @Override
   public void noopAsync(@NonNull Result<Void> result) {
     result.success(null);
   }
@@ -243,6 +254,11 @@
   }
 
   @Override
+  public void echoAsyncEnum(@NonNull AnEnum anEnum, @NonNull Result<AnEnum> result) {
+    result.success(anEnum);
+  }
+
+  @Override
   public void echoAsyncNullableInt(@Nullable Long anInt, @NonNull Result<Long> result) {
     result.success(anInt);
   }
@@ -286,6 +302,11 @@
   }
 
   @Override
+  public void echoAsyncNullableEnum(@Nullable AnEnum anEnum, @NonNull Result<AnEnum> result) {
+    result.success(anEnum);
+  }
+
+  @Override
   public void callFlutterNoop(@NonNull Result<Void> result) {
     flutterApi.noop(
         new FlutterIntegrationCoreApi.Reply<Void>() {
@@ -332,6 +353,18 @@
   }
 
   @Override
+  public void callFlutterEchoAllNullableTypes(
+      @Nullable AllNullableTypes everything, @NonNull Result<AllNullableTypes> result) {
+    flutterApi.echoAllNullableTypes(
+        everything,
+        new FlutterIntegrationCoreApi.Reply<AllNullableTypes>() {
+          public void reply(AllNullableTypes value) {
+            result.success(value);
+          }
+        });
+  }
+
+  @Override
   public void callFlutterSendMultipleNullableTypes(
       @Nullable Boolean aNullableBool,
       @Nullable Long aNullableInt,
@@ -428,6 +461,17 @@
   }
 
   @Override
+  public void callFlutterEchoEnum(@NonNull AnEnum anEnum, @NonNull Result<AnEnum> result) {
+    flutterApi.echoEnum(
+        anEnum,
+        new FlutterIntegrationCoreApi.Reply<AnEnum>() {
+          public void reply(AnEnum value) {
+            result.success(value);
+          }
+        });
+  }
+
+  @Override
   public void callFlutterEchoNullableBool(
       @Nullable Boolean aBool, @NonNull Result<Boolean> result) {
     flutterApi.echoNullableBool(
@@ -509,4 +553,15 @@
           }
         });
   }
+
+  @Override
+  public void callFlutterEchoNullableEnum(@Nullable AnEnum anEnum, @NonNull Result<AnEnum> result) {
+    flutterApi.echoNullableEnum(
+        anEnum,
+        new FlutterIntegrationCoreApi.Reply<AnEnum>() {
+          public void reply(AnEnum value) {
+            result.success(value);
+          }
+        });
+  }
 }
diff --git a/packages/pigeon/platform_tests/alternate_language_test_plugin/android/src/main/java/com/example/alternate_language_test_plugin/CoreTests.java b/packages/pigeon/platform_tests/alternate_language_test_plugin/android/src/main/java/com/example/alternate_language_test_plugin/CoreTests.java
index 6f2215f..fd32aad 100644
--- a/packages/pigeon/platform_tests/alternate_language_test_plugin/android/src/main/java/com/example/alternate_language_test_plugin/CoreTests.java
+++ b/packages/pigeon/platform_tests/alternate_language_test_plugin/android/src/main/java/com/example/alternate_language_test_plugin/CoreTests.java
@@ -407,7 +407,7 @@
       Object aMap = list.get(9);
       pigeonResult.setAMap((Map<Object, Object>) aMap);
       Object anEnum = list.get(10);
-      pigeonResult.setAnEnum(anEnum == null ? null : AnEnum.values()[(int) anEnum]);
+      pigeonResult.setAnEnum(AnEnum.values()[(int) anEnum]);
       Object aString = list.get(11);
       pigeonResult.setAString((String) aString);
       Object anObject = list.get(12);
@@ -1014,6 +1014,9 @@
     /** Returns the passed map to test nested class serialization and deserialization. */
     @NonNull
     AllClassesWrapper echoClassWrapper(@NonNull AllClassesWrapper wrapper);
+    /** Returns the passed enum to test serialization and deserialization. */
+    @NonNull
+    AnEnum echoEnum(@NonNull AnEnum anEnum);
     /** Returns the passed object, to test serialization and deserialization. */
     @Nullable
     AllNullableTypes echoAllNullableTypes(@Nullable AllNullableTypes everything);
@@ -1057,6 +1060,9 @@
     /** Returns the passed map, to test serialization and deserialization. */
     @Nullable
     Map<String, Object> echoNullableMap(@Nullable Map<String, Object> aNullableMap);
+
+    @Nullable
+    AnEnum echoNullableEnum(@Nullable AnEnum anEnum);
     /**
      * A no-op function taking no arguments and returning no value, to sanity test basic
      * asynchronous calling.
@@ -1074,11 +1080,13 @@
     void echoAsyncUint8List(@NonNull byte[] aUint8List, @NonNull Result<byte[]> result);
     /** Returns the passed in generic Object asynchronously. */
     void echoAsyncObject(@NonNull Object anObject, @NonNull Result<Object> result);
-    /** Returns the passed list, to test serialization and deserialization asynchronously. */
+    /** Returns the passed list, to test asynchronous serialization and deserialization. */
     void echoAsyncList(@NonNull List<Object> aList, @NonNull Result<List<Object>> result);
-    /** Returns the passed map, to test serialization and deserialization asynchronously. */
+    /** Returns the passed map, to test asynchronous serialization and deserialization. */
     void echoAsyncMap(
         @NonNull Map<String, Object> aMap, @NonNull Result<Map<String, Object>> result);
+    /** Returns the passed enum, to test asynchronous serialization and deserialization. */
+    void echoAsyncEnum(@NonNull AnEnum anEnum, @NonNull Result<AnEnum> result);
     /** Responds with an error from an async function returning a value. */
     void throwAsyncError(@NonNull Result<Object> result);
     /** Responds with an error from an async void function. */
@@ -1102,11 +1110,13 @@
     void echoAsyncNullableUint8List(@Nullable byte[] aUint8List, @NonNull Result<byte[]> result);
     /** Returns the passed in generic Object asynchronously. */
     void echoAsyncNullableObject(@Nullable Object anObject, @NonNull Result<Object> result);
-    /** Returns the passed list, to test serialization and deserialization asynchronously. */
+    /** Returns the passed list, to test asynchronous serialization and deserialization. */
     void echoAsyncNullableList(@Nullable List<Object> aList, @NonNull Result<List<Object>> result);
-    /** Returns the passed map, to test serialization and deserialization asynchronously. */
+    /** Returns the passed map, to test asynchronous serialization and deserialization. */
     void echoAsyncNullableMap(
         @Nullable Map<String, Object> aMap, @NonNull Result<Map<String, Object>> result);
+    /** Returns the passed enum, to test asynchronous serialization and deserialization. */
+    void echoAsyncNullableEnum(@Nullable AnEnum anEnum, @NonNull Result<AnEnum> result);
 
     void callFlutterNoop(@NonNull Result<Void> result);
 
@@ -1116,6 +1126,9 @@
 
     void callFlutterEchoAllTypes(@NonNull AllTypes everything, @NonNull Result<AllTypes> result);
 
+    void callFlutterEchoAllNullableTypes(
+        @Nullable AllNullableTypes everything, @NonNull Result<AllNullableTypes> result);
+
     void callFlutterSendMultipleNullableTypes(
         @Nullable Boolean aNullableBool,
         @Nullable Long aNullableInt,
@@ -1137,6 +1150,8 @@
     void callFlutterEchoMap(
         @NonNull Map<String, Object> aMap, @NonNull Result<Map<String, Object>> result);
 
+    void callFlutterEchoEnum(@NonNull AnEnum anEnum, @NonNull Result<AnEnum> result);
+
     void callFlutterEchoNullableBool(@Nullable Boolean aBool, @NonNull Result<Boolean> result);
 
     void callFlutterEchoNullableInt(@Nullable Long anInt, @NonNull Result<Long> result);
@@ -1153,6 +1168,8 @@
     void callFlutterEchoNullableMap(
         @Nullable Map<String, Object> aMap, @NonNull Result<Map<String, Object>> result);
 
+    void callFlutterEchoNullableEnum(@Nullable AnEnum anEnum, @NonNull Result<AnEnum> result);
+
     /** The codec used by HostIntegrationCoreApi. */
     static @NonNull MessageCodec<Object> getCodec() {
       return HostIntegrationCoreApiCodec.INSTANCE;
@@ -1509,6 +1526,31 @@
         BasicMessageChannel<Object> channel =
             new BasicMessageChannel<>(
                 binaryMessenger,
+                "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoEnum",
+                getCodec());
+        if (api != null) {
+          channel.setMessageHandler(
+              (message, reply) -> {
+                ArrayList<Object> wrapped = new ArrayList<Object>();
+                ArrayList<Object> args = (ArrayList<Object>) message;
+                AnEnum anEnumArg = AnEnum.values()[(int) args.get(0)];
+                try {
+                  AnEnum output = api.echoEnum(anEnumArg);
+                  wrapped.add(0, output.index);
+                } 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.pigeon_integration_tests.HostIntegrationCoreApi.echoAllNullableTypes",
                 getCodec());
         if (api != null) {
@@ -1817,6 +1859,31 @@
         BasicMessageChannel<Object> channel =
             new BasicMessageChannel<>(
                 binaryMessenger,
+                "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoNullableEnum",
+                getCodec());
+        if (api != null) {
+          channel.setMessageHandler(
+              (message, reply) -> {
+                ArrayList<Object> wrapped = new ArrayList<Object>();
+                ArrayList<Object> args = (ArrayList<Object>) message;
+                AnEnum anEnumArg = args.get(0) == null ? null : AnEnum.values()[(int) args.get(0)];
+                try {
+                  AnEnum output = api.echoNullableEnum(anEnumArg);
+                  wrapped.add(0, output == null ? null : output.index);
+                } 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.pigeon_integration_tests.HostIntegrationCoreApi.noopAsync",
                 getCodec());
         if (api != null) {
@@ -2094,6 +2161,37 @@
         BasicMessageChannel<Object> channel =
             new BasicMessageChannel<>(
                 binaryMessenger,
+                "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoAsyncEnum",
+                getCodec());
+        if (api != null) {
+          channel.setMessageHandler(
+              (message, reply) -> {
+                ArrayList<Object> wrapped = new ArrayList<Object>();
+                ArrayList<Object> args = (ArrayList<Object>) message;
+                AnEnum anEnumArg = AnEnum.values()[(int) args.get(0)];
+                Result<AnEnum> resultCallback =
+                    new Result<AnEnum>() {
+                      public void success(AnEnum result) {
+                        wrapped.add(0, result.index);
+                        reply.reply(wrapped);
+                      }
+
+                      public void error(Throwable error) {
+                        ArrayList<Object> wrappedError = wrapError(error);
+                        reply.reply(wrappedError);
+                      }
+                    };
+
+                api.echoAsyncEnum(anEnumArg, resultCallback);
+              });
+        } else {
+          channel.setMessageHandler(null);
+        }
+      }
+      {
+        BasicMessageChannel<Object> channel =
+            new BasicMessageChannel<>(
+                binaryMessenger,
                 "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.throwAsyncError",
                 getCodec());
         if (api != null) {
@@ -2492,6 +2590,37 @@
         BasicMessageChannel<Object> channel =
             new BasicMessageChannel<>(
                 binaryMessenger,
+                "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoAsyncNullableEnum",
+                getCodec());
+        if (api != null) {
+          channel.setMessageHandler(
+              (message, reply) -> {
+                ArrayList<Object> wrapped = new ArrayList<Object>();
+                ArrayList<Object> args = (ArrayList<Object>) message;
+                AnEnum anEnumArg = args.get(0) == null ? null : AnEnum.values()[(int) args.get(0)];
+                Result<AnEnum> resultCallback =
+                    new Result<AnEnum>() {
+                      public void success(AnEnum result) {
+                        wrapped.add(0, result == null ? null : result.index);
+                        reply.reply(wrapped);
+                      }
+
+                      public void error(Throwable error) {
+                        ArrayList<Object> wrappedError = wrapError(error);
+                        reply.reply(wrappedError);
+                      }
+                    };
+
+                api.echoAsyncNullableEnum(anEnumArg, resultCallback);
+              });
+        } else {
+          channel.setMessageHandler(null);
+        }
+      }
+      {
+        BasicMessageChannel<Object> channel =
+            new BasicMessageChannel<>(
+                binaryMessenger,
                 "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.callFlutterNoop",
                 getCodec());
         if (api != null) {
@@ -2610,6 +2739,37 @@
         BasicMessageChannel<Object> channel =
             new BasicMessageChannel<>(
                 binaryMessenger,
+                "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.callFlutterEchoAllNullableTypes",
+                getCodec());
+        if (api != null) {
+          channel.setMessageHandler(
+              (message, reply) -> {
+                ArrayList<Object> wrapped = new ArrayList<Object>();
+                ArrayList<Object> args = (ArrayList<Object>) message;
+                AllNullableTypes everythingArg = (AllNullableTypes) args.get(0);
+                Result<AllNullableTypes> resultCallback =
+                    new Result<AllNullableTypes>() {
+                      public void success(AllNullableTypes result) {
+                        wrapped.add(0, result);
+                        reply.reply(wrapped);
+                      }
+
+                      public void error(Throwable error) {
+                        ArrayList<Object> wrappedError = wrapError(error);
+                        reply.reply(wrappedError);
+                      }
+                    };
+
+                api.callFlutterEchoAllNullableTypes(everythingArg, resultCallback);
+              });
+        } else {
+          channel.setMessageHandler(null);
+        }
+      }
+      {
+        BasicMessageChannel<Object> channel =
+            new BasicMessageChannel<>(
+                binaryMessenger,
                 "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.callFlutterSendMultipleNullableTypes",
                 getCodec());
         if (api != null) {
@@ -2865,6 +3025,37 @@
         BasicMessageChannel<Object> channel =
             new BasicMessageChannel<>(
                 binaryMessenger,
+                "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.callFlutterEchoEnum",
+                getCodec());
+        if (api != null) {
+          channel.setMessageHandler(
+              (message, reply) -> {
+                ArrayList<Object> wrapped = new ArrayList<Object>();
+                ArrayList<Object> args = (ArrayList<Object>) message;
+                AnEnum anEnumArg = AnEnum.values()[(int) args.get(0)];
+                Result<AnEnum> resultCallback =
+                    new Result<AnEnum>() {
+                      public void success(AnEnum result) {
+                        wrapped.add(0, result.index);
+                        reply.reply(wrapped);
+                      }
+
+                      public void error(Throwable error) {
+                        ArrayList<Object> wrappedError = wrapError(error);
+                        reply.reply(wrappedError);
+                      }
+                    };
+
+                api.callFlutterEchoEnum(anEnumArg, resultCallback);
+              });
+        } else {
+          channel.setMessageHandler(null);
+        }
+      }
+      {
+        BasicMessageChannel<Object> channel =
+            new BasicMessageChannel<>(
+                binaryMessenger,
                 "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.callFlutterEchoNullableBool",
                 getCodec());
         if (api != null) {
@@ -3079,6 +3270,37 @@
           channel.setMessageHandler(null);
         }
       }
+      {
+        BasicMessageChannel<Object> channel =
+            new BasicMessageChannel<>(
+                binaryMessenger,
+                "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.callFlutterEchoNullableEnum",
+                getCodec());
+        if (api != null) {
+          channel.setMessageHandler(
+              (message, reply) -> {
+                ArrayList<Object> wrapped = new ArrayList<Object>();
+                ArrayList<Object> args = (ArrayList<Object>) message;
+                AnEnum anEnumArg = args.get(0) == null ? null : AnEnum.values()[(int) args.get(0)];
+                Result<AnEnum> resultCallback =
+                    new Result<AnEnum>() {
+                      public void success(AnEnum result) {
+                        wrapped.add(0, result == null ? null : result.index);
+                        reply.reply(wrapped);
+                      }
+
+                      public void error(Throwable error) {
+                        ArrayList<Object> wrappedError = wrapError(error);
+                        reply.reply(wrappedError);
+                      }
+                    };
+
+                api.callFlutterEchoNullableEnum(anEnumArg, resultCallback);
+              });
+        } else {
+          channel.setMessageHandler(null);
+        }
+      }
     }
   }
 
@@ -3198,7 +3420,7 @@
     }
     /** Returns the passed object, to test serialization and deserialization. */
     public void echoAllNullableTypes(
-        @NonNull AllNullableTypes everythingArg, @NonNull Reply<AllNullableTypes> callback) {
+        @Nullable AllNullableTypes everythingArg, @NonNull Reply<AllNullableTypes> callback) {
       BasicMessageChannel<Object> channel =
           new BasicMessageChannel<>(
               binaryMessenger,
@@ -3342,6 +3564,21 @@
             callback.reply(output);
           });
     }
+    /** Returns the passed enum to test serialization and deserialization. */
+    public void echoEnum(@NonNull AnEnum anEnumArg, @NonNull Reply<AnEnum> callback) {
+      BasicMessageChannel<Object> channel =
+          new BasicMessageChannel<>(
+              binaryMessenger,
+              "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoEnum",
+              getCodec());
+      channel.send(
+          new ArrayList<Object>(Collections.singletonList(anEnumArg.index)),
+          channelReply -> {
+            @SuppressWarnings("ConstantConditions")
+            AnEnum output = AnEnum.values()[(int) channelReply];
+            callback.reply(output);
+          });
+    }
     /** Returns the passed boolean, to test serialization and deserialization. */
     public void echoNullableBool(@Nullable Boolean aBoolArg, @NonNull Reply<Boolean> callback) {
       BasicMessageChannel<Object> channel =
@@ -3449,6 +3686,22 @@
             callback.reply(output);
           });
     }
+    /** Returns the passed enum to test serialization and deserialization. */
+    public void echoNullableEnum(@Nullable AnEnum anEnumArg, @NonNull Reply<AnEnum> callback) {
+      BasicMessageChannel<Object> channel =
+          new BasicMessageChannel<>(
+              binaryMessenger,
+              "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoNullableEnum",
+              getCodec());
+      channel.send(
+          new ArrayList<Object>(
+              Collections.singletonList(anEnumArg == null ? null : anEnumArg.index)),
+          channelReply -> {
+            @SuppressWarnings("ConstantConditions")
+            AnEnum output = channelReply == null ? null : AnEnum.values()[(int) channelReply];
+            callback.reply(output);
+          });
+    }
     /**
      * A no-op function taking no arguments and returning no value, to sanity test basic
      * asynchronous calling.
diff --git a/packages/pigeon/platform_tests/alternate_language_test_plugin/example/ios/RunnerTests/EnumTest.m b/packages/pigeon/platform_tests/alternate_language_test_plugin/example/ios/RunnerTests/EnumTest.m
index 99ff3eb..7a3350e 100644
--- a/packages/pigeon/platform_tests/alternate_language_test_plugin/example/ios/RunnerTests/EnumTest.m
+++ b/packages/pigeon/platform_tests/alternate_language_test_plugin/example/ios/RunnerTests/EnumTest.m
@@ -17,15 +17,16 @@
 @implementation EnumTest
 
 - (void)testEcho {
-  DataWithEnum *data = [[DataWithEnum alloc] init];
-  data.state = EnumStateError;
+  PGNDataWithEnum *data = [[PGNDataWithEnum alloc] init];
+  PGNEnumStateBox *stateBox = [[PGNEnumStateBox alloc] initWithValue:PGNEnumStateError];
+  data.state = stateBox;
   EchoBinaryMessenger *binaryMessenger =
-      [[EchoBinaryMessenger alloc] initWithCodec:EnumApi2HostGetCodec()];
-  EnumApi2Flutter *api = [[EnumApi2Flutter alloc] initWithBinaryMessenger:binaryMessenger];
+      [[EchoBinaryMessenger alloc] initWithCodec:PGNEnumApi2HostGetCodec()];
+  PGNEnumApi2Flutter *api = [[PGNEnumApi2Flutter alloc] initWithBinaryMessenger:binaryMessenger];
   XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
   [api echoData:data
-      completion:^(DataWithEnum *_Nonnull result, FlutterError *_Nullable error) {
-        XCTAssertEqual(data.state, result.state);
+      completion:^(PGNDataWithEnum *_Nonnull result, FlutterError *_Nullable error) {
+        XCTAssertEqual(data.state.value, result.state.value);
         [expectation fulfill];
       }];
   [self waitForExpectations:@[ expectation ] timeout:1.0];
diff --git a/packages/pigeon/platform_tests/alternate_language_test_plugin/example/ios/RunnerTests/NullFieldsTest.m b/packages/pigeon/platform_tests/alternate_language_test_plugin/example/ios/RunnerTests/NullFieldsTest.m
index 605f15f..04f9c7d 100644
--- a/packages/pigeon/platform_tests/alternate_language_test_plugin/example/ios/RunnerTests/NullFieldsTest.m
+++ b/packages/pigeon/platform_tests/alternate_language_test_plugin/example/ios/RunnerTests/NullFieldsTest.m
@@ -31,19 +31,20 @@
 - (void)testMakeWithValues {
   NullFieldsSearchRequest *request = [NullFieldsSearchRequest makeWithQuery:@"hello" identifier:@1];
 
-  NullFieldsSearchReply *reply =
-      [NullFieldsSearchReply makeWithResult:@"result"
-                                      error:@"error"
-                                    indices:@[ @1, @2, @3 ]
-                                    request:request
-                                       type:NullFieldsSearchReplyTypeSuccess];
+  NullFieldsSearchReplyTypeBox *typeWrapper =
+      [[NullFieldsSearchReplyTypeBox alloc] initWithValue:NullFieldsSearchReplyTypeSuccess];
+  NullFieldsSearchReply *reply = [NullFieldsSearchReply makeWithResult:@"result"
+                                                                 error:@"error"
+                                                               indices:@[ @1, @2, @3 ]
+                                                               request:request
+                                                                  type:typeWrapper];
 
   NSArray *indices = @[ @1, @2, @3 ];
   XCTAssertEqualObjects(@"result", reply.result);
   XCTAssertEqualObjects(@"error", reply.error);
   XCTAssertEqualObjects(indices, reply.indices);
   XCTAssertEqualObjects(@"hello", reply.request.query);
-  XCTAssertEqual(NullFieldsSearchReplyTypeSuccess, reply.type);
+  XCTAssertEqual(typeWrapper.value, reply.type.value);
 }
 
 - (void)testMakeRequestWithNulls {
@@ -52,17 +53,16 @@
 }
 
 - (void)testMakeReplyWithNulls {
-  NullFieldsSearchReply *reply =
-      [NullFieldsSearchReply makeWithResult:nil
-                                      error:nil
-                                    indices:nil
-                                    request:nil
-                                       type:NullFieldsSearchReplyTypeSuccess];
+  NullFieldsSearchReply *reply = [NullFieldsSearchReply makeWithResult:nil
+                                                                 error:nil
+                                                               indices:nil
+                                                               request:nil
+                                                                  type:nil];
   XCTAssertNil(reply.result);
   XCTAssertNil(reply.error);
   XCTAssertNil(reply.indices);
   XCTAssertNil(reply.request);
-  XCTAssertEqual(NullFieldsSearchReplyTypeSuccess, reply.type);
+  XCTAssertNil(reply.type);
 }
 
 - (void)testRequestFromListWithValues {
@@ -101,7 +101,7 @@
   XCTAssertEqualObjects(@"error", reply.error);
   XCTAssertEqualObjects(indices, reply.indices);
   XCTAssertEqualObjects(@"hello", reply.request.query);
-  XCTAssertEqual(NullFieldsSearchReplyTypeSuccess, reply.type);
+  XCTAssertEqual(NullFieldsSearchReplyTypeSuccess, reply.type.value);
 }
 
 - (void)testReplyFromListWithNulls {
@@ -117,7 +117,7 @@
   XCTAssertNil(reply.error);
   XCTAssertNil(reply.indices);
   XCTAssertNil(reply.request.query);
-  XCTAssertEqual(NullFieldsSearchReplyTypeSuccess, reply.type);
+  XCTAssertNil(reply.type);
 }
 
 - (void)testRequestToListWithValuess {
@@ -133,33 +133,39 @@
 }
 
 - (void)testReplyToListWithValuess {
+  NullFieldsSearchReplyTypeBox *typeWrapper =
+      [[NullFieldsSearchReplyTypeBox alloc] initWithValue:NullFieldsSearchReplyTypeSuccess];
   NullFieldsSearchReply *reply = [NullFieldsSearchReply
       makeWithResult:@"result"
                error:@"error"
              indices:@[ @1, @2, @3 ]
              request:[NullFieldsSearchRequest makeWithQuery:@"hello" identifier:@1]
-                type:NullFieldsSearchReplyTypeSuccess];
+                type:typeWrapper];
   NSArray *list = [reply toList];
   NSArray *indices = @[ @1, @2, @3 ];
   XCTAssertEqualObjects(@"result", list[0]);
   XCTAssertEqualObjects(@"error", list[1]);
   XCTAssertEqualObjects(indices, list[2]);
   XCTAssertEqualObjects(@"hello", list[3][0]);
-  XCTAssertEqualObjects(@0, list[4]);
+  NSNumber *typeNumber = list[4];
+  NullFieldsSearchReplyTypeBox *output =
+      [[NullFieldsSearchReplyTypeBox alloc] initWithValue:[typeNumber integerValue]];
+
+  XCTAssertEqual(typeWrapper.value, output.value);
 }
 
 - (void)testReplyToListWithNulls {
-  NullFieldsSearchReply *reply =
-      [NullFieldsSearchReply makeWithResult:nil
-                                      error:nil
-                                    indices:nil
-                                    request:nil
-                                       type:NullFieldsSearchReplyTypeSuccess];
+  NullFieldsSearchReply *reply = [NullFieldsSearchReply makeWithResult:nil
+                                                                 error:nil
+                                                               indices:nil
+                                                               request:nil
+                                                                  type:nil];
   NSArray *list = [reply toList];
   XCTAssertEqual([NSNull null], list[0]);
   XCTAssertEqual([NSNull null], list[1]);
   XCTAssertEqual([NSNull null], list[2]);
   XCTAssertEqual([NSNull null], list[3]);
+  XCTAssertEqual([NSNull null], list[4]);
 }
 
 @end
diff --git a/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/AlternateLanguageTestPlugin.m b/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/AlternateLanguageTestPlugin.m
index 1df7a34..3423d53 100644
--- a/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/AlternateLanguageTestPlugin.m
+++ b/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/AlternateLanguageTestPlugin.m
@@ -92,6 +92,10 @@
   return wrapper;
 }
 
+- (AnEnum)echoEnum:(AnEnum)anEnum error:(FlutterError *_Nullable *_Nonnull)error {
+  return anEnum;
+}
+
 - (nullable NSString *)extractNestedNullableStringFrom:(AllClassesWrapper *)wrapper
                                                  error:(FlutterError *_Nullable *_Nonnull)error {
   return wrapper.allNullableTypes.aNullableString;
@@ -159,6 +163,11 @@
   return aNullableMap;
 }
 
+- (AnEnumBox *_Nullable)echoNullableEnum:(nullable AnEnumBox *)AnEnumBoxed
+                                   error:(FlutterError *_Nullable *_Nonnull)error {
+  return AnEnumBoxed;
+}
+
 - (void)noopAsyncWithCompletion:(void (^)(FlutterError *_Nullable))completion {
   completion(nil);
 }
@@ -229,6 +238,11 @@
   completion(aMap, nil);
 }
 
+- (void)echoAsyncEnum:(AnEnum)anEnum
+           completion:(void (^)(AnEnum, FlutterError *_Nullable))completion {
+  completion(anEnum, nil);
+}
+
 - (void)echoAsyncNullableInt:(nullable NSNumber *)anInt
                   completion:(void (^)(NSNumber *_Nullable, FlutterError *_Nullable))completion {
   completion(anInt, nil);
@@ -272,6 +286,11 @@
   completion(aMap, nil);
 }
 
+- (void)echoAsyncNullableEnum:(nullable AnEnumBox *)AnEnumBoxed
+                   completion:(void (^)(AnEnumBox *_Nullable, FlutterError *_Nullable))completion {
+  completion(AnEnumBoxed, nil);
+}
+
 - (void)callFlutterNoopWithCompletion:(void (^)(FlutterError *_Nullable))completion {
   [self.flutterAPI noopWithCompletion:^(FlutterError *error) {
     completion(error);
@@ -370,6 +389,23 @@
                 }];
 }
 
+- (void)callFlutterEchoEnum:(AnEnum)anEnum
+                 completion:(void (^)(AnEnum, FlutterError *_Nullable))completion {
+  [self.flutterAPI echoEnum:anEnum
+                 completion:^(AnEnum value, FlutterError *error) {
+                   completion(value, error);
+                 }];
+}
+
+- (void)callFlutterEchoAllNullableTypes:(nullable AllNullableTypes *)everything
+                             completion:(void (^)(AllNullableTypes *_Nullable,
+                                                  FlutterError *_Nullable))completion {
+  [self.flutterAPI echoAllNullableTypes:everything
+                             completion:^(AllNullableTypes *value, FlutterError *error) {
+                               completion(value, error);
+                             }];
+}
+
 - (void)callFlutterEchoNullableBool:(nullable NSNumber *)aBool
                          completion:
                              (void (^)(NSNumber *_Nullable, FlutterError *_Nullable))completion {
@@ -433,4 +469,13 @@
                         }];
 }
 
+- (void)callFlutterEchoNullableEnum:(nullable AnEnumBox *)AnEnumBoxed
+                         completion:
+                             (void (^)(AnEnumBox *_Nullable, FlutterError *_Nullable))completion {
+  [self.flutterAPI echoNullableEnum:AnEnumBoxed
+                         completion:^(AnEnumBox *value, FlutterError *error) {
+                           completion(value, error);
+                         }];
+}
+
 @end
diff --git a/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/CoreTests.gen.h b/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/CoreTests.gen.h
index 2cf2d11..1b558e4 100644
--- a/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/CoreTests.gen.h
+++ b/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/CoreTests.gen.h
@@ -20,6 +20,12 @@
   AnEnumThree = 2,
 };
 
+/// Wrapper for AnEnum to allow for nullability.
+@interface AnEnumBox : NSObject
+@property(nonatomic, assign) AnEnum value;
+- (instancetype)initWithValue:(AnEnum)value;
+@end
+
 @class AllTypes;
 @class AllNullableTypes;
 @class AllClassesWrapper;
@@ -73,9 +79,9 @@
            nullableMapWithAnnotations:
                (nullable NSDictionary<NSString *, NSString *> *)nullableMapWithAnnotations
                 nullableMapWithObject:(nullable NSDictionary<NSString *, id> *)nullableMapWithObject
-                        aNullableEnum:(AnEnum)aNullableEnum
+                        aNullableEnum:(nullable AnEnumBox *)aNullableEnum
                       aNullableString:(nullable NSString *)aNullableString
-                      aNullableObject:(id)aNullableObject;
+                      aNullableObject:(nullable id)aNullableObject;
 @property(nonatomic, strong, nullable) NSNumber *aNullableBool;
 @property(nonatomic, strong, nullable) NSNumber *aNullableInt;
 @property(nonatomic, strong, nullable) NSNumber *aNullableInt64;
@@ -90,9 +96,9 @@
 @property(nonatomic, strong, nullable)
     NSDictionary<NSString *, NSString *> *nullableMapWithAnnotations;
 @property(nonatomic, strong, nullable) NSDictionary<NSString *, id> *nullableMapWithObject;
-@property(nonatomic, assign) AnEnum aNullableEnum;
+@property(nonatomic, strong, nullable) AnEnumBox *aNullableEnum;
 @property(nonatomic, copy, nullable) NSString *aNullableString;
-@property(nonatomic, strong) id aNullableObject;
+@property(nonatomic, strong, nullable) id aNullableObject;
 @end
 
 /// A class for testing nested class handling.
@@ -177,6 +183,10 @@
 /// @return `nil` only when `error != nil`.
 - (nullable AllClassesWrapper *)echoClassWrapper:(AllClassesWrapper *)wrapper
                                            error:(FlutterError *_Nullable *_Nonnull)error;
+/// Returns the passed enum to test serialization and deserialization.
+///
+/// @return `nil` only when `error != nil`.
+- (AnEnum)echoEnum:(AnEnum)anEnum error:(FlutterError *_Nullable *_Nonnull)error;
 /// Returns the passed object, to test serialization and deserialization.
 - (nullable AllNullableTypes *)echoAllNullableTypes:(nullable AllNullableTypes *)everything
                                               error:(FlutterError *_Nullable *_Nonnull)error;
@@ -225,6 +235,8 @@
 - (nullable NSDictionary<NSString *, id> *)echoNullableMap:
                                                (nullable NSDictionary<NSString *, id> *)aNullableMap
                                                      error:(FlutterError *_Nullable *_Nonnull)error;
+- (AnEnumBox *_Nullable)echoNullableEnum:(nullable AnEnumBox *)anEnumBoxed
+                                   error:(FlutterError *_Nullable *_Nonnull)error;
 /// A no-op function taking no arguments and returning no value, to sanity
 /// test basic asynchronous calling.
 - (void)noopAsyncWithCompletion:(void (^)(FlutterError *_Nullable))completion;
@@ -247,13 +259,16 @@
 /// Returns the passed in generic Object asynchronously.
 - (void)echoAsyncObject:(id)anObject
              completion:(void (^)(id _Nullable, FlutterError *_Nullable))completion;
-/// Returns the passed list, to test serialization and deserialization asynchronously.
+/// Returns the passed list, to test asynchronous serialization and deserialization.
 - (void)echoAsyncList:(NSArray<id> *)aList
            completion:(void (^)(NSArray<id> *_Nullable, FlutterError *_Nullable))completion;
-/// Returns the passed map, to test serialization and deserialization asynchronously.
+/// Returns the passed map, to test asynchronous serialization and deserialization.
 - (void)echoAsyncMap:(NSDictionary<NSString *, id> *)aMap
           completion:(void (^)(NSDictionary<NSString *, id> *_Nullable,
                                FlutterError *_Nullable))completion;
+/// Returns the passed enum, to test asynchronous serialization and deserialization.
+- (void)echoAsyncEnum:(AnEnum)anEnum
+           completion:(void (^)(AnEnum, FlutterError *_Nullable))completion;
 /// Responds with an error from an async function returning a value.
 - (void)throwAsyncErrorWithCompletion:(void (^)(id _Nullable, FlutterError *_Nullable))completion;
 /// Responds with an error from an async void function.
@@ -287,19 +302,25 @@
 /// Returns the passed in generic Object asynchronously.
 - (void)echoAsyncNullableObject:(nullable id)anObject
                      completion:(void (^)(id _Nullable, FlutterError *_Nullable))completion;
-/// Returns the passed list, to test serialization and deserialization asynchronously.
+/// Returns the passed list, to test asynchronous serialization and deserialization.
 - (void)echoAsyncNullableList:(nullable NSArray<id> *)aList
                    completion:(void (^)(NSArray<id> *_Nullable, FlutterError *_Nullable))completion;
-/// Returns the passed map, to test serialization and deserialization asynchronously.
+/// Returns the passed map, to test asynchronous serialization and deserialization.
 - (void)echoAsyncNullableMap:(nullable NSDictionary<NSString *, id> *)aMap
                   completion:(void (^)(NSDictionary<NSString *, id> *_Nullable,
                                        FlutterError *_Nullable))completion;
+/// Returns the passed enum, to test asynchronous serialization and deserialization.
+- (void)echoAsyncNullableEnum:(nullable AnEnumBox *)anEnumBoxed
+                   completion:(void (^)(AnEnumBox *_Nullable, FlutterError *_Nullable))completion;
 - (void)callFlutterNoopWithCompletion:(void (^)(FlutterError *_Nullable))completion;
 - (void)callFlutterThrowErrorWithCompletion:(void (^)(id _Nullable,
                                                       FlutterError *_Nullable))completion;
 - (void)callFlutterThrowErrorFromVoidWithCompletion:(void (^)(FlutterError *_Nullable))completion;
 - (void)callFlutterEchoAllTypes:(AllTypes *)everything
                      completion:(void (^)(AllTypes *_Nullable, FlutterError *_Nullable))completion;
+- (void)callFlutterEchoAllNullableTypes:(nullable AllNullableTypes *)everything
+                             completion:(void (^)(AllNullableTypes *_Nullable,
+                                                  FlutterError *_Nullable))completion;
 - (void)callFlutterSendMultipleNullableTypesABool:(nullable NSNumber *)aNullableBool
                                             anInt:(nullable NSNumber *)aNullableInt
                                           aString:(nullable NSString *)aNullableString
@@ -321,6 +342,8 @@
 - (void)callFlutterEchoMap:(NSDictionary<NSString *, id> *)aMap
                 completion:(void (^)(NSDictionary<NSString *, id> *_Nullable,
                                      FlutterError *_Nullable))completion;
+- (void)callFlutterEchoEnum:(AnEnum)anEnum
+                 completion:(void (^)(AnEnum, FlutterError *_Nullable))completion;
 - (void)callFlutterEchoNullableBool:(nullable NSNumber *)aBool
                          completion:
                              (void (^)(NSNumber *_Nullable, FlutterError *_Nullable))completion;
@@ -342,6 +365,9 @@
 - (void)callFlutterEchoNullableMap:(nullable NSDictionary<NSString *, id> *)aMap
                         completion:(void (^)(NSDictionary<NSString *, id> *_Nullable,
                                              FlutterError *_Nullable))completion;
+- (void)callFlutterEchoNullableEnum:(nullable AnEnumBox *)anEnumBoxed
+                         completion:
+                             (void (^)(AnEnumBox *_Nullable, FlutterError *_Nullable))completion;
 @end
 
 extern void HostIntegrationCoreApiSetup(id<FlutterBinaryMessenger> binaryMessenger,
@@ -365,7 +391,7 @@
 - (void)echoAllTypes:(AllTypes *)everything
           completion:(void (^)(AllTypes *_Nullable, FlutterError *_Nullable))completion;
 /// Returns the passed object, to test serialization and deserialization.
-- (void)echoAllNullableTypes:(AllNullableTypes *)everything
+- (void)echoAllNullableTypes:(nullable AllNullableTypes *)everything
                   completion:
                       (void (^)(AllNullableTypes *_Nullable, FlutterError *_Nullable))completion;
 /// Returns passed in arguments of multiple types.
@@ -399,6 +425,8 @@
 - (void)echoMap:(NSDictionary<NSString *, id> *)aMap
      completion:
          (void (^)(NSDictionary<NSString *, id> *_Nullable, FlutterError *_Nullable))completion;
+/// Returns the passed enum to test serialization and deserialization.
+- (void)echoEnum:(AnEnum)anEnum completion:(void (^)(AnEnum, FlutterError *_Nullable))completion;
 /// Returns the passed boolean, to test serialization and deserialization.
 - (void)echoNullableBool:(nullable NSNumber *)aBool
               completion:(void (^)(NSNumber *_Nullable, FlutterError *_Nullable))completion;
@@ -422,6 +450,9 @@
 - (void)echoNullableMap:(nullable NSDictionary<NSString *, id> *)aMap
              completion:(void (^)(NSDictionary<NSString *, id> *_Nullable,
                                   FlutterError *_Nullable))completion;
+/// Returns the passed enum to test serialization and deserialization.
+- (void)echoNullableEnum:(nullable AnEnumBox *)anEnumBoxed
+              completion:(void (^)(AnEnumBox *_Nullable, FlutterError *_Nullable))completion;
 /// A no-op function taking no arguments and returning no value, to sanity
 /// test basic asynchronous calling.
 - (void)noopAsyncWithCompletion:(void (^)(FlutterError *_Nullable))completion;
diff --git a/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/CoreTests.gen.m b/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/CoreTests.gen.m
index 524a9dd..f68ecd8 100644
--- a/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/CoreTests.gen.m
+++ b/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/CoreTests.gen.m
@@ -17,6 +17,16 @@
 #error File requires ARC to be enabled.
 #endif
 
+@implementation AnEnumBox
+- (instancetype)initWithValue:(AnEnum)value {
+  self = [super init];
+  if (self) {
+    _value = value;
+  }
+  return self;
+}
+@end
+
 static NSArray *wrapResult(id result, FlutterError *error) {
   if (error) {
     return @[
@@ -150,9 +160,9 @@
            nullableMapWithAnnotations:
                (nullable NSDictionary<NSString *, NSString *> *)nullableMapWithAnnotations
                 nullableMapWithObject:(nullable NSDictionary<NSString *, id> *)nullableMapWithObject
-                        aNullableEnum:(AnEnum)aNullableEnum
+                        aNullableEnum:(nullable AnEnumBox *)aNullableEnum
                       aNullableString:(nullable NSString *)aNullableString
-                      aNullableObject:(id)aNullableObject {
+                      aNullableObject:(nullable id)aNullableObject {
   AllNullableTypes *pigeonResult = [[AllNullableTypes alloc] init];
   pigeonResult.aNullableBool = aNullableBool;
   pigeonResult.aNullableInt = aNullableInt;
@@ -187,7 +197,12 @@
   pigeonResult.nullableNestedList = GetNullableObjectAtIndex(list, 10);
   pigeonResult.nullableMapWithAnnotations = GetNullableObjectAtIndex(list, 11);
   pigeonResult.nullableMapWithObject = GetNullableObjectAtIndex(list, 12);
-  pigeonResult.aNullableEnum = [GetNullableObjectAtIndex(list, 13) integerValue];
+  NSNumber *aNullableEnumAsNumber = GetNullableObjectAtIndex(list, 13);
+  AnEnumBox *aNullableEnum =
+      aNullableEnumAsNumber == nil
+          ? nil
+          : [[AnEnumBox alloc] initWithValue:[aNullableEnumAsNumber integerValue]];
+  pigeonResult.aNullableEnum = aNullableEnum;
   pigeonResult.aNullableString = GetNullableObjectAtIndex(list, 14);
   pigeonResult.aNullableObject = GetNullableObjectAtIndex(list, 15);
   return pigeonResult;
@@ -210,7 +225,8 @@
     (self.nullableNestedList ?: [NSNull null]),
     (self.nullableMapWithAnnotations ?: [NSNull null]),
     (self.nullableMapWithObject ?: [NSNull null]),
-    @(self.aNullableEnum),
+    (self.aNullableEnum == nil ? [NSNull null]
+                               : [NSNumber numberWithInteger:self.aNullableEnum.value]),
     (self.aNullableString ?: [NSNull null]),
     (self.aNullableObject ?: [NSNull null]),
   ];
@@ -636,6 +652,29 @@
       [channel setMessageHandler:nil];
     }
   }
+  /// Returns the passed enum to test serialization and deserialization.
+  {
+    FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc]
+           initWithName:
+               @"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoEnum"
+        binaryMessenger:binaryMessenger
+                  codec:HostIntegrationCoreApiGetCodec()];
+    if (api) {
+      NSCAssert([api respondsToSelector:@selector(echoEnum:error:)],
+                @"HostIntegrationCoreApi api (%@) doesn't respond to @selector(echoEnum:error:)",
+                api);
+      [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) {
+        NSArray *args = message;
+        AnEnum arg_anEnum = [GetNullableObjectAtIndex(args, 0) integerValue];
+        FlutterError *error;
+        AnEnum enumValue = [api echoEnum:arg_anEnum error:&error];
+        NSNumber *output = [NSNumber numberWithInteger:enumValue];
+        callback(wrapResult(output, error));
+      }];
+    } else {
+      [channel setMessageHandler:nil];
+    }
+  }
   /// Returns the passed object, to test serialization and deserialization.
   {
     FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc]
@@ -922,6 +961,33 @@
       [channel setMessageHandler:nil];
     }
   }
+  {
+    FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc]
+           initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi."
+                        @"echoNullableEnum"
+        binaryMessenger:binaryMessenger
+                  codec:HostIntegrationCoreApiGetCodec()];
+    if (api) {
+      NSCAssert(
+          [api respondsToSelector:@selector(echoNullableEnum:error:)],
+          @"HostIntegrationCoreApi api (%@) doesn't respond to @selector(echoNullableEnum:error:)",
+          api);
+      [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) {
+        NSArray *args = message;
+        NSNumber *arg_anEnumAsNumber = GetNullableObjectAtIndex(args, 0);
+        AnEnumBox *arg_anEnum =
+            arg_anEnumAsNumber == nil
+                ? nil
+                : [[AnEnumBox alloc] initWithValue:[arg_anEnumAsNumber integerValue]];
+        FlutterError *error;
+        AnEnumBox *enumBox = [api echoNullableEnum:arg_anEnum error:&error];
+        NSNumber *output = enumBox == nil ? nil : [NSNumber numberWithInteger:enumBox.value];
+        callback(wrapResult(output, error));
+      }];
+    } else {
+      [channel setMessageHandler:nil];
+    }
+  }
   /// A no-op function taking no arguments and returning no value, to sanity
   /// test basic asynchronous calling.
   {
@@ -1089,7 +1155,7 @@
       [channel setMessageHandler:nil];
     }
   }
-  /// Returns the passed list, to test serialization and deserialization asynchronously.
+  /// Returns the passed list, to test asynchronous serialization and deserialization.
   {
     FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc]
            initWithName:
@@ -1113,7 +1179,7 @@
       [channel setMessageHandler:nil];
     }
   }
-  /// Returns the passed map, to test serialization and deserialization asynchronously.
+  /// Returns the passed map, to test asynchronous serialization and deserialization.
   {
     FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc]
            initWithName:
@@ -1138,6 +1204,31 @@
       [channel setMessageHandler:nil];
     }
   }
+  /// Returns the passed enum, to test asynchronous serialization and deserialization.
+  {
+    FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc]
+           initWithName:
+               @"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoAsyncEnum"
+        binaryMessenger:binaryMessenger
+                  codec:HostIntegrationCoreApiGetCodec()];
+    if (api) {
+      NSCAssert([api respondsToSelector:@selector(echoAsyncEnum:completion:)],
+                @"HostIntegrationCoreApi api (%@) doesn't respond to "
+                @"@selector(echoAsyncEnum:completion:)",
+                api);
+      [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) {
+        NSArray *args = message;
+        AnEnum arg_anEnum = [GetNullableObjectAtIndex(args, 0) integerValue];
+        [api echoAsyncEnum:arg_anEnum
+                completion:^(AnEnum enumValue, FlutterError *_Nullable error) {
+                  NSNumber *output = [NSNumber numberWithInteger:enumValue];
+                  callback(wrapResult(output, error));
+                }];
+      }];
+    } else {
+      [channel setMessageHandler:nil];
+    }
+  }
   /// Responds with an error from an async function returning a value.
   {
     FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc]
@@ -1396,7 +1487,7 @@
       [channel setMessageHandler:nil];
     }
   }
-  /// Returns the passed list, to test serialization and deserialization asynchronously.
+  /// Returns the passed list, to test asynchronous serialization and deserialization.
   {
     FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc]
            initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi."
@@ -1420,7 +1511,7 @@
       [channel setMessageHandler:nil];
     }
   }
-  /// Returns the passed map, to test serialization and deserialization asynchronously.
+  /// Returns the passed map, to test asynchronous serialization and deserialization.
   {
     FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc]
            initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi."
@@ -1445,6 +1536,37 @@
       [channel setMessageHandler:nil];
     }
   }
+  /// Returns the passed enum, to test asynchronous serialization and deserialization.
+  {
+    FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc]
+           initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi."
+                        @"echoAsyncNullableEnum"
+        binaryMessenger:binaryMessenger
+                  codec:HostIntegrationCoreApiGetCodec()];
+    if (api) {
+      NSCAssert([api respondsToSelector:@selector(echoAsyncNullableEnum:completion:)],
+                @"HostIntegrationCoreApi api (%@) doesn't respond to "
+                @"@selector(echoAsyncNullableEnum:completion:)",
+                api);
+      [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) {
+        NSArray *args = message;
+        NSNumber *arg_anEnumAsNumber = GetNullableObjectAtIndex(args, 0);
+        AnEnumBox *arg_anEnum =
+            arg_anEnumAsNumber == nil
+                ? nil
+                : [[AnEnumBox alloc] initWithValue:[arg_anEnumAsNumber integerValue]];
+        [api
+            echoAsyncNullableEnum:arg_anEnum
+                       completion:^(AnEnumBox *_Nullable enumValue, FlutterError *_Nullable error) {
+                         NSNumber *output =
+                             enumValue == nil ? nil : [NSNumber numberWithInteger:enumValue.value];
+                         callback(wrapResult(output, error));
+                       }];
+      }];
+    } else {
+      [channel setMessageHandler:nil];
+    }
+  }
   {
     FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc]
            initWithName:
@@ -1532,6 +1654,30 @@
   {
     FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc]
            initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi."
+                        @"callFlutterEchoAllNullableTypes"
+        binaryMessenger:binaryMessenger
+                  codec:HostIntegrationCoreApiGetCodec()];
+    if (api) {
+      NSCAssert([api respondsToSelector:@selector(callFlutterEchoAllNullableTypes:completion:)],
+                @"HostIntegrationCoreApi api (%@) doesn't respond to "
+                @"@selector(callFlutterEchoAllNullableTypes:completion:)",
+                api);
+      [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) {
+        NSArray *args = message;
+        AllNullableTypes *arg_everything = GetNullableObjectAtIndex(args, 0);
+        [api callFlutterEchoAllNullableTypes:arg_everything
+                                  completion:^(AllNullableTypes *_Nullable output,
+                                               FlutterError *_Nullable error) {
+                                    callback(wrapResult(output, error));
+                                  }];
+      }];
+    } else {
+      [channel setMessageHandler:nil];
+    }
+  }
+  {
+    FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc]
+           initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi."
                         @"callFlutterSendMultipleNullableTypes"
         binaryMessenger:binaryMessenger
                   codec:HostIntegrationCoreApiGetCodec()];
@@ -1724,6 +1870,30 @@
   {
     FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc]
            initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi."
+                        @"callFlutterEchoEnum"
+        binaryMessenger:binaryMessenger
+                  codec:HostIntegrationCoreApiGetCodec()];
+    if (api) {
+      NSCAssert([api respondsToSelector:@selector(callFlutterEchoEnum:completion:)],
+                @"HostIntegrationCoreApi api (%@) doesn't respond to "
+                @"@selector(callFlutterEchoEnum:completion:)",
+                api);
+      [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) {
+        NSArray *args = message;
+        AnEnum arg_anEnum = [GetNullableObjectAtIndex(args, 0) integerValue];
+        [api callFlutterEchoEnum:arg_anEnum
+                      completion:^(AnEnum enumValue, FlutterError *_Nullable error) {
+                        NSNumber *output = [NSNumber numberWithInteger:enumValue];
+                        callback(wrapResult(output, error));
+                      }];
+      }];
+    } else {
+      [channel setMessageHandler:nil];
+    }
+  }
+  {
+    FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc]
+           initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi."
                         @"callFlutterEchoNullableBool"
         binaryMessenger:binaryMessenger
                   codec:HostIntegrationCoreApiGetCodec()];
@@ -1889,6 +2059,37 @@
       [channel setMessageHandler:nil];
     }
   }
+  {
+    FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc]
+           initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi."
+                        @"callFlutterEchoNullableEnum"
+        binaryMessenger:binaryMessenger
+                  codec:HostIntegrationCoreApiGetCodec()];
+    if (api) {
+      NSCAssert([api respondsToSelector:@selector(callFlutterEchoNullableEnum:completion:)],
+                @"HostIntegrationCoreApi api (%@) doesn't respond to "
+                @"@selector(callFlutterEchoNullableEnum:completion:)",
+                api);
+      [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) {
+        NSArray *args = message;
+        NSNumber *arg_anEnumAsNumber = GetNullableObjectAtIndex(args, 0);
+        AnEnumBox *arg_anEnum =
+            arg_anEnumAsNumber == nil
+                ? nil
+                : [[AnEnumBox alloc] initWithValue:[arg_anEnumAsNumber integerValue]];
+        [api callFlutterEchoNullableEnum:arg_anEnum
+                              completion:^(AnEnumBox *_Nullable enumValue,
+                                           FlutterError *_Nullable error) {
+                                NSNumber *output =
+                                    enumValue == nil ? nil
+                                                     : [NSNumber numberWithInteger:enumValue.value];
+                                callback(wrapResult(output, error));
+                              }];
+      }];
+    } else {
+      [channel setMessageHandler:nil];
+    }
+  }
 }
 @interface FlutterIntegrationCoreApiCodecReader : FlutterStandardReader
 @end
@@ -2013,7 +2214,7 @@
                    completion(output, nil);
                  }];
 }
-- (void)echoAllNullableTypes:(AllNullableTypes *)arg_everything
+- (void)echoAllNullableTypes:(nullable AllNullableTypes *)arg_everything
                   completion:
                       (void (^)(AllNullableTypes *_Nullable, FlutterError *_Nullable))completion {
   FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel
@@ -2139,6 +2340,19 @@
                    completion(output, nil);
                  }];
 }
+- (void)echoEnum:(AnEnum)arg_anEnum
+      completion:(void (^)(AnEnum, FlutterError *_Nullable))completion {
+  FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel
+      messageChannelWithName:
+          @"dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoEnum"
+             binaryMessenger:self.binaryMessenger
+                       codec:FlutterIntegrationCoreApiGetCodec()];
+  [channel sendMessage:@[ [NSNumber numberWithInteger:arg_anEnum] ]
+                 reply:^(id reply) {
+                   AnEnum output = [reply integerValue];
+                   completion(output, nil);
+                 }];
+}
 - (void)echoNullableBool:(nullable NSNumber *)arg_aBool
               completion:(void (^)(NSNumber *_Nullable, FlutterError *_Nullable))completion {
   FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel
@@ -2232,6 +2446,24 @@
                    completion(output, nil);
                  }];
 }
+- (void)echoNullableEnum:(nullable AnEnumBox *)arg_anEnum
+              completion:(void (^)(AnEnumBox *_Nullable, FlutterError *_Nullable))completion {
+  FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel
+      messageChannelWithName:
+          @"dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoNullableEnum"
+             binaryMessenger:self.binaryMessenger
+                       codec:FlutterIntegrationCoreApiGetCodec()];
+  [channel sendMessage:@[ arg_anEnum == nil ? [NSNull null]
+                                            : [NSNumber numberWithInteger:arg_anEnum.value] ]
+                 reply:^(id reply) {
+                   NSNumber *outputAsNumber = reply == [NSNull null] ? nil : reply;
+                   AnEnumBox *output =
+                       outputAsNumber == nil
+                           ? nil
+                           : [[AnEnumBox alloc] initWithValue:[outputAsNumber integerValue]];
+                   completion(output, nil);
+                 }];
+}
 - (void)noopAsyncWithCompletion:(void (^)(FlutterError *_Nullable))completion {
   FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel
       messageChannelWithName:
diff --git a/packages/pigeon/platform_tests/alternate_language_test_plugin/macos/Classes/AlternateLanguageTestPlugin.m b/packages/pigeon/platform_tests/alternate_language_test_plugin/macos/Classes/AlternateLanguageTestPlugin.m
index 488ff0d..335913b 100644
--- a/packages/pigeon/platform_tests/alternate_language_test_plugin/macos/Classes/AlternateLanguageTestPlugin.m
+++ b/packages/pigeon/platform_tests/alternate_language_test_plugin/macos/Classes/AlternateLanguageTestPlugin.m
@@ -92,6 +92,10 @@
   return wrapper;
 }
 
+- (AnEnum)echoEnum:(AnEnum)anEnum error:(FlutterError *_Nullable *_Nonnull)error {
+  return anEnum;
+}
+
 - (nullable NSString *)extractNestedNullableStringFrom:(AllClassesWrapper *)wrapper
                                                  error:(FlutterError *_Nullable *_Nonnull)error {
   return wrapper.allNullableTypes.aNullableString;
@@ -159,6 +163,11 @@
   return aNullableMap;
 }
 
+- (AnEnumBox *_Nullable)echoNullableEnum:(nullable AnEnumBox *)AnEnumBoxed
+                                   error:(FlutterError *_Nullable *_Nonnull)error {
+  return AnEnumBoxed;
+}
+
 - (void)noopAsyncWithCompletion:(void (^)(FlutterError *_Nullable))completion {
   completion(nil);
 }
@@ -229,6 +238,11 @@
   completion(aMap, nil);
 }
 
+- (void)echoAsyncEnum:(AnEnum)anEnum
+           completion:(void (^)(AnEnum, FlutterError *_Nullable))completion {
+  completion(anEnum, nil);
+}
+
 - (void)echoAsyncNullableInt:(nullable NSNumber *)anInt
                   completion:(void (^)(NSNumber *_Nullable, FlutterError *_Nullable))completion {
   completion(anInt, nil);
@@ -272,6 +286,11 @@
   completion(aMap, nil);
 }
 
+- (void)echoAsyncNullableEnum:(nullable AnEnumBox *)AnEnumBoxed
+                   completion:(void (^)(AnEnumBox *_Nullable, FlutterError *_Nullable))completion {
+  completion(AnEnumBoxed, nil);
+}
+
 - (void)callFlutterNoopWithCompletion:(void (^)(FlutterError *_Nullable))completion {
   [self.flutterAPI noopWithCompletion:^(FlutterError *error) {
     completion(error);
@@ -370,6 +389,23 @@
                 }];
 }
 
+- (void)callFlutterEchoEnum:(AnEnum)anEnum
+                 completion:(void (^)(AnEnum, FlutterError *_Nullable))completion {
+  [self.flutterAPI echoEnum:anEnum
+                 completion:^(AnEnum value, FlutterError *error) {
+                   completion(value, error);
+                 }];
+}
+
+- (void)callFlutterEchoAllNullableTypes:(nullable AllNullableTypes *)everything
+                             completion:(void (^)(AllNullableTypes *_Nullable,
+                                                  FlutterError *_Nullable))completion {
+  [self.flutterAPI echoAllNullableTypes:everything
+                             completion:^(AllNullableTypes *value, FlutterError *error) {
+                               completion(value, error);
+                             }];
+}
+
 - (void)callFlutterEchoNullableBool:(nullable NSNumber *)aBool
                          completion:
                              (void (^)(NSNumber *_Nullable, FlutterError *_Nullable))completion {
@@ -433,4 +469,13 @@
                         }];
 }
 
+- (void)callFlutterEchoNullableEnum:(nullable AnEnumBox *)AnEnumBoxed
+                         completion:
+                             (void (^)(AnEnumBox *_Nullable, FlutterError *_Nullable))completion {
+  [self.flutterAPI echoNullableEnum:AnEnumBoxed
+                         completion:^(AnEnumBox *value, FlutterError *error) {
+                           completion(value, error);
+                         }];
+}
+
 @end
diff --git a/packages/pigeon/platform_tests/alternate_language_test_plugin/macos/Classes/CoreTests.gen.h b/packages/pigeon/platform_tests/alternate_language_test_plugin/macos/Classes/CoreTests.gen.h
index 2cf2d11..1b558e4 100644
--- a/packages/pigeon/platform_tests/alternate_language_test_plugin/macos/Classes/CoreTests.gen.h
+++ b/packages/pigeon/platform_tests/alternate_language_test_plugin/macos/Classes/CoreTests.gen.h
@@ -20,6 +20,12 @@
   AnEnumThree = 2,
 };
 
+/// Wrapper for AnEnum to allow for nullability.
+@interface AnEnumBox : NSObject
+@property(nonatomic, assign) AnEnum value;
+- (instancetype)initWithValue:(AnEnum)value;
+@end
+
 @class AllTypes;
 @class AllNullableTypes;
 @class AllClassesWrapper;
@@ -73,9 +79,9 @@
            nullableMapWithAnnotations:
                (nullable NSDictionary<NSString *, NSString *> *)nullableMapWithAnnotations
                 nullableMapWithObject:(nullable NSDictionary<NSString *, id> *)nullableMapWithObject
-                        aNullableEnum:(AnEnum)aNullableEnum
+                        aNullableEnum:(nullable AnEnumBox *)aNullableEnum
                       aNullableString:(nullable NSString *)aNullableString
-                      aNullableObject:(id)aNullableObject;
+                      aNullableObject:(nullable id)aNullableObject;
 @property(nonatomic, strong, nullable) NSNumber *aNullableBool;
 @property(nonatomic, strong, nullable) NSNumber *aNullableInt;
 @property(nonatomic, strong, nullable) NSNumber *aNullableInt64;
@@ -90,9 +96,9 @@
 @property(nonatomic, strong, nullable)
     NSDictionary<NSString *, NSString *> *nullableMapWithAnnotations;
 @property(nonatomic, strong, nullable) NSDictionary<NSString *, id> *nullableMapWithObject;
-@property(nonatomic, assign) AnEnum aNullableEnum;
+@property(nonatomic, strong, nullable) AnEnumBox *aNullableEnum;
 @property(nonatomic, copy, nullable) NSString *aNullableString;
-@property(nonatomic, strong) id aNullableObject;
+@property(nonatomic, strong, nullable) id aNullableObject;
 @end
 
 /// A class for testing nested class handling.
@@ -177,6 +183,10 @@
 /// @return `nil` only when `error != nil`.
 - (nullable AllClassesWrapper *)echoClassWrapper:(AllClassesWrapper *)wrapper
                                            error:(FlutterError *_Nullable *_Nonnull)error;
+/// Returns the passed enum to test serialization and deserialization.
+///
+/// @return `nil` only when `error != nil`.
+- (AnEnum)echoEnum:(AnEnum)anEnum error:(FlutterError *_Nullable *_Nonnull)error;
 /// Returns the passed object, to test serialization and deserialization.
 - (nullable AllNullableTypes *)echoAllNullableTypes:(nullable AllNullableTypes *)everything
                                               error:(FlutterError *_Nullable *_Nonnull)error;
@@ -225,6 +235,8 @@
 - (nullable NSDictionary<NSString *, id> *)echoNullableMap:
                                                (nullable NSDictionary<NSString *, id> *)aNullableMap
                                                      error:(FlutterError *_Nullable *_Nonnull)error;
+- (AnEnumBox *_Nullable)echoNullableEnum:(nullable AnEnumBox *)anEnumBoxed
+                                   error:(FlutterError *_Nullable *_Nonnull)error;
 /// A no-op function taking no arguments and returning no value, to sanity
 /// test basic asynchronous calling.
 - (void)noopAsyncWithCompletion:(void (^)(FlutterError *_Nullable))completion;
@@ -247,13 +259,16 @@
 /// Returns the passed in generic Object asynchronously.
 - (void)echoAsyncObject:(id)anObject
              completion:(void (^)(id _Nullable, FlutterError *_Nullable))completion;
-/// Returns the passed list, to test serialization and deserialization asynchronously.
+/// Returns the passed list, to test asynchronous serialization and deserialization.
 - (void)echoAsyncList:(NSArray<id> *)aList
            completion:(void (^)(NSArray<id> *_Nullable, FlutterError *_Nullable))completion;
-/// Returns the passed map, to test serialization and deserialization asynchronously.
+/// Returns the passed map, to test asynchronous serialization and deserialization.
 - (void)echoAsyncMap:(NSDictionary<NSString *, id> *)aMap
           completion:(void (^)(NSDictionary<NSString *, id> *_Nullable,
                                FlutterError *_Nullable))completion;
+/// Returns the passed enum, to test asynchronous serialization and deserialization.
+- (void)echoAsyncEnum:(AnEnum)anEnum
+           completion:(void (^)(AnEnum, FlutterError *_Nullable))completion;
 /// Responds with an error from an async function returning a value.
 - (void)throwAsyncErrorWithCompletion:(void (^)(id _Nullable, FlutterError *_Nullable))completion;
 /// Responds with an error from an async void function.
@@ -287,19 +302,25 @@
 /// Returns the passed in generic Object asynchronously.
 - (void)echoAsyncNullableObject:(nullable id)anObject
                      completion:(void (^)(id _Nullable, FlutterError *_Nullable))completion;
-/// Returns the passed list, to test serialization and deserialization asynchronously.
+/// Returns the passed list, to test asynchronous serialization and deserialization.
 - (void)echoAsyncNullableList:(nullable NSArray<id> *)aList
                    completion:(void (^)(NSArray<id> *_Nullable, FlutterError *_Nullable))completion;
-/// Returns the passed map, to test serialization and deserialization asynchronously.
+/// Returns the passed map, to test asynchronous serialization and deserialization.
 - (void)echoAsyncNullableMap:(nullable NSDictionary<NSString *, id> *)aMap
                   completion:(void (^)(NSDictionary<NSString *, id> *_Nullable,
                                        FlutterError *_Nullable))completion;
+/// Returns the passed enum, to test asynchronous serialization and deserialization.
+- (void)echoAsyncNullableEnum:(nullable AnEnumBox *)anEnumBoxed
+                   completion:(void (^)(AnEnumBox *_Nullable, FlutterError *_Nullable))completion;
 - (void)callFlutterNoopWithCompletion:(void (^)(FlutterError *_Nullable))completion;
 - (void)callFlutterThrowErrorWithCompletion:(void (^)(id _Nullable,
                                                       FlutterError *_Nullable))completion;
 - (void)callFlutterThrowErrorFromVoidWithCompletion:(void (^)(FlutterError *_Nullable))completion;
 - (void)callFlutterEchoAllTypes:(AllTypes *)everything
                      completion:(void (^)(AllTypes *_Nullable, FlutterError *_Nullable))completion;
+- (void)callFlutterEchoAllNullableTypes:(nullable AllNullableTypes *)everything
+                             completion:(void (^)(AllNullableTypes *_Nullable,
+                                                  FlutterError *_Nullable))completion;
 - (void)callFlutterSendMultipleNullableTypesABool:(nullable NSNumber *)aNullableBool
                                             anInt:(nullable NSNumber *)aNullableInt
                                           aString:(nullable NSString *)aNullableString
@@ -321,6 +342,8 @@
 - (void)callFlutterEchoMap:(NSDictionary<NSString *, id> *)aMap
                 completion:(void (^)(NSDictionary<NSString *, id> *_Nullable,
                                      FlutterError *_Nullable))completion;
+- (void)callFlutterEchoEnum:(AnEnum)anEnum
+                 completion:(void (^)(AnEnum, FlutterError *_Nullable))completion;
 - (void)callFlutterEchoNullableBool:(nullable NSNumber *)aBool
                          completion:
                              (void (^)(NSNumber *_Nullable, FlutterError *_Nullable))completion;
@@ -342,6 +365,9 @@
 - (void)callFlutterEchoNullableMap:(nullable NSDictionary<NSString *, id> *)aMap
                         completion:(void (^)(NSDictionary<NSString *, id> *_Nullable,
                                              FlutterError *_Nullable))completion;
+- (void)callFlutterEchoNullableEnum:(nullable AnEnumBox *)anEnumBoxed
+                         completion:
+                             (void (^)(AnEnumBox *_Nullable, FlutterError *_Nullable))completion;
 @end
 
 extern void HostIntegrationCoreApiSetup(id<FlutterBinaryMessenger> binaryMessenger,
@@ -365,7 +391,7 @@
 - (void)echoAllTypes:(AllTypes *)everything
           completion:(void (^)(AllTypes *_Nullable, FlutterError *_Nullable))completion;
 /// Returns the passed object, to test serialization and deserialization.
-- (void)echoAllNullableTypes:(AllNullableTypes *)everything
+- (void)echoAllNullableTypes:(nullable AllNullableTypes *)everything
                   completion:
                       (void (^)(AllNullableTypes *_Nullable, FlutterError *_Nullable))completion;
 /// Returns passed in arguments of multiple types.
@@ -399,6 +425,8 @@
 - (void)echoMap:(NSDictionary<NSString *, id> *)aMap
      completion:
          (void (^)(NSDictionary<NSString *, id> *_Nullable, FlutterError *_Nullable))completion;
+/// Returns the passed enum to test serialization and deserialization.
+- (void)echoEnum:(AnEnum)anEnum completion:(void (^)(AnEnum, FlutterError *_Nullable))completion;
 /// Returns the passed boolean, to test serialization and deserialization.
 - (void)echoNullableBool:(nullable NSNumber *)aBool
               completion:(void (^)(NSNumber *_Nullable, FlutterError *_Nullable))completion;
@@ -422,6 +450,9 @@
 - (void)echoNullableMap:(nullable NSDictionary<NSString *, id> *)aMap
              completion:(void (^)(NSDictionary<NSString *, id> *_Nullable,
                                   FlutterError *_Nullable))completion;
+/// Returns the passed enum to test serialization and deserialization.
+- (void)echoNullableEnum:(nullable AnEnumBox *)anEnumBoxed
+              completion:(void (^)(AnEnumBox *_Nullable, FlutterError *_Nullable))completion;
 /// A no-op function taking no arguments and returning no value, to sanity
 /// test basic asynchronous calling.
 - (void)noopAsyncWithCompletion:(void (^)(FlutterError *_Nullable))completion;
diff --git a/packages/pigeon/platform_tests/alternate_language_test_plugin/macos/Classes/CoreTests.gen.m b/packages/pigeon/platform_tests/alternate_language_test_plugin/macos/Classes/CoreTests.gen.m
index 524a9dd..f68ecd8 100644
--- a/packages/pigeon/platform_tests/alternate_language_test_plugin/macos/Classes/CoreTests.gen.m
+++ b/packages/pigeon/platform_tests/alternate_language_test_plugin/macos/Classes/CoreTests.gen.m
@@ -17,6 +17,16 @@
 #error File requires ARC to be enabled.
 #endif
 
+@implementation AnEnumBox
+- (instancetype)initWithValue:(AnEnum)value {
+  self = [super init];
+  if (self) {
+    _value = value;
+  }
+  return self;
+}
+@end
+
 static NSArray *wrapResult(id result, FlutterError *error) {
   if (error) {
     return @[
@@ -150,9 +160,9 @@
            nullableMapWithAnnotations:
                (nullable NSDictionary<NSString *, NSString *> *)nullableMapWithAnnotations
                 nullableMapWithObject:(nullable NSDictionary<NSString *, id> *)nullableMapWithObject
-                        aNullableEnum:(AnEnum)aNullableEnum
+                        aNullableEnum:(nullable AnEnumBox *)aNullableEnum
                       aNullableString:(nullable NSString *)aNullableString
-                      aNullableObject:(id)aNullableObject {
+                      aNullableObject:(nullable id)aNullableObject {
   AllNullableTypes *pigeonResult = [[AllNullableTypes alloc] init];
   pigeonResult.aNullableBool = aNullableBool;
   pigeonResult.aNullableInt = aNullableInt;
@@ -187,7 +197,12 @@
   pigeonResult.nullableNestedList = GetNullableObjectAtIndex(list, 10);
   pigeonResult.nullableMapWithAnnotations = GetNullableObjectAtIndex(list, 11);
   pigeonResult.nullableMapWithObject = GetNullableObjectAtIndex(list, 12);
-  pigeonResult.aNullableEnum = [GetNullableObjectAtIndex(list, 13) integerValue];
+  NSNumber *aNullableEnumAsNumber = GetNullableObjectAtIndex(list, 13);
+  AnEnumBox *aNullableEnum =
+      aNullableEnumAsNumber == nil
+          ? nil
+          : [[AnEnumBox alloc] initWithValue:[aNullableEnumAsNumber integerValue]];
+  pigeonResult.aNullableEnum = aNullableEnum;
   pigeonResult.aNullableString = GetNullableObjectAtIndex(list, 14);
   pigeonResult.aNullableObject = GetNullableObjectAtIndex(list, 15);
   return pigeonResult;
@@ -210,7 +225,8 @@
     (self.nullableNestedList ?: [NSNull null]),
     (self.nullableMapWithAnnotations ?: [NSNull null]),
     (self.nullableMapWithObject ?: [NSNull null]),
-    @(self.aNullableEnum),
+    (self.aNullableEnum == nil ? [NSNull null]
+                               : [NSNumber numberWithInteger:self.aNullableEnum.value]),
     (self.aNullableString ?: [NSNull null]),
     (self.aNullableObject ?: [NSNull null]),
   ];
@@ -636,6 +652,29 @@
       [channel setMessageHandler:nil];
     }
   }
+  /// Returns the passed enum to test serialization and deserialization.
+  {
+    FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc]
+           initWithName:
+               @"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoEnum"
+        binaryMessenger:binaryMessenger
+                  codec:HostIntegrationCoreApiGetCodec()];
+    if (api) {
+      NSCAssert([api respondsToSelector:@selector(echoEnum:error:)],
+                @"HostIntegrationCoreApi api (%@) doesn't respond to @selector(echoEnum:error:)",
+                api);
+      [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) {
+        NSArray *args = message;
+        AnEnum arg_anEnum = [GetNullableObjectAtIndex(args, 0) integerValue];
+        FlutterError *error;
+        AnEnum enumValue = [api echoEnum:arg_anEnum error:&error];
+        NSNumber *output = [NSNumber numberWithInteger:enumValue];
+        callback(wrapResult(output, error));
+      }];
+    } else {
+      [channel setMessageHandler:nil];
+    }
+  }
   /// Returns the passed object, to test serialization and deserialization.
   {
     FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc]
@@ -922,6 +961,33 @@
       [channel setMessageHandler:nil];
     }
   }
+  {
+    FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc]
+           initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi."
+                        @"echoNullableEnum"
+        binaryMessenger:binaryMessenger
+                  codec:HostIntegrationCoreApiGetCodec()];
+    if (api) {
+      NSCAssert(
+          [api respondsToSelector:@selector(echoNullableEnum:error:)],
+          @"HostIntegrationCoreApi api (%@) doesn't respond to @selector(echoNullableEnum:error:)",
+          api);
+      [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) {
+        NSArray *args = message;
+        NSNumber *arg_anEnumAsNumber = GetNullableObjectAtIndex(args, 0);
+        AnEnumBox *arg_anEnum =
+            arg_anEnumAsNumber == nil
+                ? nil
+                : [[AnEnumBox alloc] initWithValue:[arg_anEnumAsNumber integerValue]];
+        FlutterError *error;
+        AnEnumBox *enumBox = [api echoNullableEnum:arg_anEnum error:&error];
+        NSNumber *output = enumBox == nil ? nil : [NSNumber numberWithInteger:enumBox.value];
+        callback(wrapResult(output, error));
+      }];
+    } else {
+      [channel setMessageHandler:nil];
+    }
+  }
   /// A no-op function taking no arguments and returning no value, to sanity
   /// test basic asynchronous calling.
   {
@@ -1089,7 +1155,7 @@
       [channel setMessageHandler:nil];
     }
   }
-  /// Returns the passed list, to test serialization and deserialization asynchronously.
+  /// Returns the passed list, to test asynchronous serialization and deserialization.
   {
     FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc]
            initWithName:
@@ -1113,7 +1179,7 @@
       [channel setMessageHandler:nil];
     }
   }
-  /// Returns the passed map, to test serialization and deserialization asynchronously.
+  /// Returns the passed map, to test asynchronous serialization and deserialization.
   {
     FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc]
            initWithName:
@@ -1138,6 +1204,31 @@
       [channel setMessageHandler:nil];
     }
   }
+  /// Returns the passed enum, to test asynchronous serialization and deserialization.
+  {
+    FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc]
+           initWithName:
+               @"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoAsyncEnum"
+        binaryMessenger:binaryMessenger
+                  codec:HostIntegrationCoreApiGetCodec()];
+    if (api) {
+      NSCAssert([api respondsToSelector:@selector(echoAsyncEnum:completion:)],
+                @"HostIntegrationCoreApi api (%@) doesn't respond to "
+                @"@selector(echoAsyncEnum:completion:)",
+                api);
+      [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) {
+        NSArray *args = message;
+        AnEnum arg_anEnum = [GetNullableObjectAtIndex(args, 0) integerValue];
+        [api echoAsyncEnum:arg_anEnum
+                completion:^(AnEnum enumValue, FlutterError *_Nullable error) {
+                  NSNumber *output = [NSNumber numberWithInteger:enumValue];
+                  callback(wrapResult(output, error));
+                }];
+      }];
+    } else {
+      [channel setMessageHandler:nil];
+    }
+  }
   /// Responds with an error from an async function returning a value.
   {
     FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc]
@@ -1396,7 +1487,7 @@
       [channel setMessageHandler:nil];
     }
   }
-  /// Returns the passed list, to test serialization and deserialization asynchronously.
+  /// Returns the passed list, to test asynchronous serialization and deserialization.
   {
     FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc]
            initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi."
@@ -1420,7 +1511,7 @@
       [channel setMessageHandler:nil];
     }
   }
-  /// Returns the passed map, to test serialization and deserialization asynchronously.
+  /// Returns the passed map, to test asynchronous serialization and deserialization.
   {
     FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc]
            initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi."
@@ -1445,6 +1536,37 @@
       [channel setMessageHandler:nil];
     }
   }
+  /// Returns the passed enum, to test asynchronous serialization and deserialization.
+  {
+    FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc]
+           initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi."
+                        @"echoAsyncNullableEnum"
+        binaryMessenger:binaryMessenger
+                  codec:HostIntegrationCoreApiGetCodec()];
+    if (api) {
+      NSCAssert([api respondsToSelector:@selector(echoAsyncNullableEnum:completion:)],
+                @"HostIntegrationCoreApi api (%@) doesn't respond to "
+                @"@selector(echoAsyncNullableEnum:completion:)",
+                api);
+      [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) {
+        NSArray *args = message;
+        NSNumber *arg_anEnumAsNumber = GetNullableObjectAtIndex(args, 0);
+        AnEnumBox *arg_anEnum =
+            arg_anEnumAsNumber == nil
+                ? nil
+                : [[AnEnumBox alloc] initWithValue:[arg_anEnumAsNumber integerValue]];
+        [api
+            echoAsyncNullableEnum:arg_anEnum
+                       completion:^(AnEnumBox *_Nullable enumValue, FlutterError *_Nullable error) {
+                         NSNumber *output =
+                             enumValue == nil ? nil : [NSNumber numberWithInteger:enumValue.value];
+                         callback(wrapResult(output, error));
+                       }];
+      }];
+    } else {
+      [channel setMessageHandler:nil];
+    }
+  }
   {
     FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc]
            initWithName:
@@ -1532,6 +1654,30 @@
   {
     FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc]
            initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi."
+                        @"callFlutterEchoAllNullableTypes"
+        binaryMessenger:binaryMessenger
+                  codec:HostIntegrationCoreApiGetCodec()];
+    if (api) {
+      NSCAssert([api respondsToSelector:@selector(callFlutterEchoAllNullableTypes:completion:)],
+                @"HostIntegrationCoreApi api (%@) doesn't respond to "
+                @"@selector(callFlutterEchoAllNullableTypes:completion:)",
+                api);
+      [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) {
+        NSArray *args = message;
+        AllNullableTypes *arg_everything = GetNullableObjectAtIndex(args, 0);
+        [api callFlutterEchoAllNullableTypes:arg_everything
+                                  completion:^(AllNullableTypes *_Nullable output,
+                                               FlutterError *_Nullable error) {
+                                    callback(wrapResult(output, error));
+                                  }];
+      }];
+    } else {
+      [channel setMessageHandler:nil];
+    }
+  }
+  {
+    FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc]
+           initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi."
                         @"callFlutterSendMultipleNullableTypes"
         binaryMessenger:binaryMessenger
                   codec:HostIntegrationCoreApiGetCodec()];
@@ -1724,6 +1870,30 @@
   {
     FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc]
            initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi."
+                        @"callFlutterEchoEnum"
+        binaryMessenger:binaryMessenger
+                  codec:HostIntegrationCoreApiGetCodec()];
+    if (api) {
+      NSCAssert([api respondsToSelector:@selector(callFlutterEchoEnum:completion:)],
+                @"HostIntegrationCoreApi api (%@) doesn't respond to "
+                @"@selector(callFlutterEchoEnum:completion:)",
+                api);
+      [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) {
+        NSArray *args = message;
+        AnEnum arg_anEnum = [GetNullableObjectAtIndex(args, 0) integerValue];
+        [api callFlutterEchoEnum:arg_anEnum
+                      completion:^(AnEnum enumValue, FlutterError *_Nullable error) {
+                        NSNumber *output = [NSNumber numberWithInteger:enumValue];
+                        callback(wrapResult(output, error));
+                      }];
+      }];
+    } else {
+      [channel setMessageHandler:nil];
+    }
+  }
+  {
+    FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc]
+           initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi."
                         @"callFlutterEchoNullableBool"
         binaryMessenger:binaryMessenger
                   codec:HostIntegrationCoreApiGetCodec()];
@@ -1889,6 +2059,37 @@
       [channel setMessageHandler:nil];
     }
   }
+  {
+    FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc]
+           initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi."
+                        @"callFlutterEchoNullableEnum"
+        binaryMessenger:binaryMessenger
+                  codec:HostIntegrationCoreApiGetCodec()];
+    if (api) {
+      NSCAssert([api respondsToSelector:@selector(callFlutterEchoNullableEnum:completion:)],
+                @"HostIntegrationCoreApi api (%@) doesn't respond to "
+                @"@selector(callFlutterEchoNullableEnum:completion:)",
+                api);
+      [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) {
+        NSArray *args = message;
+        NSNumber *arg_anEnumAsNumber = GetNullableObjectAtIndex(args, 0);
+        AnEnumBox *arg_anEnum =
+            arg_anEnumAsNumber == nil
+                ? nil
+                : [[AnEnumBox alloc] initWithValue:[arg_anEnumAsNumber integerValue]];
+        [api callFlutterEchoNullableEnum:arg_anEnum
+                              completion:^(AnEnumBox *_Nullable enumValue,
+                                           FlutterError *_Nullable error) {
+                                NSNumber *output =
+                                    enumValue == nil ? nil
+                                                     : [NSNumber numberWithInteger:enumValue.value];
+                                callback(wrapResult(output, error));
+                              }];
+      }];
+    } else {
+      [channel setMessageHandler:nil];
+    }
+  }
 }
 @interface FlutterIntegrationCoreApiCodecReader : FlutterStandardReader
 @end
@@ -2013,7 +2214,7 @@
                    completion(output, nil);
                  }];
 }
-- (void)echoAllNullableTypes:(AllNullableTypes *)arg_everything
+- (void)echoAllNullableTypes:(nullable AllNullableTypes *)arg_everything
                   completion:
                       (void (^)(AllNullableTypes *_Nullable, FlutterError *_Nullable))completion {
   FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel
@@ -2139,6 +2340,19 @@
                    completion(output, nil);
                  }];
 }
+- (void)echoEnum:(AnEnum)arg_anEnum
+      completion:(void (^)(AnEnum, FlutterError *_Nullable))completion {
+  FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel
+      messageChannelWithName:
+          @"dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoEnum"
+             binaryMessenger:self.binaryMessenger
+                       codec:FlutterIntegrationCoreApiGetCodec()];
+  [channel sendMessage:@[ [NSNumber numberWithInteger:arg_anEnum] ]
+                 reply:^(id reply) {
+                   AnEnum output = [reply integerValue];
+                   completion(output, nil);
+                 }];
+}
 - (void)echoNullableBool:(nullable NSNumber *)arg_aBool
               completion:(void (^)(NSNumber *_Nullable, FlutterError *_Nullable))completion {
   FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel
@@ -2232,6 +2446,24 @@
                    completion(output, nil);
                  }];
 }
+- (void)echoNullableEnum:(nullable AnEnumBox *)arg_anEnum
+              completion:(void (^)(AnEnumBox *_Nullable, FlutterError *_Nullable))completion {
+  FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel
+      messageChannelWithName:
+          @"dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoNullableEnum"
+             binaryMessenger:self.binaryMessenger
+                       codec:FlutterIntegrationCoreApiGetCodec()];
+  [channel sendMessage:@[ arg_anEnum == nil ? [NSNull null]
+                                            : [NSNumber numberWithInteger:arg_anEnum.value] ]
+                 reply:^(id reply) {
+                   NSNumber *outputAsNumber = reply == [NSNull null] ? nil : reply;
+                   AnEnumBox *output =
+                       outputAsNumber == nil
+                           ? nil
+                           : [[AnEnumBox alloc] initWithValue:[outputAsNumber integerValue]];
+                   completion(output, nil);
+                 }];
+}
 - (void)noopAsyncWithCompletion:(void (^)(FlutterError *_Nullable))completion {
   FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel
       messageChannelWithName:
diff --git a/packages/pigeon/platform_tests/shared_test_plugin_code/lib/integration_tests.dart b/packages/pigeon/platform_tests/shared_test_plugin_code/lib/integration_tests.dart
index c43e2ef..954ed8e 100644
--- a/packages/pigeon/platform_tests/shared_test_plugin_code/lib/integration_tests.dart
+++ b/packages/pigeon/platform_tests/shared_test_plugin_code/lib/integration_tests.dart
@@ -104,12 +104,8 @@
         true);
     expect(allNullableTypesOne.aNullableObject,
         allNullableTypesTwo.aNullableObject);
-    // TODO(stuartmorgan): Fix and re-enable.
-    // See https://github.com/flutter/flutter/issues/118733
-    if (targetGenerator != TargetGenerator.objc) {
-      expect(
-          allNullableTypesOne.aNullableEnum, allNullableTypesTwo.aNullableEnum);
-    }
+    expect(
+        allNullableTypesOne.aNullableEnum, allNullableTypesTwo.aNullableEnum);
   }
 
   void compareAllClassesWrapper(
@@ -439,6 +435,15 @@
       expect(mapEquals(echoObject, sentObject), true);
     });
 
+    testWidgets('enums serialize and deserialize correctly',
+        (WidgetTester _) async {
+      final HostIntegrationCoreApi api = HostIntegrationCoreApi();
+
+      const AnEnum sentEnum = AnEnum.two;
+      final AnEnum receivedEnum = await api.echoEnum(sentEnum);
+      expect(receivedEnum, sentEnum);
+    });
+
     testWidgets('Nullable Int serialize and deserialize correctly',
         (WidgetTester _) async {
       final HostIntegrationCoreApi api = HostIntegrationCoreApi();
@@ -592,6 +597,15 @@
       expect(mapEquals(echoObject, sentObject), true);
     });
 
+    testWidgets('nullable enums serialize and deserialize correctly',
+        (WidgetTester _) async {
+      final HostIntegrationCoreApi api = HostIntegrationCoreApi();
+
+      const AnEnum sentEnum = AnEnum.three;
+      final AnEnum? echoEnum = await api.echoNullableEnum(sentEnum);
+      expect(echoEnum, sentEnum);
+    });
+
     testWidgets('null lists serialize and deserialize correctly',
         (WidgetTester _) async {
       final HostIntegrationCoreApi api = HostIntegrationCoreApi();
@@ -607,6 +621,15 @@
       final Map<String?, Object?>? echoObject = await api.echoNullableMap(null);
       expect(mapEquals(echoObject, null), true);
     });
+
+    testWidgets('null enums serialize and deserialize correctly',
+        (WidgetTester _) async {
+      final HostIntegrationCoreApi api = HostIntegrationCoreApi();
+
+      const AnEnum? sentEnum = null;
+      final AnEnum? echoEnum = await api.echoNullableEnum(sentEnum);
+      expect(echoEnum, sentEnum);
+    });
   });
 
   group('Host async API tests', () {
@@ -783,6 +806,15 @@
       expect(mapEquals(echoObject, sentObject), true);
     });
 
+    testWidgets('enums serialize and deserialize correctly',
+        (WidgetTester _) async {
+      final HostIntegrationCoreApi api = HostIntegrationCoreApi();
+
+      const AnEnum sentEnum = AnEnum.three;
+      final AnEnum echoEnum = await api.echoAsyncEnum(sentEnum);
+      expect(echoEnum, sentEnum);
+    });
+
     testWidgets('nullable Int async serialize and deserialize correctly',
         (WidgetTester _) async {
       final HostIntegrationCoreApi api = HostIntegrationCoreApi();
@@ -891,6 +923,15 @@
       expect(mapEquals(echoObject, sentObject), true);
     });
 
+    testWidgets('nullable enums serialize and deserialize correctly',
+        (WidgetTester _) async {
+      final HostIntegrationCoreApi api = HostIntegrationCoreApi();
+
+      const AnEnum sentEnum = AnEnum.three;
+      final AnEnum? echoEnum = await api.echoAsyncNullableEnum(sentEnum);
+      expect(echoEnum, sentEnum);
+    });
+
     testWidgets('null Ints async serialize and deserialize correctly',
         (WidgetTester _) async {
       final HostIntegrationCoreApi api = HostIntegrationCoreApi();
@@ -956,6 +997,15 @@
           await api.echoAsyncNullableMap(null);
       expect(mapEquals(echoObject, null), true);
     });
+
+    testWidgets('null enums serialize and deserialize correctly',
+        (WidgetTester _) async {
+      final HostIntegrationCoreApi api = HostIntegrationCoreApi();
+
+      const AnEnum? sentEnum = null;
+      final AnEnum? echoEnum = await api.echoAsyncNullableEnum(null);
+      expect(echoEnum, sentEnum);
+    });
   });
 
   // These tests rely on the async Dart->host calls to work correctly, since
@@ -1118,6 +1168,15 @@
       expect(mapEquals(echoObject, sentObject), true);
     });
 
+    testWidgets('enums serialize and deserialize correctly',
+        (WidgetTester _) async {
+      final HostIntegrationCoreApi api = HostIntegrationCoreApi();
+
+      const AnEnum sentEnum = AnEnum.three;
+      final AnEnum echoEnum = await api.callFlutterEchoEnum(sentEnum);
+      expect(echoEnum, sentEnum);
+    });
+
     testWidgets('nullable booleans serialize and deserialize correctly',
         (WidgetTester _) async {
       final HostIntegrationCoreApi api = HostIntegrationCoreApi();
@@ -1272,6 +1331,24 @@
           await api.callFlutterEchoNullableMap(null);
       expect(mapEquals(echoObject, null), true);
     });
+
+    testWidgets('nullable enums serialize and deserialize correctly',
+        (WidgetTester _) async {
+      final HostIntegrationCoreApi api = HostIntegrationCoreApi();
+
+      const AnEnum sentEnum = AnEnum.three;
+      final AnEnum? echoEnum = await api.callFlutterEchoNullableEnum(sentEnum);
+      expect(echoEnum, sentEnum);
+    });
+
+    testWidgets('null enums serialize and deserialize correctly',
+        (WidgetTester _) async {
+      final HostIntegrationCoreApi api = HostIntegrationCoreApi();
+
+      const AnEnum? sentEnum = null;
+      final AnEnum? echoEnum = await api.callFlutterEchoNullableEnum(sentEnum);
+      expect(echoEnum, sentEnum);
+    });
   });
 }
 
@@ -1282,7 +1359,7 @@
   }
 
   @override
-  AllNullableTypes echoAllNullableTypes(AllNullableTypes everything) {
+  AllNullableTypes? echoAllNullableTypes(AllNullableTypes? everything) {
     return everything;
   }
 
@@ -1330,6 +1407,9 @@
   Map<String?, Object?> echoMap(Map<String?, Object?> aMap) => aMap;
 
   @override
+  AnEnum echoEnum(AnEnum anEnum) => anEnum;
+
+  @override
   bool? echoNullableBool(bool? aBool) => aBool;
 
   @override
@@ -1351,6 +1431,9 @@
   Uint8List? echoNullableUint8List(Uint8List? aList) => aList;
 
   @override
+  AnEnum? echoNullableEnum(AnEnum? anEnum) => anEnum;
+
+  @override
   Future<void> noopAsync() async {}
 
   @override
diff --git a/packages/pigeon/platform_tests/shared_test_plugin_code/lib/src/generated/core_tests.gen.dart b/packages/pigeon/platform_tests/shared_test_plugin_code/lib/src/generated/core_tests.gen.dart
index 6d93313..08b223c 100644
--- a/packages/pigeon/platform_tests/shared_test_plugin_code/lib/src/generated/core_tests.gen.dart
+++ b/packages/pigeon/platform_tests/shared_test_plugin_code/lib/src/generated/core_tests.gen.dart
@@ -689,6 +689,35 @@
     }
   }
 
+  /// Returns the passed enum to test serialization and deserialization.
+  Future<AnEnum> echoEnum(AnEnum arg_anEnum) async {
+    final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+        'dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoEnum',
+        codec,
+        binaryMessenger: _binaryMessenger);
+    final List<Object?>? replyList =
+        await channel.send(<Object?>[arg_anEnum.index]) 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 AnEnum.values[replyList[0]! as int];
+    }
+  }
+
   /// Returns the passed object, to test serialization and deserialization.
   Future<AllNullableTypes?> echoAllNullableTypes(
       AllNullableTypes? arg_everything) async {
@@ -997,6 +1026,31 @@
     }
   }
 
+  Future<AnEnum?> echoNullableEnum(AnEnum? arg_anEnum) async {
+    final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+        'dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoNullableEnum',
+        codec,
+        binaryMessenger: _binaryMessenger);
+    final List<Object?>? replyList =
+        await channel.send(<Object?>[arg_anEnum?.index]) 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 {
+      return (replyList[0] as int?) == null
+          ? null
+          : AnEnum.values[replyList[0]! as int];
+    }
+  }
+
   /// A no-op function taking no arguments and returning no value, to sanity
   /// test basic asynchronous calling.
   Future<void> noopAsync() async {
@@ -1195,7 +1249,7 @@
     }
   }
 
-  /// Returns the passed list, to test serialization and deserialization asynchronously.
+  /// Returns the passed list, to test asynchronous serialization and deserialization.
   Future<List<Object?>> echoAsyncList(List<Object?> arg_aList) async {
     final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
         'dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoAsyncList',
@@ -1224,7 +1278,7 @@
     }
   }
 
-  /// Returns the passed map, to test serialization and deserialization asynchronously.
+  /// Returns the passed map, to test asynchronous serialization and deserialization.
   Future<Map<String?, Object?>> echoAsyncMap(
       Map<String?, Object?> arg_aMap) async {
     final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
@@ -1254,6 +1308,35 @@
     }
   }
 
+  /// Returns the passed enum, to test asynchronous serialization and deserialization.
+  Future<AnEnum> echoAsyncEnum(AnEnum arg_anEnum) async {
+    final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+        'dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoAsyncEnum',
+        codec,
+        binaryMessenger: _binaryMessenger);
+    final List<Object?>? replyList =
+        await channel.send(<Object?>[arg_anEnum.index]) 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 AnEnum.values[replyList[0]! as int];
+    }
+  }
+
   /// Responds with an error from an async function returning a value.
   Future<Object?> throwAsyncError() async {
     final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
@@ -1522,7 +1605,7 @@
     }
   }
 
-  /// Returns the passed list, to test serialization and deserialization asynchronously.
+  /// Returns the passed list, to test asynchronous serialization and deserialization.
   Future<List<Object?>?> echoAsyncNullableList(List<Object?>? arg_aList) async {
     final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
         'dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoAsyncNullableList',
@@ -1546,7 +1629,7 @@
     }
   }
 
-  /// Returns the passed map, to test serialization and deserialization asynchronously.
+  /// Returns the passed map, to test asynchronous serialization and deserialization.
   Future<Map<String?, Object?>?> echoAsyncNullableMap(
       Map<String?, Object?>? arg_aMap) async {
     final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
@@ -1571,6 +1654,32 @@
     }
   }
 
+  /// Returns the passed enum, to test asynchronous serialization and deserialization.
+  Future<AnEnum?> echoAsyncNullableEnum(AnEnum? arg_anEnum) async {
+    final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+        'dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoAsyncNullableEnum',
+        codec,
+        binaryMessenger: _binaryMessenger);
+    final List<Object?>? replyList =
+        await channel.send(<Object?>[arg_anEnum?.index]) 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 {
+      return (replyList[0] as int?) == null
+          ? null
+          : AnEnum.values[replyList[0]! as int];
+    }
+  }
+
   Future<void> callFlutterNoop() async {
     final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
         'dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.callFlutterNoop',
@@ -1665,6 +1774,30 @@
     }
   }
 
+  Future<AllNullableTypes?> callFlutterEchoAllNullableTypes(
+      AllNullableTypes? arg_everything) async {
+    final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+        'dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.callFlutterEchoAllNullableTypes',
+        codec,
+        binaryMessenger: _binaryMessenger);
+    final List<Object?>? replyList =
+        await channel.send(<Object?>[arg_everything]) 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 {
+      return (replyList[0] as AllNullableTypes?);
+    }
+  }
+
   Future<AllNullableTypes> callFlutterSendMultipleNullableTypes(
       bool? arg_aNullableBool,
       int? arg_aNullableInt,
@@ -1894,6 +2027,34 @@
     }
   }
 
+  Future<AnEnum> callFlutterEchoEnum(AnEnum arg_anEnum) async {
+    final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+        'dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.callFlutterEchoEnum',
+        codec,
+        binaryMessenger: _binaryMessenger);
+    final List<Object?>? replyList =
+        await channel.send(<Object?>[arg_anEnum.index]) 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 AnEnum.values[replyList[0]! as int];
+    }
+  }
+
   Future<bool?> callFlutterEchoNullableBool(bool? arg_aBool) async {
     final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
         'dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.callFlutterEchoNullableBool',
@@ -2057,6 +2218,31 @@
       return (replyList[0] as Map<Object?, Object?>?)?.cast<String?, Object?>();
     }
   }
+
+  Future<AnEnum?> callFlutterEchoNullableEnum(AnEnum? arg_anEnum) async {
+    final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+        'dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.callFlutterEchoNullableEnum',
+        codec,
+        binaryMessenger: _binaryMessenger);
+    final List<Object?>? replyList =
+        await channel.send(<Object?>[arg_anEnum?.index]) 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 {
+      return (replyList[0] as int?) == null
+          ? null
+          : AnEnum.values[replyList[0]! as int];
+    }
+  }
 }
 
 class _FlutterIntegrationCoreApiCodec extends StandardMessageCodec {
@@ -2116,7 +2302,7 @@
   AllTypes echoAllTypes(AllTypes everything);
 
   /// Returns the passed object, to test serialization and deserialization.
-  AllNullableTypes echoAllNullableTypes(AllNullableTypes everything);
+  AllNullableTypes? echoAllNullableTypes(AllNullableTypes? everything);
 
   /// Returns passed in arguments of multiple types.
   ///
@@ -2145,6 +2331,9 @@
   /// Returns the passed map, to test serialization and deserialization.
   Map<String?, Object?> echoMap(Map<String?, Object?> aMap);
 
+  /// Returns the passed enum to test serialization and deserialization.
+  AnEnum echoEnum(AnEnum anEnum);
+
   /// Returns the passed boolean, to test serialization and deserialization.
   bool? echoNullableBool(bool? aBool);
 
@@ -2166,6 +2355,9 @@
   /// Returns the passed map, to test serialization and deserialization.
   Map<String?, Object?>? echoNullableMap(Map<String?, Object?>? aMap);
 
+  /// Returns the passed enum to test serialization and deserialization.
+  AnEnum? echoNullableEnum(AnEnum? anEnum);
+
   /// A no-op function taking no arguments and returning no value, to sanity
   /// test basic asynchronous calling.
   Future<void> noopAsync();
@@ -2254,10 +2446,8 @@
           final List<Object?> args = (message as List<Object?>?)!;
           final AllNullableTypes? arg_everything =
               (args[0] as AllNullableTypes?);
-          assert(arg_everything != null,
-              'Argument for dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoAllNullableTypes was null, expected non-null AllNullableTypes.');
-          final AllNullableTypes output =
-              api.echoAllNullableTypes(arg_everything!);
+          final AllNullableTypes? output =
+              api.echoAllNullableTypes(arg_everything);
           return output;
         });
       }
@@ -2427,6 +2617,27 @@
     }
     {
       final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+          'dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoEnum',
+          codec,
+          binaryMessenger: binaryMessenger);
+      if (api == null) {
+        channel.setMessageHandler(null);
+      } else {
+        channel.setMessageHandler((Object? message) async {
+          assert(message != null,
+              'Argument for dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoEnum was null.');
+          final List<Object?> args = (message as List<Object?>?)!;
+          final AnEnum? arg_anEnum =
+              args[0] == null ? null : AnEnum.values[args[0]! as int];
+          assert(arg_anEnum != null,
+              'Argument for dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoEnum was null, expected non-null AnEnum.');
+          final AnEnum output = api.echoEnum(arg_anEnum!);
+          return output.index;
+        });
+      }
+    }
+    {
+      final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
           'dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoNullableBool',
           codec,
           binaryMessenger: binaryMessenger);
@@ -2555,6 +2766,25 @@
     }
     {
       final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+          'dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoNullableEnum',
+          codec,
+          binaryMessenger: binaryMessenger);
+      if (api == null) {
+        channel.setMessageHandler(null);
+      } else {
+        channel.setMessageHandler((Object? message) async {
+          assert(message != null,
+              'Argument for dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoNullableEnum was null.');
+          final List<Object?> args = (message as List<Object?>?)!;
+          final AnEnum? arg_anEnum =
+              args[0] == null ? null : AnEnum.values[args[0]! as int];
+          final AnEnum? output = api.echoNullableEnum(arg_anEnum);
+          return output?.index;
+        });
+      }
+    }
+    {
+      final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
           'dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.noopAsync',
           codec,
           binaryMessenger: binaryMessenger);
diff --git a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/CoreTests.gen.kt b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/CoreTests.gen.kt
index cb8aadd..ab6e862 100644
--- a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/CoreTests.gen.kt
+++ b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/CoreTests.gen.kt
@@ -331,6 +331,8 @@
   fun echoMap(aMap: Map<String?, Any?>): Map<String?, Any?>
   /** Returns the passed map to test nested class serialization and deserialization. */
   fun echoClassWrapper(wrapper: AllClassesWrapper): AllClassesWrapper
+  /** Returns the passed enum to test serialization and deserialization. */
+  fun echoEnum(anEnum: AnEnum): AnEnum
   /** Returns the passed object, to test serialization and deserialization. */
   fun echoAllNullableTypes(everything: AllNullableTypes?): AllNullableTypes?
   /**
@@ -361,6 +363,7 @@
   fun echoNullableList(aNullableList: List<Any?>?): List<Any?>?
   /** Returns the passed map, to test serialization and deserialization. */
   fun echoNullableMap(aNullableMap: Map<String?, Any?>?): Map<String?, Any?>?
+  fun echoNullableEnum(anEnum: AnEnum?): AnEnum?
   /**
    * A no-op function taking no arguments and returning no value, to sanity
    * test basic asynchronous calling.
@@ -378,10 +381,12 @@
   fun echoAsyncUint8List(aUint8List: ByteArray, callback: (Result<ByteArray>) -> Unit)
   /** Returns the passed in generic Object asynchronously. */
   fun echoAsyncObject(anObject: Any, callback: (Result<Any>) -> Unit)
-  /** Returns the passed list, to test serialization and deserialization asynchronously. */
+  /** Returns the passed list, to test asynchronous serialization and deserialization. */
   fun echoAsyncList(aList: List<Any?>, callback: (Result<List<Any?>>) -> Unit)
-  /** Returns the passed map, to test serialization and deserialization asynchronously. */
+  /** Returns the passed map, to test asynchronous serialization and deserialization. */
   fun echoAsyncMap(aMap: Map<String?, Any?>, callback: (Result<Map<String?, Any?>>) -> Unit)
+  /** Returns the passed enum, to test asynchronous serialization and deserialization. */
+  fun echoAsyncEnum(anEnum: AnEnum, callback: (Result<AnEnum>) -> Unit)
   /** Responds with an error from an async function returning a value. */
   fun throwAsyncError(callback: (Result<Any?>) -> Unit)
   /** Responds with an error from an async void function. */
@@ -404,14 +409,17 @@
   fun echoAsyncNullableUint8List(aUint8List: ByteArray?, callback: (Result<ByteArray?>) -> Unit)
   /** Returns the passed in generic Object asynchronously. */
   fun echoAsyncNullableObject(anObject: Any?, callback: (Result<Any?>) -> Unit)
-  /** Returns the passed list, to test serialization and deserialization asynchronously. */
+  /** Returns the passed list, to test asynchronous serialization and deserialization. */
   fun echoAsyncNullableList(aList: List<Any?>?, callback: (Result<List<Any?>?>) -> Unit)
-  /** Returns the passed map, to test serialization and deserialization asynchronously. */
+  /** Returns the passed map, to test asynchronous serialization and deserialization. */
   fun echoAsyncNullableMap(aMap: Map<String?, Any?>?, callback: (Result<Map<String?, Any?>?>) -> Unit)
+  /** Returns the passed enum, to test asynchronous serialization and deserialization. */
+  fun echoAsyncNullableEnum(anEnum: AnEnum?, callback: (Result<AnEnum?>) -> Unit)
   fun callFlutterNoop(callback: (Result<Unit>) -> Unit)
   fun callFlutterThrowError(callback: (Result<Any?>) -> Unit)
   fun callFlutterThrowErrorFromVoid(callback: (Result<Unit>) -> Unit)
   fun callFlutterEchoAllTypes(everything: AllTypes, callback: (Result<AllTypes>) -> Unit)
+  fun callFlutterEchoAllNullableTypes(everything: AllNullableTypes?, callback: (Result<AllNullableTypes?>) -> Unit)
   fun callFlutterSendMultipleNullableTypes(aNullableBool: Boolean?, aNullableInt: Long?, aNullableString: String?, callback: (Result<AllNullableTypes>) -> Unit)
   fun callFlutterEchoBool(aBool: Boolean, callback: (Result<Boolean>) -> Unit)
   fun callFlutterEchoInt(anInt: Long, callback: (Result<Long>) -> Unit)
@@ -420,6 +428,7 @@
   fun callFlutterEchoUint8List(aList: ByteArray, callback: (Result<ByteArray>) -> Unit)
   fun callFlutterEchoList(aList: List<Any?>, callback: (Result<List<Any?>>) -> Unit)
   fun callFlutterEchoMap(aMap: Map<String?, Any?>, callback: (Result<Map<String?, Any?>>) -> Unit)
+  fun callFlutterEchoEnum(anEnum: AnEnum, callback: (Result<AnEnum>) -> Unit)
   fun callFlutterEchoNullableBool(aBool: Boolean?, callback: (Result<Boolean?>) -> Unit)
   fun callFlutterEchoNullableInt(anInt: Long?, callback: (Result<Long?>) -> Unit)
   fun callFlutterEchoNullableDouble(aDouble: Double?, callback: (Result<Double?>) -> Unit)
@@ -427,6 +436,7 @@
   fun callFlutterEchoNullableUint8List(aList: ByteArray?, callback: (Result<ByteArray?>) -> Unit)
   fun callFlutterEchoNullableList(aList: List<Any?>?, callback: (Result<List<Any?>?>) -> Unit)
   fun callFlutterEchoNullableMap(aMap: Map<String?, Any?>?, callback: (Result<Map<String?, Any?>?>) -> Unit)
+  fun callFlutterEchoNullableEnum(anEnum: AnEnum?, callback: (Result<AnEnum?>) -> Unit)
 
   companion object {
     /** The codec used by HostIntegrationCoreApi. */
@@ -683,6 +693,24 @@
         }
       }
       run {
+        val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoEnum", codec)
+        if (api != null) {
+          channel.setMessageHandler { message, reply ->
+            val args = message as List<Any?>
+            val anEnumArg = AnEnum.ofRaw(args[0] as Int)!!
+            var wrapped: List<Any?>
+            try {
+              wrapped = listOf<Any?>(api.echoEnum(anEnumArg).raw)
+            } catch (exception: Throwable) {
+              wrapped = wrapError(exception)
+            }
+            reply.reply(wrapped)
+          }
+        } else {
+          channel.setMessageHandler(null)
+        }
+      }
+      run {
         val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoAllNullableTypes", codec)
         if (api != null) {
           channel.setMessageHandler { message, reply ->
@@ -901,6 +929,24 @@
         }
       }
       run {
+        val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoNullableEnum", codec)
+        if (api != null) {
+          channel.setMessageHandler { message, reply ->
+            val args = message as List<Any?>
+            val anEnumArg = if (args[0] == null) null else AnEnum.ofRaw(args[0] as Int)
+            var wrapped: List<Any?>
+            try {
+              wrapped = listOf<Any?>(api.echoNullableEnum(anEnumArg)?.raw)
+            } catch (exception: Throwable) {
+              wrapped = wrapError(exception)
+            }
+            reply.reply(wrapped)
+          }
+        } else {
+          channel.setMessageHandler(null)
+        }
+      }
+      run {
         val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.noopAsync", codec)
         if (api != null) {
           channel.setMessageHandler { _, reply ->
@@ -1078,6 +1124,26 @@
         }
       }
       run {
+        val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoAsyncEnum", codec)
+        if (api != null) {
+          channel.setMessageHandler { message, reply ->
+            val args = message as List<Any?>
+            val anEnumArg = AnEnum.ofRaw(args[0] as Int)!!
+            api.echoAsyncEnum(anEnumArg) { result: Result<AnEnum> ->
+              val error = result.exceptionOrNull()
+              if (error != null) {
+                reply.reply(wrapError(error))
+              } else {
+                val data = result.getOrNull()
+                reply.reply(wrapResult(data!!.raw))
+              }
+            }
+          }
+        } else {
+          channel.setMessageHandler(null)
+        }
+      }
+      run {
         val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.throwAsyncError", codec)
         if (api != null) {
           channel.setMessageHandler { _, reply ->
@@ -1331,6 +1397,26 @@
         }
       }
       run {
+        val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoAsyncNullableEnum", codec)
+        if (api != null) {
+          channel.setMessageHandler { message, reply ->
+            val args = message as List<Any?>
+            val anEnumArg = if (args[0] == null) null else AnEnum.ofRaw(args[0] as Int)
+            api.echoAsyncNullableEnum(anEnumArg) { result: Result<AnEnum?> ->
+              val error = result.exceptionOrNull()
+              if (error != null) {
+                reply.reply(wrapError(error))
+              } else {
+                val data = result.getOrNull()
+                reply.reply(wrapResult(data?.raw))
+              }
+            }
+          }
+        } else {
+          channel.setMessageHandler(null)
+        }
+      }
+      run {
         val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.callFlutterNoop", codec)
         if (api != null) {
           channel.setMessageHandler { _, reply ->
@@ -1403,6 +1489,26 @@
         }
       }
       run {
+        val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.callFlutterEchoAllNullableTypes", codec)
+        if (api != null) {
+          channel.setMessageHandler { message, reply ->
+            val args = message as List<Any?>
+            val everythingArg = args[0] as AllNullableTypes?
+            api.callFlutterEchoAllNullableTypes(everythingArg) { result: Result<AllNullableTypes?> ->
+              val error = result.exceptionOrNull()
+              if (error != null) {
+                reply.reply(wrapError(error))
+              } else {
+                val data = result.getOrNull()
+                reply.reply(wrapResult(data))
+              }
+            }
+          }
+        } else {
+          channel.setMessageHandler(null)
+        }
+      }
+      run {
         val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.callFlutterSendMultipleNullableTypes", codec)
         if (api != null) {
           channel.setMessageHandler { message, reply ->
@@ -1565,6 +1671,26 @@
         }
       }
       run {
+        val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.callFlutterEchoEnum", codec)
+        if (api != null) {
+          channel.setMessageHandler { message, reply ->
+            val args = message as List<Any?>
+            val anEnumArg = AnEnum.ofRaw(args[0] as Int)!!
+            api.callFlutterEchoEnum(anEnumArg) { result: Result<AnEnum> ->
+              val error = result.exceptionOrNull()
+              if (error != null) {
+                reply.reply(wrapError(error))
+              } else {
+                val data = result.getOrNull()
+                reply.reply(wrapResult(data!!.raw))
+              }
+            }
+          }
+        } else {
+          channel.setMessageHandler(null)
+        }
+      }
+      run {
         val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.callFlutterEchoNullableBool", codec)
         if (api != null) {
           channel.setMessageHandler { message, reply ->
@@ -1704,6 +1830,26 @@
           channel.setMessageHandler(null)
         }
       }
+      run {
+        val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.callFlutterEchoNullableEnum", codec)
+        if (api != null) {
+          channel.setMessageHandler { message, reply ->
+            val args = message as List<Any?>
+            val anEnumArg = if (args[0] == null) null else AnEnum.ofRaw(args[0] as Int)
+            api.callFlutterEchoNullableEnum(anEnumArg) { result: Result<AnEnum?> ->
+              val error = result.exceptionOrNull()
+              if (error != null) {
+                reply.reply(wrapError(error))
+              } else {
+                val data = result.getOrNull()
+                reply.reply(wrapResult(data?.raw))
+              }
+            }
+          }
+        } else {
+          channel.setMessageHandler(null)
+        }
+      }
     }
   }
 }
@@ -1805,10 +1951,10 @@
     }
   }
   /** Returns the passed object, to test serialization and deserialization. */
-  fun echoAllNullableTypes(everythingArg: AllNullableTypes, callback: (AllNullableTypes) -> Unit) {
+  fun echoAllNullableTypes(everythingArg: AllNullableTypes?, callback: (AllNullableTypes?) -> Unit) {
     val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoAllNullableTypes", codec)
     channel.send(listOf(everythingArg)) {
-      val result = it as AllNullableTypes
+      val result = it as AllNullableTypes?
       callback(result)
     }
   }
@@ -1880,6 +2026,14 @@
       callback(result)
     }
   }
+  /** Returns the passed enum to test serialization and deserialization. */
+  fun echoEnum(anEnumArg: AnEnum, callback: (AnEnum) -> Unit) {
+    val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoEnum", codec)
+    channel.send(listOf(anEnumArg.raw)) {
+      val result = AnEnum.ofRaw(it as Int)!!
+      callback(result)
+    }
+  }
   /** Returns the passed boolean, to test serialization and deserialization. */
   fun echoNullableBool(aBoolArg: Boolean?, callback: (Boolean?) -> Unit) {
     val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoNullableBool", codec)
@@ -1936,6 +2090,16 @@
       callback(result)
     }
   }
+  /** Returns the passed enum to test serialization and deserialization. */
+  fun echoNullableEnum(anEnumArg: AnEnum?, callback: (AnEnum?) -> Unit) {
+    val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoNullableEnum", codec)
+    channel.send(listOf(anEnumArg?.raw)) {
+      val result = (it as Int?)?.let {
+        AnEnum.ofRaw(it)
+      }
+      callback(result)
+    }
+  }
   /**
    * A no-op function taking no arguments and returning no value, to sanity
    * test basic asynchronous calling.
diff --git a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/TestPlugin.kt b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/TestPlugin.kt
index 93a6615..842c8ae 100644
--- a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/TestPlugin.kt
+++ b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/TestPlugin.kt
@@ -87,6 +87,10 @@
     return wrapper
   }
 
+  override fun echoEnum(anEnum: AnEnum): AnEnum {
+    return anEnum
+  }
+
   override fun extractNestedNullableString(wrapper: AllClassesWrapper): String? {
     return wrapper.allNullableTypes.aNullableString
   }
@@ -130,6 +134,11 @@
   override fun echoNullableMap(aNullableMap: Map<String?, Any?>?): Map<String?, Any?>? {
     return aNullableMap
   }
+
+  override fun echoNullableEnum(anEnum: AnEnum?): AnEnum? {
+    return anEnum
+  }
+
   override fun noopAsync(callback: (Result<Unit>) -> Unit) {
     callback(Result.success(Unit))
   }
@@ -186,6 +195,10 @@
     callback(Result.success(aMap))
   }
 
+  override fun echoAsyncEnum(anEnum: AnEnum, callback: (Result<AnEnum>) -> Unit) {
+    callback(Result.success(anEnum))
+  }
+
   override fun echoAsyncNullableInt(anInt: Long?, callback: (Result<Long?>) -> Unit) {
     callback(Result.success(anInt))
   }
@@ -218,6 +231,10 @@
     callback(Result.success(aMap))
   }
 
+  override fun echoAsyncNullableEnum(anEnum: AnEnum?, callback: (Result<AnEnum?>) -> Unit) {
+    callback(Result.success(anEnum))
+  }
+
   override fun callFlutterNoop(callback: (Result<Unit>) -> Unit) {
     flutterApi!!.noop() { callback(Result.success(Unit)) }
   }
@@ -274,6 +291,14 @@
     flutterApi!!.echoMap(aMap) { echo -> callback(Result.success(echo)) }
   }
 
+  override fun callFlutterEchoEnum(anEnum: AnEnum, callback: (Result<AnEnum>) -> Unit) {
+    flutterApi!!.echoEnum(anEnum) { echo -> callback(Result.success(echo)) }
+  }
+
+  override fun callFlutterEchoAllNullableTypes(everything: AllNullableTypes?, callback: (Result<AllNullableTypes?>) -> Unit) {
+    flutterApi!!.echoAllNullableTypes(everything) { echo -> callback(Result.success(echo)) }
+  }
+
   override fun callFlutterEchoNullableBool(aBool: Boolean?, callback: (Result<Boolean?>) -> Unit) {
     flutterApi!!.echoNullableBool(aBool) { echo -> callback(Result.success(echo)) }
   }
@@ -302,4 +327,8 @@
     flutterApi!!.echoNullableMap(aMap) { echo -> callback(Result.success(echo)) }
   }
 
+  override fun callFlutterEchoNullableEnum(anEnum: AnEnum?, callback: (Result<AnEnum?>) -> Unit) {
+    flutterApi!!.echoNullableEnum(anEnum) { echo -> callback(Result.success(echo)) }
+  }
+
 }
diff --git a/packages/pigeon/platform_tests/test_plugin/android/src/test/kotlin/com/example/test_plugin/AllDatatypesTest.kt b/packages/pigeon/platform_tests/test_plugin/android/src/test/kotlin/com/example/test_plugin/AllDatatypesTest.kt
index abfae65..af1ff21 100644
--- a/packages/pigeon/platform_tests/test_plugin/android/src/test/kotlin/com/example/test_plugin/AllDatatypesTest.kt
+++ b/packages/pigeon/platform_tests/test_plugin/android/src/test/kotlin/com/example/test_plugin/AllDatatypesTest.kt
@@ -73,7 +73,8 @@
         var didCall = false
         api.echoAllNullableTypes(everything) {
             didCall = true
-            assertNull(it.aNullableBool)
+            assertNotNull(it)
+            assertNull(it!!.aNullableBool)
             assertNull(it.aNullableInt)
             assertNull(it.aNullableDouble)
             assertNull(it.aNullableString)
diff --git a/packages/pigeon/platform_tests/test_plugin/example/ios/RunnerTests/AllDatatypesTests.swift b/packages/pigeon/platform_tests/test_plugin/example/ios/RunnerTests/AllDatatypesTests.swift
index 4992841..2373673 100644
--- a/packages/pigeon/platform_tests/test_plugin/example/ios/RunnerTests/AllDatatypesTests.swift
+++ b/packages/pigeon/platform_tests/test_plugin/example/ios/RunnerTests/AllDatatypesTests.swift
@@ -16,19 +16,20 @@
     let expectation = XCTestExpectation(description: "callback")
 
     api.echoNullable(everything) { result in
-      XCTAssertNil(result.aNullableBool)
-      XCTAssertNil(result.aNullableInt)
-      XCTAssertNil(result.aNullableDouble)
-      XCTAssertNil(result.aNullableString)
-      XCTAssertNil(result.aNullableByteArray)
-      XCTAssertNil(result.aNullable4ByteArray)
-      XCTAssertNil(result.aNullable8ByteArray)
-      XCTAssertNil(result.aNullableFloatArray)
-      XCTAssertNil(result.aNullableList)
-      XCTAssertNil(result.aNullableMap)
-      XCTAssertNil(result.nullableNestedList)
-      XCTAssertNil(result.nullableMapWithAnnotations)
-      XCTAssertNil(result.nullableMapWithObject)
+      XCTAssertNotNil(result)
+      XCTAssertNil(result!.aNullableBool)
+      XCTAssertNil(result!.aNullableInt)
+      XCTAssertNil(result!.aNullableDouble)
+      XCTAssertNil(result!.aNullableString)
+      XCTAssertNil(result!.aNullableByteArray)
+      XCTAssertNil(result!.aNullable4ByteArray)
+      XCTAssertNil(result!.aNullable8ByteArray)
+      XCTAssertNil(result!.aNullableFloatArray)
+      XCTAssertNil(result!.aNullableList)
+      XCTAssertNil(result!.aNullableMap)
+      XCTAssertNil(result!.nullableNestedList)
+      XCTAssertNil(result!.nullableMapWithAnnotations)
+      XCTAssertNil(result!.nullableMapWithObject)
       expectation.fulfill()
     }
 
@@ -58,19 +59,20 @@
     let expectation = XCTestExpectation(description: "callback")
 
     api.echoNullable(everything) { result in
-      XCTAssertEqual(result.aNullableBool, everything.aNullableBool)
-      XCTAssertEqual(result.aNullableInt, everything.aNullableInt)
-      XCTAssertEqual(result.aNullableDouble, everything.aNullableDouble)
-      XCTAssertEqual(result.aNullableString, everything.aNullableString)
-      XCTAssertEqual(result.aNullableByteArray, everything.aNullableByteArray)
-      XCTAssertEqual(result.aNullable4ByteArray, everything.aNullable4ByteArray)
-      XCTAssertEqual(result.aNullable8ByteArray, everything.aNullable8ByteArray)
-      XCTAssertEqual(result.aNullableFloatArray, everything.aNullableFloatArray)
-      XCTAssert(equalsList(result.aNullableList, everything.aNullableList))
-      XCTAssert(equalsDictionary(result.aNullableMap, everything.aNullableMap))
-      XCTAssertEqual(result.nullableNestedList, everything.nullableNestedList)
-      XCTAssertEqual(result.nullableMapWithAnnotations, everything.nullableMapWithAnnotations)
-      XCTAssert(equalsDictionary(result.nullableMapWithObject, everything.nullableMapWithObject))
+      XCTAssertNotNil(result)
+      XCTAssertEqual(result!.aNullableBool, everything.aNullableBool)
+      XCTAssertEqual(result!.aNullableInt, everything.aNullableInt)
+      XCTAssertEqual(result!.aNullableDouble, everything.aNullableDouble)
+      XCTAssertEqual(result!.aNullableString, everything.aNullableString)
+      XCTAssertEqual(result!.aNullableByteArray, everything.aNullableByteArray)
+      XCTAssertEqual(result!.aNullable4ByteArray, everything.aNullable4ByteArray)
+      XCTAssertEqual(result!.aNullable8ByteArray, everything.aNullable8ByteArray)
+      XCTAssertEqual(result!.aNullableFloatArray, everything.aNullableFloatArray)
+      XCTAssert(equalsList(result!.aNullableList, everything.aNullableList))
+      XCTAssert(equalsDictionary(result!.aNullableMap, everything.aNullableMap))
+      XCTAssertEqual(result!.nullableNestedList, everything.nullableNestedList)
+      XCTAssertEqual(result!.nullableMapWithAnnotations, everything.nullableMapWithAnnotations)
+      XCTAssert(equalsDictionary(result!.nullableMapWithObject, everything.nullableMapWithObject))
 
       expectation.fulfill()
     }
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 4a70e4a..130d53b 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
@@ -14,6 +14,10 @@
 #error("Unsupported platform.")
 #endif
 
+private func isNullish(_ value: Any?) -> Bool {
+  return value is NSNull || value == nil
+}
+
 private func wrapResult(_ result: Any?) -> [Any?] {
   return [result]
 }
@@ -135,8 +139,8 @@
 
   static func fromList(_ list: [Any?]) -> AllNullableTypes? {
     let aNullableBool: Bool? = nilOrValue(list[0])
-    let aNullableInt: Int64? = list[1] is NSNull ? nil : (list[1] is Int64? ? list[1] as! Int64? : Int64(list[1] as! Int32))
-    let aNullableInt64: Int64? = list[2] is NSNull ? nil : (list[2] is Int64? ? list[2] as! Int64? : Int64(list[2] as! Int32))
+    let aNullableInt: Int64? = isNullish(list[1]) ? nil : (list[1] is Int64? ? list[1] as! Int64? : Int64(list[1] as! Int32))
+    let aNullableInt64: Int64? = isNullish(list[2]) ? nil : (list[2] is Int64? ? list[2] as! Int64? : Int64(list[2] as! Int32))
     let aNullableDouble: Double? = nilOrValue(list[3])
     let aNullableByteArray: FlutterStandardTypedData? = nilOrValue(list[4])
     let aNullable4ByteArray: FlutterStandardTypedData? = nilOrValue(list[5])
@@ -332,6 +336,8 @@
   func echo(_ aMap: [String?: Any?]) throws -> [String?: Any?]
   /// Returns the passed map to test nested class serialization and deserialization.
   func echo(_ wrapper: AllClassesWrapper) throws -> AllClassesWrapper
+  /// Returns the passed enum to test serialization and deserialization.
+  func echo(_ anEnum: AnEnum) throws -> AnEnum
   /// Returns the passed object, to test serialization and deserialization.
   func echo(_ everything: AllNullableTypes?) throws -> AllNullableTypes?
   /// Returns the inner `aString` value from the wrapped object, to test
@@ -358,6 +364,7 @@
   func echoNullable(_ aNullableList: [Any?]?) throws -> [Any?]?
   /// Returns the passed map, to test serialization and deserialization.
   func echoNullable(_ aNullableMap: [String?: Any?]?) throws -> [String?: Any?]?
+  func echoNullable(_ anEnum: AnEnum?) throws -> AnEnum?
   /// A no-op function taking no arguments and returning no value, to sanity
   /// test basic asynchronous calling.
   func noopAsync(completion: @escaping (Result<Void, Error>) -> Void)
@@ -373,10 +380,12 @@
   func echoAsync(_ aUint8List: FlutterStandardTypedData, completion: @escaping (Result<FlutterStandardTypedData, Error>) -> Void)
   /// Returns the passed in generic Object asynchronously.
   func echoAsync(_ anObject: Any, completion: @escaping (Result<Any, Error>) -> Void)
-  /// Returns the passed list, to test serialization and deserialization asynchronously.
+  /// Returns the passed list, to test asynchronous serialization and deserialization.
   func echoAsync(_ aList: [Any?], completion: @escaping (Result<[Any?], Error>) -> Void)
-  /// Returns the passed map, to test serialization and deserialization asynchronously.
+  /// Returns the passed map, to test asynchronous serialization and deserialization.
   func echoAsync(_ aMap: [String?: Any?], completion: @escaping (Result<[String?: Any?], Error>) -> Void)
+  /// Returns the passed enum, to test asynchronous serialization and deserialization.
+  func echoAsync(_ anEnum: AnEnum, completion: @escaping (Result<AnEnum, Error>) -> Void)
   /// Responds with an error from an async function returning a value.
   func throwAsyncError(completion: @escaping (Result<Any?, Error>) -> Void)
   /// Responds with an error from an async void function.
@@ -399,14 +408,17 @@
   func echoAsyncNullable(_ aUint8List: FlutterStandardTypedData?, completion: @escaping (Result<FlutterStandardTypedData?, Error>) -> Void)
   /// Returns the passed in generic Object asynchronously.
   func echoAsyncNullable(_ anObject: Any?, completion: @escaping (Result<Any?, Error>) -> Void)
-  /// Returns the passed list, to test serialization and deserialization asynchronously.
+  /// Returns the passed list, to test asynchronous serialization and deserialization.
   func echoAsyncNullable(_ aList: [Any?]?, completion: @escaping (Result<[Any?]?, Error>) -> Void)
-  /// Returns the passed map, to test serialization and deserialization asynchronously.
+  /// Returns the passed map, to test asynchronous serialization and deserialization.
   func echoAsyncNullable(_ aMap: [String?: Any?]?, completion: @escaping (Result<[String?: Any?]?, Error>) -> Void)
+  /// Returns the passed enum, to test asynchronous serialization and deserialization.
+  func echoAsyncNullable(_ anEnum: AnEnum?, completion: @escaping (Result<AnEnum?, 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)
   func callFlutterEcho(_ everything: AllTypes, completion: @escaping (Result<AllTypes, Error>) -> Void)
+  func callFlutterEcho(_ everything: AllNullableTypes?, completion: @escaping (Result<AllNullableTypes?, Error>) -> Void)
   func callFlutterSendMultipleNullableTypes(aBool aNullableBool: Bool?, anInt aNullableInt: Int64?, aString aNullableString: String?, completion: @escaping (Result<AllNullableTypes, Error>) -> Void)
   func callFlutterEcho(_ aBool: Bool, completion: @escaping (Result<Bool, Error>) -> Void)
   func callFlutterEcho(_ anInt: Int64, completion: @escaping (Result<Int64, Error>) -> Void)
@@ -415,6 +427,7 @@
   func callFlutterEcho(_ aList: FlutterStandardTypedData, completion: @escaping (Result<FlutterStandardTypedData, Error>) -> Void)
   func callFlutterEcho(_ aList: [Any?], completion: @escaping (Result<[Any?], Error>) -> Void)
   func callFlutterEcho(_ aMap: [String?: Any?], completion: @escaping (Result<[String?: Any?], Error>) -> Void)
+  func callFlutterEcho(_ anEnum: AnEnum, completion: @escaping (Result<AnEnum, Error>) -> Void)
   func callFlutterEchoNullable(_ aBool: Bool?, completion: @escaping (Result<Bool?, Error>) -> Void)
   func callFlutterEchoNullable(_ anInt: Int64?, completion: @escaping (Result<Int64?, Error>) -> Void)
   func callFlutterEchoNullable(_ aDouble: Double?, completion: @escaping (Result<Double?, Error>) -> Void)
@@ -422,6 +435,7 @@
   func callFlutterEchoNullable(_ aList: FlutterStandardTypedData?, completion: @escaping (Result<FlutterStandardTypedData?, Error>) -> Void)
   func callFlutterEchoNullable(_ aList: [Any?]?, completion: @escaping (Result<[Any?]?, Error>) -> Void)
   func callFlutterEchoNullable(_ aMap: [String?: Any?]?, completion: @escaping (Result<[String?: Any?]?, Error>) -> Void)
+  func callFlutterNullableEcho(_ anEnum: AnEnum?, completion: @escaping (Result<AnEnum?, Error>) -> Void)
 }
 
 /// Generated setup class from Pigeon to handle messages through the `binaryMessenger`.
@@ -647,6 +661,22 @@
     } else {
       echoClassWrapperChannel.setMessageHandler(nil)
     }
+    /// Returns the passed enum to test serialization and deserialization.
+    let echoEnumChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoEnum", binaryMessenger: binaryMessenger, codec: codec)
+    if let api = api {
+      echoEnumChannel.setMessageHandler { message, reply in
+        let args = message as! [Any?]
+        let anEnumArg = AnEnum(rawValue: args[0] as! Int)!
+        do {
+          let result = try api.echo(anEnumArg)
+          reply(wrapResult(result.rawValue))
+        } catch {
+          reply(wrapError(error))
+        }
+      }
+    } else {
+      echoEnumChannel.setMessageHandler(nil)
+    }
     /// Returns the passed object, to test serialization and deserialization.
     let echoAllNullableTypesChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoAllNullableTypes", binaryMessenger: binaryMessenger, codec: codec)
     if let api = api {
@@ -703,7 +733,7 @@
       sendMultipleNullableTypesChannel.setMessageHandler { message, reply in
         let args = message as! [Any?]
         let aNullableBoolArg: Bool? = nilOrValue(args[0])
-        let aNullableIntArg: Int64? = args[1] is NSNull ? nil : (args[1] is Int64? ? args[1] as! Int64? : Int64(args[1] as! Int32))
+        let aNullableIntArg: Int64? = isNullish(args[1]) ? nil : (args[1] is Int64? ? args[1] as! Int64? : Int64(args[1] as! Int32))
         let aNullableStringArg: String? = nilOrValue(args[2])
         do {
           let result = try api.sendMultipleNullableTypes(aBool: aNullableBoolArg, anInt: aNullableIntArg, aString: aNullableStringArg)
@@ -720,7 +750,7 @@
     if let api = api {
       echoNullableIntChannel.setMessageHandler { message, reply in
         let args = message as! [Any?]
-        let aNullableIntArg: Int64? = args[0] is NSNull ? nil : (args[0] is Int64? ? args[0] as! Int64? : Int64(args[0] as! Int32))
+        let aNullableIntArg: Int64? = isNullish(args[0]) ? nil : (args[0] is Int64? ? args[0] as! Int64? : Int64(args[0] as! Int32))
         do {
           let result = try api.echo(aNullableIntArg)
           reply(wrapResult(result))
@@ -843,6 +873,21 @@
     } else {
       echoNullableMapChannel.setMessageHandler(nil)
     }
+    let echoNullableEnumChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoNullableEnum", binaryMessenger: binaryMessenger, codec: codec)
+    if let api = api {
+      echoNullableEnumChannel.setMessageHandler { message, reply in
+        let args = message as! [Any?]
+        let anEnumArg: AnEnum? = isNullish(args[0]) ? nil : AnEnum(rawValue: args[0] as! Int)!
+        do {
+          let result = try api.echoNullable(anEnumArg)
+          reply(wrapResult(result?.rawValue))
+        } catch {
+          reply(wrapError(error))
+        }
+      }
+    } else {
+      echoNullableEnumChannel.setMessageHandler(nil)
+    }
     /// A no-op function taking no arguments and returning no value, to sanity
     /// test basic asynchronous calling.
     let noopAsyncChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.noopAsync", binaryMessenger: binaryMessenger, codec: codec)
@@ -968,7 +1013,7 @@
     } else {
       echoAsyncObjectChannel.setMessageHandler(nil)
     }
-    /// Returns the passed list, to test serialization and deserialization asynchronously.
+    /// Returns the passed list, to test asynchronous serialization and deserialization.
     let echoAsyncListChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoAsyncList", binaryMessenger: binaryMessenger, codec: codec)
     if let api = api {
       echoAsyncListChannel.setMessageHandler { message, reply in
@@ -986,7 +1031,7 @@
     } else {
       echoAsyncListChannel.setMessageHandler(nil)
     }
-    /// Returns the passed map, to test serialization and deserialization asynchronously.
+    /// Returns the passed map, to test asynchronous serialization and deserialization.
     let echoAsyncMapChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoAsyncMap", binaryMessenger: binaryMessenger, codec: codec)
     if let api = api {
       echoAsyncMapChannel.setMessageHandler { message, reply in
@@ -1004,6 +1049,24 @@
     } else {
       echoAsyncMapChannel.setMessageHandler(nil)
     }
+    /// Returns the passed enum, to test asynchronous serialization and deserialization.
+    let echoAsyncEnumChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoAsyncEnum", binaryMessenger: binaryMessenger, codec: codec)
+    if let api = api {
+      echoAsyncEnumChannel.setMessageHandler { message, reply in
+        let args = message as! [Any?]
+        let anEnumArg = AnEnum(rawValue: args[0] as! Int)!
+        api.echoAsync(anEnumArg) { result in
+          switch result {
+            case .success(let res):
+              reply(wrapResult(res.rawValue))
+            case .failure(let error):
+              reply(wrapError(error))
+          }
+        }
+      }
+    } else {
+      echoAsyncEnumChannel.setMessageHandler(nil)
+    }
     /// Responds with an error from an async function returning a value.
     let throwAsyncErrorChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.throwAsyncError", binaryMessenger: binaryMessenger, codec: codec)
     if let api = api {
@@ -1093,7 +1156,7 @@
     if let api = api {
       echoAsyncNullableIntChannel.setMessageHandler { message, reply in
         let args = message as! [Any?]
-        let anIntArg: Int64? = args[0] is NSNull ? nil : (args[0] is Int64? ? args[0] as! Int64? : Int64(args[0] as! Int32))
+        let anIntArg: Int64? = isNullish(args[0]) ? nil : (args[0] is Int64? ? args[0] as! Int64? : Int64(args[0] as! Int32))
         api.echoAsyncNullable(anIntArg) { result in
           switch result {
             case .success(let res):
@@ -1196,7 +1259,7 @@
     } else {
       echoAsyncNullableObjectChannel.setMessageHandler(nil)
     }
-    /// Returns the passed list, to test serialization and deserialization asynchronously.
+    /// Returns the passed list, to test asynchronous serialization and deserialization.
     let echoAsyncNullableListChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoAsyncNullableList", binaryMessenger: binaryMessenger, codec: codec)
     if let api = api {
       echoAsyncNullableListChannel.setMessageHandler { message, reply in
@@ -1214,7 +1277,7 @@
     } else {
       echoAsyncNullableListChannel.setMessageHandler(nil)
     }
-    /// Returns the passed map, to test serialization and deserialization asynchronously.
+    /// Returns the passed map, to test asynchronous serialization and deserialization.
     let echoAsyncNullableMapChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoAsyncNullableMap", binaryMessenger: binaryMessenger, codec: codec)
     if let api = api {
       echoAsyncNullableMapChannel.setMessageHandler { message, reply in
@@ -1232,6 +1295,24 @@
     } else {
       echoAsyncNullableMapChannel.setMessageHandler(nil)
     }
+    /// Returns the passed enum, to test asynchronous serialization and deserialization.
+    let echoAsyncNullableEnumChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoAsyncNullableEnum", binaryMessenger: binaryMessenger, codec: codec)
+    if let api = api {
+      echoAsyncNullableEnumChannel.setMessageHandler { message, reply in
+        let args = message as! [Any?]
+        let anEnumArg: AnEnum? = isNullish(args[0]) ? nil : AnEnum(rawValue: args[0] as! Int)!
+        api.echoAsyncNullable(anEnumArg) { result in
+          switch result {
+            case .success(let res):
+              reply(wrapResult(res?.rawValue))
+            case .failure(let error):
+              reply(wrapError(error))
+          }
+        }
+      }
+    } else {
+      echoAsyncNullableEnumChannel.setMessageHandler(nil)
+    }
     let callFlutterNoopChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.callFlutterNoop", binaryMessenger: binaryMessenger, codec: codec)
     if let api = api {
       callFlutterNoopChannel.setMessageHandler { _, reply in
@@ -1294,12 +1375,29 @@
     } else {
       callFlutterEchoAllTypesChannel.setMessageHandler(nil)
     }
+    let callFlutterEchoAllNullableTypesChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.callFlutterEchoAllNullableTypes", binaryMessenger: binaryMessenger, codec: codec)
+    if let api = api {
+      callFlutterEchoAllNullableTypesChannel.setMessageHandler { message, reply in
+        let args = message as! [Any?]
+        let everythingArg: AllNullableTypes? = nilOrValue(args[0])
+        api.callFlutterEcho(everythingArg) { result in
+          switch result {
+            case .success(let res):
+              reply(wrapResult(res))
+            case .failure(let error):
+              reply(wrapError(error))
+          }
+        }
+      }
+    } else {
+      callFlutterEchoAllNullableTypesChannel.setMessageHandler(nil)
+    }
     let callFlutterSendMultipleNullableTypesChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.callFlutterSendMultipleNullableTypes", binaryMessenger: binaryMessenger, codec: codec)
     if let api = api {
       callFlutterSendMultipleNullableTypesChannel.setMessageHandler { message, reply in
         let args = message as! [Any?]
         let aNullableBoolArg: Bool? = nilOrValue(args[0])
-        let aNullableIntArg: Int64? = args[1] is NSNull ? nil : (args[1] is Int64? ? args[1] as! Int64? : Int64(args[1] as! Int32))
+        let aNullableIntArg: Int64? = isNullish(args[1]) ? nil : (args[1] is Int64? ? args[1] as! Int64? : Int64(args[1] as! Int32))
         let aNullableStringArg: String? = nilOrValue(args[2])
         api.callFlutterSendMultipleNullableTypes(aBool: aNullableBoolArg, anInt: aNullableIntArg, aString: aNullableStringArg) { result in
           switch result {
@@ -1432,6 +1530,23 @@
     } else {
       callFlutterEchoMapChannel.setMessageHandler(nil)
     }
+    let callFlutterEchoEnumChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.callFlutterEchoEnum", binaryMessenger: binaryMessenger, codec: codec)
+    if let api = api {
+      callFlutterEchoEnumChannel.setMessageHandler { message, reply in
+        let args = message as! [Any?]
+        let anEnumArg = AnEnum(rawValue: args[0] as! Int)!
+        api.callFlutterEcho(anEnumArg) { result in
+          switch result {
+            case .success(let res):
+              reply(wrapResult(res.rawValue))
+            case .failure(let error):
+              reply(wrapError(error))
+          }
+        }
+      }
+    } else {
+      callFlutterEchoEnumChannel.setMessageHandler(nil)
+    }
     let callFlutterEchoNullableBoolChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.callFlutterEchoNullableBool", binaryMessenger: binaryMessenger, codec: codec)
     if let api = api {
       callFlutterEchoNullableBoolChannel.setMessageHandler { message, reply in
@@ -1453,7 +1568,7 @@
     if let api = api {
       callFlutterEchoNullableIntChannel.setMessageHandler { message, reply in
         let args = message as! [Any?]
-        let anIntArg: Int64? = args[0] is NSNull ? nil : (args[0] is Int64? ? args[0] as! Int64? : Int64(args[0] as! Int32))
+        let anIntArg: Int64? = isNullish(args[0]) ? nil : (args[0] is Int64? ? args[0] as! Int64? : Int64(args[0] as! Int32))
         api.callFlutterEchoNullable(anIntArg) { result in
           switch result {
             case .success(let res):
@@ -1551,6 +1666,23 @@
     } else {
       callFlutterEchoNullableMapChannel.setMessageHandler(nil)
     }
+    let callFlutterEchoNullableEnumChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.callFlutterEchoNullableEnum", binaryMessenger: binaryMessenger, codec: codec)
+    if let api = api {
+      callFlutterEchoNullableEnumChannel.setMessageHandler { message, reply in
+        let args = message as! [Any?]
+        let anEnumArg: AnEnum? = isNullish(args[0]) ? nil : AnEnum(rawValue: args[0] as! Int)!
+        api.callFlutterNullableEcho(anEnumArg) { result in
+          switch result {
+            case .success(let res):
+              reply(wrapResult(res?.rawValue))
+            case .failure(let error):
+              reply(wrapError(error))
+          }
+        }
+      }
+    } else {
+      callFlutterEchoNullableEnumChannel.setMessageHandler(nil)
+    }
   }
 }
 private class FlutterIntegrationCoreApiCodecReader: FlutterStandardReader {
@@ -1648,10 +1780,10 @@
     }
   }
   /// Returns the passed object, to test serialization and deserialization.
-  func echoNullable(_ everythingArg: AllNullableTypes, completion: @escaping (AllNullableTypes) -> Void) {
+  func echoNullable(_ everythingArg: AllNullableTypes?, completion: @escaping (AllNullableTypes?) -> Void) {
     let channel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoAllNullableTypes", binaryMessenger: binaryMessenger, codec: codec)
     channel.sendMessage([everythingArg] as [Any?]) { response in
-      let result = response as! AllNullableTypes
+      let result: AllNullableTypes? = nilOrValue(response)
       completion(result)
     }
   }
@@ -1721,6 +1853,14 @@
       completion(result)
     }
   }
+  /// Returns the passed enum to test serialization and deserialization.
+  func echo(_ anEnumArg: AnEnum, completion: @escaping (AnEnum) -> Void) {
+    let channel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoEnum", binaryMessenger: binaryMessenger, codec: codec)
+    channel.sendMessage([anEnumArg.rawValue] as [Any?]) { response in
+      let result = AnEnum(rawValue: response as! Int)!
+      completion(result)
+    }
+  }
   /// Returns the passed boolean, to test serialization and deserialization.
   func echoNullable(_ aBoolArg: Bool?, completion: @escaping (Bool?) -> Void) {
     let channel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoNullableBool", binaryMessenger: binaryMessenger, codec: codec)
@@ -1733,7 +1873,7 @@
   func echoNullable(_ anIntArg: Int64?, completion: @escaping (Int64?) -> Void) {
     let channel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoNullableInt", binaryMessenger: binaryMessenger, codec: codec)
     channel.sendMessage([anIntArg] as [Any?]) { response in
-      let result: Int64? = response is NSNull ? nil : (response is Int64? ? response as! Int64? : Int64(response as! Int32))
+      let result: Int64? = isNullish(response) ? nil : (response is Int64? ? response as! Int64? : Int64(response as! Int32))
       completion(result)
     }
   }
@@ -1777,6 +1917,14 @@
       completion(result)
     }
   }
+  /// Returns the passed enum to test serialization and deserialization.
+  func echoNullable(_ anEnumArg: AnEnum?, completion: @escaping (AnEnum?) -> Void) {
+    let channel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoNullableEnum", binaryMessenger: binaryMessenger, codec: codec)
+    channel.sendMessage([anEnumArg?.rawValue] as [Any?]) { response in
+      let result: AnEnum? = isNullish(response) ? nil : AnEnum(rawValue: response as! Int)!
+      completion(result)
+    }
+  }
   /// A no-op function taking no arguments and returning no value, to sanity
   /// test basic asynchronous calling.
   func noopAsync(completion: @escaping () -> Void) {
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 315c86c..201b113 100644
--- a/packages/pigeon/platform_tests/test_plugin/ios/Classes/TestPlugin.swift
+++ b/packages/pigeon/platform_tests/test_plugin/ios/Classes/TestPlugin.swift
@@ -81,9 +81,13 @@
     return aMap
   }
 
-   func echo(_ wrapper: AllClassesWrapper) throws -> AllClassesWrapper {
+  func echo(_ wrapper: AllClassesWrapper) throws -> AllClassesWrapper {
     return wrapper
-   }
+  }
+
+  func echo(_ anEnum: AnEnum) throws -> AnEnum {
+    return anEnum
+  }
 
   func extractNestedNullableString(from wrapper: AllClassesWrapper) -> String? {
     return wrapper.allNullableTypes.aNullableString;
@@ -130,6 +134,10 @@
     return aNullableMap
   }
 
+  func echoNullable(_ anEnum: AnEnum?) throws -> AnEnum? {
+    return anEnum
+  }
+
   func noopAsync(completion: @escaping (Result<Void, Error>) -> Void) {
     completion(.success(Void()))
   }
@@ -186,6 +194,10 @@
     completion(.success(aMap))
   }
 
+  func echoAsync(_ anEnum: AnEnum, completion: @escaping (Result<AnEnum, Error>) -> Void) {
+    completion(.success(anEnum))
+  }
+
   func echoAsyncNullable(_ anInt: Int64?, completion: @escaping (Result<Int64?, Error>) -> Void) {
     completion(.success(anInt))
   }
@@ -218,6 +230,10 @@
     completion(.success(aMap))
   }
 
+  func echoAsyncNullable(_ anEnum: AnEnum?, completion: @escaping (Result<AnEnum?, Error>) -> Void) {
+    completion(.success(anEnum))
+  }
+
   func callFlutterNoop(completion: @escaping (Result<Void, Error>) -> Void) {
     flutterAPI.noop() {
       completion(.success(Void()))
@@ -240,6 +256,12 @@
     }
   }
 
+  func callFlutterEcho(_ everything: AllNullableTypes?, completion: @escaping (Result<AllNullableTypes?, Error>) -> Void) {
+    flutterAPI.echoNullable(everything) {
+      completion(.success($0)) 
+    }
+  }
+
   func callFlutterSendMultipleNullableTypes(
     aBool aNullableBool: Bool?,
     anInt aNullableInt: Int64?,
@@ -297,6 +319,12 @@
     }
   }
 
+  func callFlutterEcho(_ anEnum: AnEnum, completion: @escaping (Result<AnEnum, Error>) -> Void) {
+    flutterAPI.echo(anEnum) {
+      completion(.success($0))
+    }
+  }
+
   func callFlutterEchoNullable(_ aBool: Bool?, completion: @escaping (Result<Bool?, Error>) -> Void) {
     flutterAPI.echoNullable(aBool) {
       completion(.success($0))
@@ -338,4 +366,10 @@
       completion(.success($0))
     }
   }
+
+  func callFlutterNullableEcho(_ anEnum: AnEnum?, completion: @escaping (Result<AnEnum?, Error>) -> Void) {
+    flutterAPI.echoNullable(anEnum) {
+      completion(.success($0))
+    }    
+  }
 }
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 4a70e4a..130d53b 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
@@ -14,6 +14,10 @@
 #error("Unsupported platform.")
 #endif
 
+private func isNullish(_ value: Any?) -> Bool {
+  return value is NSNull || value == nil
+}
+
 private func wrapResult(_ result: Any?) -> [Any?] {
   return [result]
 }
@@ -135,8 +139,8 @@
 
   static func fromList(_ list: [Any?]) -> AllNullableTypes? {
     let aNullableBool: Bool? = nilOrValue(list[0])
-    let aNullableInt: Int64? = list[1] is NSNull ? nil : (list[1] is Int64? ? list[1] as! Int64? : Int64(list[1] as! Int32))
-    let aNullableInt64: Int64? = list[2] is NSNull ? nil : (list[2] is Int64? ? list[2] as! Int64? : Int64(list[2] as! Int32))
+    let aNullableInt: Int64? = isNullish(list[1]) ? nil : (list[1] is Int64? ? list[1] as! Int64? : Int64(list[1] as! Int32))
+    let aNullableInt64: Int64? = isNullish(list[2]) ? nil : (list[2] is Int64? ? list[2] as! Int64? : Int64(list[2] as! Int32))
     let aNullableDouble: Double? = nilOrValue(list[3])
     let aNullableByteArray: FlutterStandardTypedData? = nilOrValue(list[4])
     let aNullable4ByteArray: FlutterStandardTypedData? = nilOrValue(list[5])
@@ -332,6 +336,8 @@
   func echo(_ aMap: [String?: Any?]) throws -> [String?: Any?]
   /// Returns the passed map to test nested class serialization and deserialization.
   func echo(_ wrapper: AllClassesWrapper) throws -> AllClassesWrapper
+  /// Returns the passed enum to test serialization and deserialization.
+  func echo(_ anEnum: AnEnum) throws -> AnEnum
   /// Returns the passed object, to test serialization and deserialization.
   func echo(_ everything: AllNullableTypes?) throws -> AllNullableTypes?
   /// Returns the inner `aString` value from the wrapped object, to test
@@ -358,6 +364,7 @@
   func echoNullable(_ aNullableList: [Any?]?) throws -> [Any?]?
   /// Returns the passed map, to test serialization and deserialization.
   func echoNullable(_ aNullableMap: [String?: Any?]?) throws -> [String?: Any?]?
+  func echoNullable(_ anEnum: AnEnum?) throws -> AnEnum?
   /// A no-op function taking no arguments and returning no value, to sanity
   /// test basic asynchronous calling.
   func noopAsync(completion: @escaping (Result<Void, Error>) -> Void)
@@ -373,10 +380,12 @@
   func echoAsync(_ aUint8List: FlutterStandardTypedData, completion: @escaping (Result<FlutterStandardTypedData, Error>) -> Void)
   /// Returns the passed in generic Object asynchronously.
   func echoAsync(_ anObject: Any, completion: @escaping (Result<Any, Error>) -> Void)
-  /// Returns the passed list, to test serialization and deserialization asynchronously.
+  /// Returns the passed list, to test asynchronous serialization and deserialization.
   func echoAsync(_ aList: [Any?], completion: @escaping (Result<[Any?], Error>) -> Void)
-  /// Returns the passed map, to test serialization and deserialization asynchronously.
+  /// Returns the passed map, to test asynchronous serialization and deserialization.
   func echoAsync(_ aMap: [String?: Any?], completion: @escaping (Result<[String?: Any?], Error>) -> Void)
+  /// Returns the passed enum, to test asynchronous serialization and deserialization.
+  func echoAsync(_ anEnum: AnEnum, completion: @escaping (Result<AnEnum, Error>) -> Void)
   /// Responds with an error from an async function returning a value.
   func throwAsyncError(completion: @escaping (Result<Any?, Error>) -> Void)
   /// Responds with an error from an async void function.
@@ -399,14 +408,17 @@
   func echoAsyncNullable(_ aUint8List: FlutterStandardTypedData?, completion: @escaping (Result<FlutterStandardTypedData?, Error>) -> Void)
   /// Returns the passed in generic Object asynchronously.
   func echoAsyncNullable(_ anObject: Any?, completion: @escaping (Result<Any?, Error>) -> Void)
-  /// Returns the passed list, to test serialization and deserialization asynchronously.
+  /// Returns the passed list, to test asynchronous serialization and deserialization.
   func echoAsyncNullable(_ aList: [Any?]?, completion: @escaping (Result<[Any?]?, Error>) -> Void)
-  /// Returns the passed map, to test serialization and deserialization asynchronously.
+  /// Returns the passed map, to test asynchronous serialization and deserialization.
   func echoAsyncNullable(_ aMap: [String?: Any?]?, completion: @escaping (Result<[String?: Any?]?, Error>) -> Void)
+  /// Returns the passed enum, to test asynchronous serialization and deserialization.
+  func echoAsyncNullable(_ anEnum: AnEnum?, completion: @escaping (Result<AnEnum?, 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)
   func callFlutterEcho(_ everything: AllTypes, completion: @escaping (Result<AllTypes, Error>) -> Void)
+  func callFlutterEcho(_ everything: AllNullableTypes?, completion: @escaping (Result<AllNullableTypes?, Error>) -> Void)
   func callFlutterSendMultipleNullableTypes(aBool aNullableBool: Bool?, anInt aNullableInt: Int64?, aString aNullableString: String?, completion: @escaping (Result<AllNullableTypes, Error>) -> Void)
   func callFlutterEcho(_ aBool: Bool, completion: @escaping (Result<Bool, Error>) -> Void)
   func callFlutterEcho(_ anInt: Int64, completion: @escaping (Result<Int64, Error>) -> Void)
@@ -415,6 +427,7 @@
   func callFlutterEcho(_ aList: FlutterStandardTypedData, completion: @escaping (Result<FlutterStandardTypedData, Error>) -> Void)
   func callFlutterEcho(_ aList: [Any?], completion: @escaping (Result<[Any?], Error>) -> Void)
   func callFlutterEcho(_ aMap: [String?: Any?], completion: @escaping (Result<[String?: Any?], Error>) -> Void)
+  func callFlutterEcho(_ anEnum: AnEnum, completion: @escaping (Result<AnEnum, Error>) -> Void)
   func callFlutterEchoNullable(_ aBool: Bool?, completion: @escaping (Result<Bool?, Error>) -> Void)
   func callFlutterEchoNullable(_ anInt: Int64?, completion: @escaping (Result<Int64?, Error>) -> Void)
   func callFlutterEchoNullable(_ aDouble: Double?, completion: @escaping (Result<Double?, Error>) -> Void)
@@ -422,6 +435,7 @@
   func callFlutterEchoNullable(_ aList: FlutterStandardTypedData?, completion: @escaping (Result<FlutterStandardTypedData?, Error>) -> Void)
   func callFlutterEchoNullable(_ aList: [Any?]?, completion: @escaping (Result<[Any?]?, Error>) -> Void)
   func callFlutterEchoNullable(_ aMap: [String?: Any?]?, completion: @escaping (Result<[String?: Any?]?, Error>) -> Void)
+  func callFlutterNullableEcho(_ anEnum: AnEnum?, completion: @escaping (Result<AnEnum?, Error>) -> Void)
 }
 
 /// Generated setup class from Pigeon to handle messages through the `binaryMessenger`.
@@ -647,6 +661,22 @@
     } else {
       echoClassWrapperChannel.setMessageHandler(nil)
     }
+    /// Returns the passed enum to test serialization and deserialization.
+    let echoEnumChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoEnum", binaryMessenger: binaryMessenger, codec: codec)
+    if let api = api {
+      echoEnumChannel.setMessageHandler { message, reply in
+        let args = message as! [Any?]
+        let anEnumArg = AnEnum(rawValue: args[0] as! Int)!
+        do {
+          let result = try api.echo(anEnumArg)
+          reply(wrapResult(result.rawValue))
+        } catch {
+          reply(wrapError(error))
+        }
+      }
+    } else {
+      echoEnumChannel.setMessageHandler(nil)
+    }
     /// Returns the passed object, to test serialization and deserialization.
     let echoAllNullableTypesChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoAllNullableTypes", binaryMessenger: binaryMessenger, codec: codec)
     if let api = api {
@@ -703,7 +733,7 @@
       sendMultipleNullableTypesChannel.setMessageHandler { message, reply in
         let args = message as! [Any?]
         let aNullableBoolArg: Bool? = nilOrValue(args[0])
-        let aNullableIntArg: Int64? = args[1] is NSNull ? nil : (args[1] is Int64? ? args[1] as! Int64? : Int64(args[1] as! Int32))
+        let aNullableIntArg: Int64? = isNullish(args[1]) ? nil : (args[1] is Int64? ? args[1] as! Int64? : Int64(args[1] as! Int32))
         let aNullableStringArg: String? = nilOrValue(args[2])
         do {
           let result = try api.sendMultipleNullableTypes(aBool: aNullableBoolArg, anInt: aNullableIntArg, aString: aNullableStringArg)
@@ -720,7 +750,7 @@
     if let api = api {
       echoNullableIntChannel.setMessageHandler { message, reply in
         let args = message as! [Any?]
-        let aNullableIntArg: Int64? = args[0] is NSNull ? nil : (args[0] is Int64? ? args[0] as! Int64? : Int64(args[0] as! Int32))
+        let aNullableIntArg: Int64? = isNullish(args[0]) ? nil : (args[0] is Int64? ? args[0] as! Int64? : Int64(args[0] as! Int32))
         do {
           let result = try api.echo(aNullableIntArg)
           reply(wrapResult(result))
@@ -843,6 +873,21 @@
     } else {
       echoNullableMapChannel.setMessageHandler(nil)
     }
+    let echoNullableEnumChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoNullableEnum", binaryMessenger: binaryMessenger, codec: codec)
+    if let api = api {
+      echoNullableEnumChannel.setMessageHandler { message, reply in
+        let args = message as! [Any?]
+        let anEnumArg: AnEnum? = isNullish(args[0]) ? nil : AnEnum(rawValue: args[0] as! Int)!
+        do {
+          let result = try api.echoNullable(anEnumArg)
+          reply(wrapResult(result?.rawValue))
+        } catch {
+          reply(wrapError(error))
+        }
+      }
+    } else {
+      echoNullableEnumChannel.setMessageHandler(nil)
+    }
     /// A no-op function taking no arguments and returning no value, to sanity
     /// test basic asynchronous calling.
     let noopAsyncChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.noopAsync", binaryMessenger: binaryMessenger, codec: codec)
@@ -968,7 +1013,7 @@
     } else {
       echoAsyncObjectChannel.setMessageHandler(nil)
     }
-    /// Returns the passed list, to test serialization and deserialization asynchronously.
+    /// Returns the passed list, to test asynchronous serialization and deserialization.
     let echoAsyncListChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoAsyncList", binaryMessenger: binaryMessenger, codec: codec)
     if let api = api {
       echoAsyncListChannel.setMessageHandler { message, reply in
@@ -986,7 +1031,7 @@
     } else {
       echoAsyncListChannel.setMessageHandler(nil)
     }
-    /// Returns the passed map, to test serialization and deserialization asynchronously.
+    /// Returns the passed map, to test asynchronous serialization and deserialization.
     let echoAsyncMapChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoAsyncMap", binaryMessenger: binaryMessenger, codec: codec)
     if let api = api {
       echoAsyncMapChannel.setMessageHandler { message, reply in
@@ -1004,6 +1049,24 @@
     } else {
       echoAsyncMapChannel.setMessageHandler(nil)
     }
+    /// Returns the passed enum, to test asynchronous serialization and deserialization.
+    let echoAsyncEnumChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoAsyncEnum", binaryMessenger: binaryMessenger, codec: codec)
+    if let api = api {
+      echoAsyncEnumChannel.setMessageHandler { message, reply in
+        let args = message as! [Any?]
+        let anEnumArg = AnEnum(rawValue: args[0] as! Int)!
+        api.echoAsync(anEnumArg) { result in
+          switch result {
+            case .success(let res):
+              reply(wrapResult(res.rawValue))
+            case .failure(let error):
+              reply(wrapError(error))
+          }
+        }
+      }
+    } else {
+      echoAsyncEnumChannel.setMessageHandler(nil)
+    }
     /// Responds with an error from an async function returning a value.
     let throwAsyncErrorChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.throwAsyncError", binaryMessenger: binaryMessenger, codec: codec)
     if let api = api {
@@ -1093,7 +1156,7 @@
     if let api = api {
       echoAsyncNullableIntChannel.setMessageHandler { message, reply in
         let args = message as! [Any?]
-        let anIntArg: Int64? = args[0] is NSNull ? nil : (args[0] is Int64? ? args[0] as! Int64? : Int64(args[0] as! Int32))
+        let anIntArg: Int64? = isNullish(args[0]) ? nil : (args[0] is Int64? ? args[0] as! Int64? : Int64(args[0] as! Int32))
         api.echoAsyncNullable(anIntArg) { result in
           switch result {
             case .success(let res):
@@ -1196,7 +1259,7 @@
     } else {
       echoAsyncNullableObjectChannel.setMessageHandler(nil)
     }
-    /// Returns the passed list, to test serialization and deserialization asynchronously.
+    /// Returns the passed list, to test asynchronous serialization and deserialization.
     let echoAsyncNullableListChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoAsyncNullableList", binaryMessenger: binaryMessenger, codec: codec)
     if let api = api {
       echoAsyncNullableListChannel.setMessageHandler { message, reply in
@@ -1214,7 +1277,7 @@
     } else {
       echoAsyncNullableListChannel.setMessageHandler(nil)
     }
-    /// Returns the passed map, to test serialization and deserialization asynchronously.
+    /// Returns the passed map, to test asynchronous serialization and deserialization.
     let echoAsyncNullableMapChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoAsyncNullableMap", binaryMessenger: binaryMessenger, codec: codec)
     if let api = api {
       echoAsyncNullableMapChannel.setMessageHandler { message, reply in
@@ -1232,6 +1295,24 @@
     } else {
       echoAsyncNullableMapChannel.setMessageHandler(nil)
     }
+    /// Returns the passed enum, to test asynchronous serialization and deserialization.
+    let echoAsyncNullableEnumChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoAsyncNullableEnum", binaryMessenger: binaryMessenger, codec: codec)
+    if let api = api {
+      echoAsyncNullableEnumChannel.setMessageHandler { message, reply in
+        let args = message as! [Any?]
+        let anEnumArg: AnEnum? = isNullish(args[0]) ? nil : AnEnum(rawValue: args[0] as! Int)!
+        api.echoAsyncNullable(anEnumArg) { result in
+          switch result {
+            case .success(let res):
+              reply(wrapResult(res?.rawValue))
+            case .failure(let error):
+              reply(wrapError(error))
+          }
+        }
+      }
+    } else {
+      echoAsyncNullableEnumChannel.setMessageHandler(nil)
+    }
     let callFlutterNoopChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.callFlutterNoop", binaryMessenger: binaryMessenger, codec: codec)
     if let api = api {
       callFlutterNoopChannel.setMessageHandler { _, reply in
@@ -1294,12 +1375,29 @@
     } else {
       callFlutterEchoAllTypesChannel.setMessageHandler(nil)
     }
+    let callFlutterEchoAllNullableTypesChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.callFlutterEchoAllNullableTypes", binaryMessenger: binaryMessenger, codec: codec)
+    if let api = api {
+      callFlutterEchoAllNullableTypesChannel.setMessageHandler { message, reply in
+        let args = message as! [Any?]
+        let everythingArg: AllNullableTypes? = nilOrValue(args[0])
+        api.callFlutterEcho(everythingArg) { result in
+          switch result {
+            case .success(let res):
+              reply(wrapResult(res))
+            case .failure(let error):
+              reply(wrapError(error))
+          }
+        }
+      }
+    } else {
+      callFlutterEchoAllNullableTypesChannel.setMessageHandler(nil)
+    }
     let callFlutterSendMultipleNullableTypesChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.callFlutterSendMultipleNullableTypes", binaryMessenger: binaryMessenger, codec: codec)
     if let api = api {
       callFlutterSendMultipleNullableTypesChannel.setMessageHandler { message, reply in
         let args = message as! [Any?]
         let aNullableBoolArg: Bool? = nilOrValue(args[0])
-        let aNullableIntArg: Int64? = args[1] is NSNull ? nil : (args[1] is Int64? ? args[1] as! Int64? : Int64(args[1] as! Int32))
+        let aNullableIntArg: Int64? = isNullish(args[1]) ? nil : (args[1] is Int64? ? args[1] as! Int64? : Int64(args[1] as! Int32))
         let aNullableStringArg: String? = nilOrValue(args[2])
         api.callFlutterSendMultipleNullableTypes(aBool: aNullableBoolArg, anInt: aNullableIntArg, aString: aNullableStringArg) { result in
           switch result {
@@ -1432,6 +1530,23 @@
     } else {
       callFlutterEchoMapChannel.setMessageHandler(nil)
     }
+    let callFlutterEchoEnumChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.callFlutterEchoEnum", binaryMessenger: binaryMessenger, codec: codec)
+    if let api = api {
+      callFlutterEchoEnumChannel.setMessageHandler { message, reply in
+        let args = message as! [Any?]
+        let anEnumArg = AnEnum(rawValue: args[0] as! Int)!
+        api.callFlutterEcho(anEnumArg) { result in
+          switch result {
+            case .success(let res):
+              reply(wrapResult(res.rawValue))
+            case .failure(let error):
+              reply(wrapError(error))
+          }
+        }
+      }
+    } else {
+      callFlutterEchoEnumChannel.setMessageHandler(nil)
+    }
     let callFlutterEchoNullableBoolChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.callFlutterEchoNullableBool", binaryMessenger: binaryMessenger, codec: codec)
     if let api = api {
       callFlutterEchoNullableBoolChannel.setMessageHandler { message, reply in
@@ -1453,7 +1568,7 @@
     if let api = api {
       callFlutterEchoNullableIntChannel.setMessageHandler { message, reply in
         let args = message as! [Any?]
-        let anIntArg: Int64? = args[0] is NSNull ? nil : (args[0] is Int64? ? args[0] as! Int64? : Int64(args[0] as! Int32))
+        let anIntArg: Int64? = isNullish(args[0]) ? nil : (args[0] is Int64? ? args[0] as! Int64? : Int64(args[0] as! Int32))
         api.callFlutterEchoNullable(anIntArg) { result in
           switch result {
             case .success(let res):
@@ -1551,6 +1666,23 @@
     } else {
       callFlutterEchoNullableMapChannel.setMessageHandler(nil)
     }
+    let callFlutterEchoNullableEnumChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.callFlutterEchoNullableEnum", binaryMessenger: binaryMessenger, codec: codec)
+    if let api = api {
+      callFlutterEchoNullableEnumChannel.setMessageHandler { message, reply in
+        let args = message as! [Any?]
+        let anEnumArg: AnEnum? = isNullish(args[0]) ? nil : AnEnum(rawValue: args[0] as! Int)!
+        api.callFlutterNullableEcho(anEnumArg) { result in
+          switch result {
+            case .success(let res):
+              reply(wrapResult(res?.rawValue))
+            case .failure(let error):
+              reply(wrapError(error))
+          }
+        }
+      }
+    } else {
+      callFlutterEchoNullableEnumChannel.setMessageHandler(nil)
+    }
   }
 }
 private class FlutterIntegrationCoreApiCodecReader: FlutterStandardReader {
@@ -1648,10 +1780,10 @@
     }
   }
   /// Returns the passed object, to test serialization and deserialization.
-  func echoNullable(_ everythingArg: AllNullableTypes, completion: @escaping (AllNullableTypes) -> Void) {
+  func echoNullable(_ everythingArg: AllNullableTypes?, completion: @escaping (AllNullableTypes?) -> Void) {
     let channel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoAllNullableTypes", binaryMessenger: binaryMessenger, codec: codec)
     channel.sendMessage([everythingArg] as [Any?]) { response in
-      let result = response as! AllNullableTypes
+      let result: AllNullableTypes? = nilOrValue(response)
       completion(result)
     }
   }
@@ -1721,6 +1853,14 @@
       completion(result)
     }
   }
+  /// Returns the passed enum to test serialization and deserialization.
+  func echo(_ anEnumArg: AnEnum, completion: @escaping (AnEnum) -> Void) {
+    let channel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoEnum", binaryMessenger: binaryMessenger, codec: codec)
+    channel.sendMessage([anEnumArg.rawValue] as [Any?]) { response in
+      let result = AnEnum(rawValue: response as! Int)!
+      completion(result)
+    }
+  }
   /// Returns the passed boolean, to test serialization and deserialization.
   func echoNullable(_ aBoolArg: Bool?, completion: @escaping (Bool?) -> Void) {
     let channel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoNullableBool", binaryMessenger: binaryMessenger, codec: codec)
@@ -1733,7 +1873,7 @@
   func echoNullable(_ anIntArg: Int64?, completion: @escaping (Int64?) -> Void) {
     let channel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoNullableInt", binaryMessenger: binaryMessenger, codec: codec)
     channel.sendMessage([anIntArg] as [Any?]) { response in
-      let result: Int64? = response is NSNull ? nil : (response is Int64? ? response as! Int64? : Int64(response as! Int32))
+      let result: Int64? = isNullish(response) ? nil : (response is Int64? ? response as! Int64? : Int64(response as! Int32))
       completion(result)
     }
   }
@@ -1777,6 +1917,14 @@
       completion(result)
     }
   }
+  /// Returns the passed enum to test serialization and deserialization.
+  func echoNullable(_ anEnumArg: AnEnum?, completion: @escaping (AnEnum?) -> Void) {
+    let channel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoNullableEnum", binaryMessenger: binaryMessenger, codec: codec)
+    channel.sendMessage([anEnumArg?.rawValue] as [Any?]) { response in
+      let result: AnEnum? = isNullish(response) ? nil : AnEnum(rawValue: response as! Int)!
+      completion(result)
+    }
+  }
   /// A no-op function taking no arguments and returning no value, to sanity
   /// test basic asynchronous calling.
   func noopAsync(completion: @escaping () -> Void) {
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 d5f27f6..a06f7a5 100644
--- a/packages/pigeon/platform_tests/test_plugin/macos/Classes/TestPlugin.swift
+++ b/packages/pigeon/platform_tests/test_plugin/macos/Classes/TestPlugin.swift
@@ -24,6 +24,7 @@
   }
 
   // MARK: HostIntegrationCoreApi implementation
+
   func noop() {
 
   }
@@ -80,9 +81,13 @@
     return aMap
   }
 
-   func echo(_ wrapper: AllClassesWrapper) throws -> AllClassesWrapper {
+  func echo(_ wrapper: AllClassesWrapper) throws -> AllClassesWrapper {
     return wrapper
-   }
+  }
+
+  func echo(_ anEnum: AnEnum) throws -> AnEnum {
+    return anEnum
+  }
 
   func extractNestedNullableString(from wrapper: AllClassesWrapper) -> String? {
     return wrapper.allNullableTypes.aNullableString;
@@ -129,6 +134,10 @@
     return aNullableMap
   }
 
+  func echoNullable(_ anEnum: AnEnum?) throws -> AnEnum? {
+    return anEnum
+  }
+
   func noopAsync(completion: @escaping (Result<Void, Error>) -> Void) {
     completion(.success(Void()))
   }
@@ -185,6 +194,10 @@
     completion(.success(aMap))
   }
 
+  func echoAsync(_ anEnum: AnEnum, completion: @escaping (Result<AnEnum, Error>) -> Void) {
+    completion(.success(anEnum))
+  }
+
   func echoAsyncNullable(_ anInt: Int64?, completion: @escaping (Result<Int64?, Error>) -> Void) {
     completion(.success(anInt))
   }
@@ -217,6 +230,10 @@
     completion(.success(aMap))
   }
 
+  func echoAsyncNullable(_ anEnum: AnEnum?, completion: @escaping (Result<AnEnum?, Error>) -> Void) {
+    completion(.success(anEnum))
+  }
+
   func callFlutterNoop(completion: @escaping (Result<Void, Error>) -> Void) {
     flutterAPI.noop() {
       completion(.success(Void()))
@@ -239,6 +256,12 @@
     }
   }
 
+  func callFlutterEcho(_ everything: AllNullableTypes?, completion: @escaping (Result<AllNullableTypes?, Error>) -> Void) {
+    flutterAPI.echoNullable(everything) {
+      completion(.success($0)) 
+    }
+  }
+
   func callFlutterSendMultipleNullableTypes(
     aBool aNullableBool: Bool?,
     anInt aNullableInt: Int64?,
@@ -296,6 +319,12 @@
     }
   }
 
+  func callFlutterEcho(_ anEnum: AnEnum, completion: @escaping (Result<AnEnum, Error>) -> Void) {
+    flutterAPI.echo(anEnum) {
+      completion(.success($0))
+    }
+  }
+
   func callFlutterEchoNullable(_ aBool: Bool?, completion: @escaping (Result<Bool?, Error>) -> Void) {
     flutterAPI.echoNullable(aBool) {
       completion(.success($0))
@@ -337,4 +366,10 @@
       completion(.success($0))
     }
   }
-}
\ No newline at end of file
+
+  func callFlutterNullableEcho(_ anEnum: AnEnum?, completion: @escaping (Result<AnEnum?, Error>) -> Void) {
+    flutterAPI.echoNullable(anEnum) {
+      completion(.success($0))
+    }    
+  }
+}
diff --git a/packages/pigeon/platform_tests/test_plugin/windows/pigeon/core_tests.gen.cpp b/packages/pigeon/platform_tests/test_plugin/windows/pigeon/core_tests.gen.cpp
index 4692500..3ef10d1 100644
--- a/packages/pigeon/platform_tests/test_plugin/windows/pigeon/core_tests.gen.cpp
+++ b/packages/pigeon/platform_tests/test_plugin/windows/pigeon/core_tests.gen.cpp
@@ -1211,6 +1211,42 @@
     auto channel = std::make_unique<BasicMessageChannel<>>(
         binary_messenger,
         "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi."
+        "echoEnum",
+        &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_an_enum_arg = args.at(0);
+              if (encodable_an_enum_arg.IsNull()) {
+                reply(WrapError("an_enum_arg unexpectedly null."));
+                return;
+              }
+              const AnEnum& an_enum_arg =
+                  (AnEnum)encodable_an_enum_arg.LongValue();
+              ErrorOr<AnEnum> output = api->EchoEnum(an_enum_arg);
+              if (output.has_error()) {
+                reply(WrapError(output.error()));
+                return;
+              }
+              EncodableList wrapped;
+              wrapped.push_back(
+                  EncodableValue((int)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.pigeon_integration_tests.HostIntegrationCoreApi."
         "echoAllNullableTypes",
         &GetCodec());
     if (api != nullptr) {
@@ -1683,6 +1719,51 @@
     auto channel = std::make_unique<BasicMessageChannel<>>(
         binary_messenger,
         "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi."
+        "echoNullableEnum",
+        &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_an_enum_arg = args.at(0);
+              const int64_t an_enum_arg_value =
+                  encodable_an_enum_arg.IsNull()
+                      ? 0
+                      : encodable_an_enum_arg.LongValue();
+              const auto an_enum_arg =
+                  encodable_an_enum_arg.IsNull()
+                      ? std::nullopt
+                      : std::make_optional<AnEnum>(
+                            static_cast<AnEnum>(an_enum_arg_value));
+              ErrorOr<std::optional<AnEnum>> output = api->EchoNullableEnum(
+                  an_enum_arg ? &(*an_enum_arg) : nullptr);
+              if (output.has_error()) {
+                reply(WrapError(output.error()));
+                return;
+              }
+              EncodableList wrapped;
+              auto output_optional = std::move(output).TakeValue();
+              if (output_optional) {
+                wrapped.push_back(
+                    EncodableValue((int)std::move(output_optional).value()));
+              } else {
+                wrapped.push_back(EncodableValue());
+              }
+              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.pigeon_integration_tests.HostIntegrationCoreApi."
         "noopAsync",
         &GetCodec());
     if (api != nullptr) {
@@ -2011,6 +2092,44 @@
     auto channel = std::make_unique<BasicMessageChannel<>>(
         binary_messenger,
         "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi."
+        "echoAsyncEnum",
+        &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_an_enum_arg = args.at(0);
+              if (encodable_an_enum_arg.IsNull()) {
+                reply(WrapError("an_enum_arg unexpectedly null."));
+                return;
+              }
+              const AnEnum& an_enum_arg =
+                  (AnEnum)encodable_an_enum_arg.LongValue();
+              api->EchoAsyncEnum(
+                  an_enum_arg, [reply](ErrorOr<AnEnum>&& output) {
+                    if (output.has_error()) {
+                      reply(WrapError(output.error()));
+                      return;
+                    }
+                    EncodableList wrapped;
+                    wrapped.push_back(
+                        EncodableValue((int)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.pigeon_integration_tests.HostIntegrationCoreApi."
         "throwAsyncError",
         &GetCodec());
     if (api != nullptr) {
@@ -2512,6 +2631,53 @@
     auto channel = std::make_unique<BasicMessageChannel<>>(
         binary_messenger,
         "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi."
+        "echoAsyncNullableEnum",
+        &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_an_enum_arg = args.at(0);
+              const int64_t an_enum_arg_value =
+                  encodable_an_enum_arg.IsNull()
+                      ? 0
+                      : encodable_an_enum_arg.LongValue();
+              const auto an_enum_arg =
+                  encodable_an_enum_arg.IsNull()
+                      ? std::nullopt
+                      : std::make_optional<AnEnum>(
+                            static_cast<AnEnum>(an_enum_arg_value));
+              api->EchoAsyncNullableEnum(
+                  an_enum_arg ? &(*an_enum_arg) : nullptr,
+                  [reply](ErrorOr<std::optional<AnEnum>>&& output) {
+                    if (output.has_error()) {
+                      reply(WrapError(output.error()));
+                      return;
+                    }
+                    EncodableList wrapped;
+                    auto output_optional = std::move(output).TakeValue();
+                    if (output_optional) {
+                      wrapped.push_back(EncodableValue(
+                          (int)std::move(output_optional).value()));
+                    } else {
+                      wrapped.push_back(EncodableValue());
+                    }
+                    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.pigeon_integration_tests.HostIntegrationCoreApi."
         "callFlutterNoop",
         &GetCodec());
     if (api != nullptr) {
@@ -2643,6 +2809,48 @@
     auto channel = std::make_unique<BasicMessageChannel<>>(
         binary_messenger,
         "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi."
+        "callFlutterEchoAllNullableTypes",
+        &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_everything_arg = args.at(0);
+              const auto* everything_arg =
+                  &(std::any_cast<const AllNullableTypes&>(
+                      std::get<CustomEncodableValue>(
+                          encodable_everything_arg)));
+              api->CallFlutterEchoAllNullableTypes(
+                  everything_arg,
+                  [reply](ErrorOr<std::optional<AllNullableTypes>>&& output) {
+                    if (output.has_error()) {
+                      reply(WrapError(output.error()));
+                      return;
+                    }
+                    EncodableList wrapped;
+                    auto output_optional = std::move(output).TakeValue();
+                    if (output_optional) {
+                      wrapped.push_back(CustomEncodableValue(
+                          std::move(output_optional).value()));
+                    } else {
+                      wrapped.push_back(EncodableValue());
+                    }
+                    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.pigeon_integration_tests.HostIntegrationCoreApi."
         "callFlutterSendMultipleNullableTypes",
         &GetCodec());
     if (api != nullptr) {
@@ -2955,6 +3163,44 @@
     auto channel = std::make_unique<BasicMessageChannel<>>(
         binary_messenger,
         "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi."
+        "callFlutterEchoEnum",
+        &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_an_enum_arg = args.at(0);
+              if (encodable_an_enum_arg.IsNull()) {
+                reply(WrapError("an_enum_arg unexpectedly null."));
+                return;
+              }
+              const AnEnum& an_enum_arg =
+                  (AnEnum)encodable_an_enum_arg.LongValue();
+              api->CallFlutterEchoEnum(
+                  an_enum_arg, [reply](ErrorOr<AnEnum>&& output) {
+                    if (output.has_error()) {
+                      reply(WrapError(output.error()));
+                      return;
+                    }
+                    EncodableList wrapped;
+                    wrapped.push_back(
+                        EncodableValue((int)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.pigeon_integration_tests.HostIntegrationCoreApi."
         "callFlutterEchoNullableBool",
         &GetCodec());
     if (api != nullptr) {
@@ -3234,6 +3480,53 @@
       channel->SetMessageHandler(nullptr);
     }
   }
+  {
+    auto channel = std::make_unique<BasicMessageChannel<>>(
+        binary_messenger,
+        "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi."
+        "callFlutterEchoNullableEnum",
+        &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_an_enum_arg = args.at(0);
+              const int64_t an_enum_arg_value =
+                  encodable_an_enum_arg.IsNull()
+                      ? 0
+                      : encodable_an_enum_arg.LongValue();
+              const auto an_enum_arg =
+                  encodable_an_enum_arg.IsNull()
+                      ? std::nullopt
+                      : std::make_optional<AnEnum>(
+                            static_cast<AnEnum>(an_enum_arg_value));
+              api->CallFlutterEchoNullableEnum(
+                  an_enum_arg ? &(*an_enum_arg) : nullptr,
+                  [reply](ErrorOr<std::optional<AnEnum>>&& output) {
+                    if (output.has_error()) {
+                      reply(WrapError(output.error()));
+                      return;
+                    }
+                    EncodableList wrapped;
+                    auto output_optional = std::move(output).TakeValue();
+                    if (output_optional) {
+                      wrapped.push_back(EncodableValue(
+                          (int)std::move(output_optional).value()));
+                    } else {
+                      wrapped.push_back(EncodableValue());
+                    }
+                    reply(EncodableValue(std::move(wrapped)));
+                  });
+            } catch (const std::exception& exception) {
+              reply(WrapError(exception.what()));
+            }
+          });
+    } else {
+      channel->SetMessageHandler(nullptr);
+    }
+  }
 }
 
 EncodableValue HostIntegrationCoreApi::WrapError(
@@ -3398,8 +3691,8 @@
 }
 
 void FlutterIntegrationCoreApi::EchoAllNullableTypes(
-    const AllNullableTypes& everything_arg,
-    std::function<void(const AllNullableTypes&)>&& on_success,
+    const AllNullableTypes* everything_arg,
+    std::function<void(const AllNullableTypes*)>&& on_success,
     std::function<void(const FlutterError&)>&& on_error) {
   auto channel = std::make_unique<BasicMessageChannel<>>(
       binary_messenger_,
@@ -3407,7 +3700,7 @@
       "echoAllNullableTypes",
       &GetCodec());
   EncodableValue encoded_api_arguments = EncodableValue(EncodableList{
-      CustomEncodableValue(everything_arg),
+      everything_arg ? CustomEncodableValue(*everything_arg) : EncodableValue(),
   });
   channel->Send(
       encoded_api_arguments,
@@ -3416,8 +3709,8 @@
         std::unique_ptr<EncodableValue> response =
             GetCodec().DecodeMessage(reply, reply_size);
         const auto& encodable_return_value = *response;
-        const auto& return_value = std::any_cast<const AllNullableTypes&>(
-            std::get<CustomEncodableValue>(encodable_return_value));
+        const auto* return_value = &(std::any_cast<const AllNullableTypes&>(
+            std::get<CustomEncodableValue>(encodable_return_value)));
         on_success(return_value);
       });
 }
@@ -3622,6 +3915,29 @@
       });
 }
 
+void FlutterIntegrationCoreApi::EchoEnum(
+    const AnEnum& an_enum_arg, std::function<void(const AnEnum&)>&& on_success,
+    std::function<void(const FlutterError&)>&& on_error) {
+  auto channel = std::make_unique<BasicMessageChannel<>>(
+      binary_messenger_,
+      "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi."
+      "echoEnum",
+      &GetCodec());
+  EncodableValue encoded_api_arguments = EncodableValue(EncodableList{
+      EncodableValue((int)an_enum_arg),
+  });
+  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 AnEnum& return_value = (AnEnum)encodable_return_value.LongValue();
+        on_success(return_value);
+      });
+}
+
 void FlutterIntegrationCoreApi::EchoNullableBool(
     const bool* a_bool_arg, std::function<void(const bool*)>&& on_success,
     std::function<void(const FlutterError&)>&& on_error) {
@@ -3796,6 +4112,33 @@
       });
 }
 
+void FlutterIntegrationCoreApi::EchoNullableEnum(
+    const AnEnum* an_enum_arg, std::function<void(const AnEnum*)>&& on_success,
+    std::function<void(const FlutterError&)>&& on_error) {
+  auto channel = std::make_unique<BasicMessageChannel<>>(
+      binary_messenger_,
+      "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi."
+      "echoNullableEnum",
+      &GetCodec());
+  EncodableValue encoded_api_arguments = EncodableValue(EncodableList{
+      an_enum_arg ? EncodableValue((int)(*an_enum_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 int64_t return_value_value = encodable_return_value.IsNull()
+                                           ? 0
+                                           : encodable_return_value.LongValue();
+    const auto* return_value =
+        encodable_return_value.IsNull() ? nullptr : &(AnEnum)return_value_value;
+    on_success(return_value);
+  });
+}
+
 void FlutterIntegrationCoreApi::NoopAsync(
     std::function<void(void)>&& on_success,
     std::function<void(const FlutterError&)>&& on_error) {
diff --git a/packages/pigeon/platform_tests/test_plugin/windows/pigeon/core_tests.gen.h b/packages/pigeon/platform_tests/test_plugin/windows/pigeon/core_tests.gen.h
index 0a7b9f2..1076ae4 100644
--- a/packages/pigeon/platform_tests/test_plugin/windows/pigeon/core_tests.gen.h
+++ b/packages/pigeon/platform_tests/test_plugin/windows/pigeon/core_tests.gen.h
@@ -412,6 +412,8 @@
   // deserialization.
   virtual ErrorOr<AllClassesWrapper> EchoClassWrapper(
       const AllClassesWrapper& wrapper) = 0;
+  // Returns the passed enum to test serialization and deserialization.
+  virtual ErrorOr<AnEnum> EchoEnum(const AnEnum& an_enum) = 0;
   // Returns the passed object, to test serialization and deserialization.
   virtual ErrorOr<std::optional<AllNullableTypes>> EchoAllNullableTypes(
       const AllNullableTypes* everything) = 0;
@@ -451,6 +453,8 @@
   // Returns the passed map, to test serialization and deserialization.
   virtual ErrorOr<std::optional<flutter::EncodableMap>> EchoNullableMap(
       const flutter::EncodableMap* a_nullable_map) = 0;
+  virtual ErrorOr<std::optional<AnEnum>> EchoNullableEnum(
+      const AnEnum* an_enum) = 0;
   // A no-op function taking no arguments and returning no value, to sanity
   // test basic asynchronous calling.
   virtual void NoopAsync(
@@ -476,16 +480,21 @@
   virtual void EchoAsyncObject(
       const flutter::EncodableValue& an_object,
       std::function<void(ErrorOr<flutter::EncodableValue> reply)> result) = 0;
-  // Returns the passed list, to test serialization and deserialization
-  // asynchronously.
+  // Returns the passed list, to test asynchronous serialization and
+  // deserialization.
   virtual void EchoAsyncList(
       const flutter::EncodableList& a_list,
       std::function<void(ErrorOr<flutter::EncodableList> reply)> result) = 0;
-  // Returns the passed map, to test serialization and deserialization
-  // asynchronously.
+  // Returns the passed map, to test asynchronous serialization and
+  // deserialization.
   virtual void EchoAsyncMap(
       const flutter::EncodableMap& a_map,
       std::function<void(ErrorOr<flutter::EncodableMap> reply)> result) = 0;
+  // Returns the passed enum, to test asynchronous serialization and
+  // deserialization.
+  virtual void EchoAsyncEnum(
+      const AnEnum& an_enum,
+      std::function<void(ErrorOr<AnEnum> reply)> result) = 0;
   // Responds with an error from an async function returning a value.
   virtual void ThrowAsyncError(
       std::function<void(ErrorOr<std::optional<flutter::EncodableValue>> reply)>
@@ -533,18 +542,23 @@
       const flutter::EncodableValue* an_object,
       std::function<void(ErrorOr<std::optional<flutter::EncodableValue>> reply)>
           result) = 0;
-  // Returns the passed list, to test serialization and deserialization
-  // asynchronously.
+  // Returns the passed list, to test asynchronous serialization and
+  // deserialization.
   virtual void EchoAsyncNullableList(
       const flutter::EncodableList* a_list,
       std::function<void(ErrorOr<std::optional<flutter::EncodableList>> reply)>
           result) = 0;
-  // Returns the passed map, to test serialization and deserialization
-  // asynchronously.
+  // Returns the passed map, to test asynchronous serialization and
+  // deserialization.
   virtual void EchoAsyncNullableMap(
       const flutter::EncodableMap* a_map,
       std::function<void(ErrorOr<std::optional<flutter::EncodableMap>> reply)>
           result) = 0;
+  // Returns the passed enum, to test asynchronous serialization and
+  // deserialization.
+  virtual void EchoAsyncNullableEnum(
+      const AnEnum* an_enum,
+      std::function<void(ErrorOr<std::optional<AnEnum>> reply)> result) = 0;
   virtual void CallFlutterNoop(
       std::function<void(std::optional<FlutterError> reply)> result) = 0;
   virtual void CallFlutterThrowError(
@@ -555,6 +569,10 @@
   virtual void CallFlutterEchoAllTypes(
       const AllTypes& everything,
       std::function<void(ErrorOr<AllTypes> reply)> result) = 0;
+  virtual void CallFlutterEchoAllNullableTypes(
+      const AllNullableTypes* everything,
+      std::function<void(ErrorOr<std::optional<AllNullableTypes>> reply)>
+          result) = 0;
   virtual void CallFlutterSendMultipleNullableTypes(
       const bool* a_nullable_bool, const int64_t* a_nullable_int,
       const std::string* a_nullable_string,
@@ -577,6 +595,9 @@
   virtual void CallFlutterEchoMap(
       const flutter::EncodableMap& a_map,
       std::function<void(ErrorOr<flutter::EncodableMap> reply)> result) = 0;
+  virtual void CallFlutterEchoEnum(
+      const AnEnum& an_enum,
+      std::function<void(ErrorOr<AnEnum> reply)> result) = 0;
   virtual void CallFlutterEchoNullableBool(
       const bool* a_bool,
       std::function<void(ErrorOr<std::optional<bool>> reply)> result) = 0;
@@ -602,6 +623,9 @@
       const flutter::EncodableMap* a_map,
       std::function<void(ErrorOr<std::optional<flutter::EncodableMap>> reply)>
           result) = 0;
+  virtual void CallFlutterEchoNullableEnum(
+      const AnEnum* an_enum,
+      std::function<void(ErrorOr<std::optional<AnEnum>> reply)> result) = 0;
 
   // The codec used by HostIntegrationCoreApi.
   static const flutter::StandardMessageCodec& GetCodec();
@@ -658,8 +682,8 @@
                     std::function<void(const FlutterError&)>&& on_error);
   // Returns the passed object, to test serialization and deserialization.
   void EchoAllNullableTypes(
-      const AllNullableTypes& everything,
-      std::function<void(const AllNullableTypes&)>&& on_success,
+      const AllNullableTypes* everything,
+      std::function<void(const AllNullableTypes*)>&& on_success,
       std::function<void(const FlutterError&)>&& on_error);
   // Returns passed in arguments of multiple types.
   //
@@ -695,6 +719,10 @@
   void EchoMap(const flutter::EncodableMap& a_map,
                std::function<void(const flutter::EncodableMap&)>&& on_success,
                std::function<void(const FlutterError&)>&& on_error);
+  // Returns the passed enum to test serialization and deserialization.
+  void EchoEnum(const AnEnum& an_enum,
+                std::function<void(const AnEnum&)>&& on_success,
+                std::function<void(const FlutterError&)>&& on_error);
   // Returns the passed boolean, to test serialization and deserialization.
   void EchoNullableBool(const bool* a_bool,
                         std::function<void(const bool*)>&& on_success,
@@ -726,6 +754,10 @@
       const flutter::EncodableMap* a_map,
       std::function<void(const flutter::EncodableMap*)>&& on_success,
       std::function<void(const FlutterError&)>&& on_error);
+  // Returns the passed enum to test serialization and deserialization.
+  void EchoNullableEnum(const AnEnum* an_enum,
+                        std::function<void(const AnEnum*)>&& on_success,
+                        std::function<void(const FlutterError&)>&& on_error);
   // A no-op function taking no arguments and returning no value, to sanity
   // test basic asynchronous calling.
   void NoopAsync(std::function<void(void)>&& on_success,
diff --git a/packages/pigeon/platform_tests/test_plugin/windows/test_plugin.cpp b/packages/pigeon/platform_tests/test_plugin/windows/test_plugin.cpp
index 1257710..ea71a07 100644
--- a/packages/pigeon/platform_tests/test_plugin/windows/test_plugin.cpp
+++ b/packages/pigeon/platform_tests/test_plugin/windows/test_plugin.cpp
@@ -19,6 +19,7 @@
 using core_tests_pigeontest::AllClassesWrapper;
 using core_tests_pigeontest::AllNullableTypes;
 using core_tests_pigeontest::AllTypes;
+using core_tests_pigeontest::AnEnum;
 using core_tests_pigeontest::ErrorOr;
 using core_tests_pigeontest::FlutterError;
 using core_tests_pigeontest::FlutterIntegrationCoreApi;
@@ -103,6 +104,8 @@
   return wrapper;
 }
 
+ErrorOr<AnEnum> TestPlugin::EchoEnum(const AnEnum& an_enum) { return an_enum; }
+
 ErrorOr<std::optional<std::string>> TestPlugin::ExtractNestedNullableString(
     const AllClassesWrapper& wrapper) {
   const std::string* inner_string =
@@ -207,6 +210,14 @@
   return *a_nullable_map;
 };
 
+ErrorOr<std::optional<AnEnum>> TestPlugin::EchoNullableEnum(
+    const AnEnum* an_enum) {
+  if (!an_enum) {
+    return std::nullopt;
+  }
+  return *an_enum;
+}
+
 void TestPlugin::NoopAsync(
     std::function<void(std::optional<FlutterError> reply)> result) {
   result(std::nullopt);
@@ -277,6 +288,11 @@
   result(a_map);
 }
 
+void TestPlugin::EchoAsyncEnum(
+    const AnEnum& an_enum, std::function<void(ErrorOr<AnEnum> reply)> result) {
+  result(an_enum);
+}
+
 void TestPlugin::EchoAsyncNullableAllNullableTypes(
     const AllNullableTypes* everything,
     std::function<void(ErrorOr<std::optional<AllNullableTypes>> reply)>
@@ -335,6 +351,12 @@
   result(a_map ? std::optional<EncodableMap>(*a_map) : std::nullopt);
 }
 
+void TestPlugin::EchoAsyncNullableEnum(
+    const AnEnum* an_enum,
+    std::function<void(ErrorOr<std::optional<AnEnum>> reply)> result) {
+  result(an_enum ? std::optional<AnEnum>(*an_enum) : std::nullopt);
+}
+
 void TestPlugin::CallFlutterNoop(
     std::function<void(std::optional<FlutterError> reply)> result) {
   flutter_api_->Noop([result]() { result(std::nullopt); },
@@ -366,6 +388,18 @@
       [result](const FlutterError& error) { result(error); });
 }
 
+void TestPlugin::CallFlutterEchoAllNullableTypes(
+    const AllNullableTypes* everything,
+    std::function<void(ErrorOr<std::optional<AllNullableTypes>> reply)>
+        result) {
+  flutter_api_->EchoAllNullableTypes(
+      everything,
+      [result](const AllNullableTypes* echo) {
+        result(echo ? std::optional<AllNullableTypes>(*echo) : std::nullopt);
+      },
+      [result](const FlutterError& error) { result(error); });
+}
+
 void TestPlugin::CallFlutterSendMultipleNullableTypes(
     const bool* a_nullable_bool, const int64_t* a_nullable_int,
     const std::string* a_nullable_string,
@@ -429,6 +463,13 @@
       [result](const FlutterError& error) { result(error); });
 }
 
+void TestPlugin::CallFlutterEchoEnum(
+    const AnEnum& an_enum, std::function<void(ErrorOr<AnEnum> reply)> result) {
+  flutter_api_->EchoEnum(
+      an_enum, [result](const AnEnum& echo) { result(echo); },
+      [result](const FlutterError& error) { result(error); });
+}
+
 void TestPlugin::CallFlutterEchoNullableBool(
     const bool* a_bool,
     std::function<void(ErrorOr<std::optional<bool>> reply)> result) {
@@ -508,4 +549,15 @@
       [result](const FlutterError& error) { result(error); });
 }
 
+void TestPlugin::CallFlutterEchoNullableEnum(
+    const AnEnum* an_enum,
+    std::function<void(ErrorOr<std::optional<AnEnum>> reply)> result) {
+  flutter_api_->EchoNullableEnum(
+      an_enum,
+      [result](const AnEnum* echo) {
+        result(echo ? std::optional<AnEnum>(*echo) : std::nullopt);
+      },
+      [result](const FlutterError& error) { result(error); });
+}
+
 }  // namespace test_plugin
diff --git a/packages/pigeon/platform_tests/test_plugin/windows/test_plugin.h b/packages/pigeon/platform_tests/test_plugin/windows/test_plugin.h
index 649d20f..d66a8e5 100644
--- a/packages/pigeon/platform_tests/test_plugin/windows/test_plugin.h
+++ b/packages/pigeon/platform_tests/test_plugin/windows/test_plugin.h
@@ -61,6 +61,8 @@
   core_tests_pigeontest::ErrorOr<core_tests_pigeontest::AllClassesWrapper>
   EchoClassWrapper(
       const core_tests_pigeontest::AllClassesWrapper& wrapper) override;
+  core_tests_pigeontest::ErrorOr<core_tests_pigeontest::AnEnum> EchoEnum(
+      const core_tests_pigeontest::AnEnum& an_enum) override;
   core_tests_pigeontest::ErrorOr<std::optional<std::string>>
   ExtractNestedNullableString(
       const core_tests_pigeontest::AllClassesWrapper& wrapper) override;
@@ -87,6 +89,8 @@
   EchoNullableList(const flutter::EncodableList* a_nullable_list) override;
   core_tests_pigeontest::ErrorOr<std::optional<flutter::EncodableMap>>
   EchoNullableMap(const flutter::EncodableMap* a_nullable_map) override;
+  core_tests_pigeontest::ErrorOr<std::optional<core_tests_pigeontest::AnEnum>>
+  EchoNullableEnum(const core_tests_pigeontest::AnEnum* an_enum) override;
   void NoopAsync(std::function<
                  void(std::optional<core_tests_pigeontest::FlutterError> reply)>
                      result) override;
@@ -152,6 +156,11 @@
       std::function<
           void(core_tests_pigeontest::ErrorOr<flutter::EncodableMap> reply)>
           result) override;
+  void EchoAsyncEnum(
+      const core_tests_pigeontest::AnEnum& an_enum,
+      std::function<void(
+          core_tests_pigeontest::ErrorOr<core_tests_pigeontest::AnEnum> reply)>
+          result) override;
   void EchoAsyncNullableInt(
       const int64_t* an_int,
       std::function<
@@ -196,6 +205,12 @@
           core_tests_pigeontest::ErrorOr<std::optional<flutter::EncodableMap>>
               reply)>
           result) override;
+  void EchoAsyncNullableEnum(
+      const core_tests_pigeontest::AnEnum* an_enum,
+      std::function<void(core_tests_pigeontest::ErrorOr<
+                         std::optional<core_tests_pigeontest::AnEnum>>
+                             reply)>
+          result) override;
   void CallFlutterNoop(
       std::function<
           void(std::optional<core_tests_pigeontest::FlutterError> reply)>
@@ -215,6 +230,12 @@
           void(core_tests_pigeontest::ErrorOr<core_tests_pigeontest::AllTypes>
                    reply)>
           result) override;
+  void CallFlutterEchoAllNullableTypes(
+      const core_tests_pigeontest::AllNullableTypes* everything,
+      std::function<void(core_tests_pigeontest::ErrorOr<
+                         std::optional<core_tests_pigeontest::AllNullableTypes>>
+                             reply)>
+          result) override;
   void CallFlutterSendMultipleNullableTypes(
       const bool* a_nullable_bool, const int64_t* a_nullable_int,
       const std::string* a_nullable_string,
@@ -253,6 +274,11 @@
       std::function<
           void(core_tests_pigeontest::ErrorOr<flutter::EncodableMap> reply)>
           result) override;
+  void CallFlutterEchoEnum(
+      const core_tests_pigeontest::AnEnum& an_enum,
+      std::function<void(
+          core_tests_pigeontest::ErrorOr<core_tests_pigeontest::AnEnum> reply)>
+          result) override;
   void CallFlutterEchoNullableBool(
       const bool* a_bool,
       std::function<
@@ -291,6 +317,12 @@
           core_tests_pigeontest::ErrorOr<std::optional<flutter::EncodableMap>>
               reply)>
           result) override;
+  void CallFlutterEchoNullableEnum(
+      const core_tests_pigeontest::AnEnum* an_enum,
+      std::function<void(core_tests_pigeontest::ErrorOr<
+                         std::optional<core_tests_pigeontest::AnEnum>>
+                             reply)>
+          result) override;
 
  private:
   std::unique_ptr<core_tests_pigeontest::FlutterIntegrationCoreApi>
diff --git a/packages/pigeon/pubspec.yaml b/packages/pigeon/pubspec.yaml
index 68516fc..3c7a70e 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.6 # This must match the version in lib/generator_tools.dart
+version: 11.0.0 # This must match the version in lib/generator_tools.dart
 
 environment:
   sdk: ">=2.19.0 <4.0.0"
diff --git a/packages/pigeon/test/dart_generator_test.dart b/packages/pigeon/test/dart_generator_test.dart
index be31794..d74bdc3 100644
--- a/packages/pigeon/test/dart_generator_test.dart
+++ b/packages/pigeon/test/dart_generator_test.dart
@@ -1611,6 +1611,6 @@
     expect(
         testCode,
         contains(
-            'final Enum? arg_anEnum = args[0] == null ? null : Enum.values[args[0] as int]'));
+            'final Enum? arg_anEnum = args[0] == null ? null : Enum.values[args[0]! as int]'));
   });
 }
diff --git a/packages/pigeon/test/objc_generator_test.dart b/packages/pigeon/test/objc_generator_test.dart
index 3d30cc9..d6c1f5c 100644
--- a/packages/pigeon/test/objc_generator_test.dart
+++ b/packages/pigeon/test/objc_generator_test.dart
@@ -168,7 +168,7 @@
     expect(
         code,
         contains(
-            'pigeonResult.enum1 = [GetNullableObjectAtIndex(list, 1) integerValue];'));
+            'Enum1Box *enum1 = enum1AsNumber == nil ? nil : [[Enum1Box alloc] initWithValue: [enum1AsNumber integerValue]];'));
   });
 
   test('primitive enum host', () {
@@ -298,7 +298,8 @@
       dartPackageName: DEFAULT_PACKAGE_NAME,
     );
     final String code = sink.toString();
-    expect(code, contains('@property(nonatomic, assign) Enum1 enum1'));
+    expect(code,
+        contains('@property(nonatomic, strong, nullable) Enum1Box * enum1;'));
   });
 
   test('gen one api header', () {
diff --git a/packages/pigeon/test/pigeon_lib_test.dart b/packages/pigeon/test/pigeon_lib_test.dart
index 7e7cc21..bf6e0ba 100644
--- a/packages/pigeon/test/pigeon_lib_test.dart
+++ b/packages/pigeon/test/pigeon_lib_test.dart
@@ -765,7 +765,6 @@
   });
 
   test('enums argument flutter', () {
-    // TODO(gaaclarke): Make this not an error: https://github.com/flutter/flutter/issues/87307
     const String code = '''
 
 enum Foo {
@@ -779,12 +778,10 @@
 }
 ''';
     final ParseResults parseResult = parseSource(code);
-    expect(parseResult.errors.length, equals(1));
-    expect(parseResult.errors[0].message, contains('Enums'));
+    expect(parseResult.errors.length, equals(0));
   });
 
   test('enums list argument', () {
-    // TODO(tarrinneal): Make this not an error: https://github.com/flutter/flutter/issues/87307
     const String code = '''
 enum Foo { one, two }
 
@@ -794,12 +791,10 @@
 }
 ''';
     final ParseResults parseResult = parseSource(code);
-    expect(parseResult.errors.length, equals(1));
-    expect(parseResult.errors[0].message, contains('Enums'));
+    expect(parseResult.errors.length, equals(0));
   });
 
   test('enums map argument key', () {
-    // TODO(tarrinneal): Make this not an error: https://github.com/flutter/flutter/issues/87307
     const String code = '''
 enum Foo { one, two }
 
@@ -809,12 +804,10 @@
 }
 ''';
     final ParseResults parseResult = parseSource(code);
-    expect(parseResult.errors.length, equals(1));
-    expect(parseResult.errors[0].message, contains('Enums'));
+    expect(parseResult.errors.length, equals(0));
   });
 
   test('enums map argument value', () {
-    // TODO(tarrinneal): Make this not an error: https://github.com/flutter/flutter/issues/87307
     const String code = '''
 enum Foo { one, two }
 
@@ -824,12 +817,10 @@
 }
 ''';
     final ParseResults parseResult = parseSource(code);
-    expect(parseResult.errors.length, equals(1));
-    expect(parseResult.errors[0].message, contains('Enums'));
+    expect(parseResult.errors.length, equals(0));
   });
 
   test('enums return value', () {
-    // TODO(gaaclarke): Make this not an error: https://github.com/flutter/flutter/issues/87307
     const String code = '''
 
 enum Foo {
@@ -843,8 +834,7 @@
 }
 ''';
     final ParseResults parseResult = parseSource(code);
-    expect(parseResult.errors.length, equals(1));
-    expect(parseResult.errors[0].message, contains('Enums'));
+    expect(parseResult.errors.length, equals(0));
   });
 
   test('return type generics', () {
diff --git a/packages/pigeon/test/swift_generator_test.dart b/packages/pigeon/test/swift_generator_test.dart
index 2870f57..cd46023 100644
--- a/packages/pigeon/test/swift_generator_test.dart
+++ b/packages/pigeon/test/swift_generator_test.dart
@@ -1069,7 +1069,7 @@
     expect(
         code,
         contains(
-            'let fooArg: Int64? = args[0] is NSNull ? nil : (args[0] is Int64? ? args[0] as! Int64? : Int64(args[0] as! Int32))'));
+            'let fooArg: Int64? = isNullish(args[0]) ? nil : (args[0] is Int64? ? args[0] as! Int64? : Int64(args[0] as! Int32))'));
   });
 
   test('nullable argument flutter', () {
diff --git a/packages/pigeon/tool/shared/generation.dart b/packages/pigeon/tool/shared/generation.dart
index 3b03730..de03c3c 100644
--- a/packages/pigeon/tool/shared/generation.dart
+++ b/packages/pigeon/tool/shared/generation.dart
@@ -245,7 +245,6 @@
         'format',
         '--packages=pigeon',
       ],
-      streamOutput: false,
       workingDirectory: repositoryRoot,
       logFailure: true);
 }
diff --git a/packages/pigeon/tool/shared/test_runner.dart b/packages/pigeon/tool/shared/test_runner.dart
index 38935cf..c2df4b5 100644
--- a/packages/pigeon/tool/shared/test_runner.dart
+++ b/packages/pigeon/tool/shared/test_runner.dart
@@ -15,13 +15,14 @@
 /// them fails.
 Future<void> runTests(
   List<String> testsToRun, {
+  bool runFormat = false,
   bool runGeneration = true,
 }) async {
+  final String baseDir = p.dirname(p.dirname(Platform.script.toFilePath()));
   if (runGeneration) {
     // Pre-generate the necessary common output files.
     // TODO(stuartmorgan): Consider making this conditional on the specific
     // 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 generateTestPigeons(baseDir: baseDir);
     if (generateExitCode == 0) {
@@ -31,6 +32,16 @@
     }
   }
 
+  if (runFormat) {
+    print('Formatting generated output...');
+    final int formatExitCode =
+        await formatAllFiles(repositoryRoot: p.dirname(p.dirname(baseDir)));
+    if (formatExitCode != 0) {
+      print('Formatting failed; see above for errors.');
+      exit(formatExitCode);
+    }
+  }
+
   for (final String test in testsToRun) {
     final TestInfo? info = testSuites[test];
     if (info != null) {
diff --git a/packages/pigeon/tool/test.dart b/packages/pigeon/tool/test.dart
index 8d7e71b..83c0968 100644
--- a/packages/pigeon/tool/test.dart
+++ b/packages/pigeon/tool/test.dart
@@ -20,12 +20,15 @@
 const String _testFlag = 'test';
 const String _noGen = 'no-generation';
 const String _listFlag = 'list';
+const String _format = 'format';
 
 Future<void> main(List<String> args) async {
   final ArgParser parser = ArgParser()
     ..addMultiOption(_testFlag, abbr: 't', help: 'Only run specified tests.')
     ..addFlag(_noGen,
         abbr: 'g', help: 'Skips the generation step.', negatable: false)
+    ..addFlag(_format,
+        abbr: 'f', help: 'Formats generated test files before running tests.')
     ..addFlag(_listFlag,
         negatable: false, abbr: 'l', help: 'List available tests.')
     ..addFlag('help',
@@ -108,5 +111,9 @@
     }
   }
 
-  await runTests(testsToRun, runGeneration: !argResults.wasParsed(_noGen));
+  await runTests(
+    testsToRun,
+    runGeneration: !argResults.wasParsed(_noGen),
+    runFormat: argResults.wasParsed(_format),
+  );
 }