[pigeon] Fixes bug with mixmatch of nndb and nested types of objc. (#2157)

* [pigeon] Fixes bug with mixmatch of nndb and nested types of objc.

* created nullable variant for fromMap

* added integration test

* format

* added new generated files

* added comment

* Updated android tests.

* updated windows tests
diff --git a/packages/pigeon/CHANGELOG.md b/packages/pigeon/CHANGELOG.md
index 6532ca7..ce71e24 100644
--- a/packages/pigeon/CHANGELOG.md
+++ b/packages/pigeon/CHANGELOG.md
@@ -1,3 +1,8 @@
+## 3.1.5
+
+* Fixes potential crash bug when using a nullable nested type that has nonnull
+  fields in ObjC.
+
 ## 3.1.4
 
 * [c++] Adds support for non-nullable fields, and fixes some issues with
diff --git a/packages/pigeon/e2e_tests/test_objc/android/.project b/packages/pigeon/e2e_tests/test_objc/android/.project
index 3964dd3..a4124be 100644
--- a/packages/pigeon/e2e_tests/test_objc/android/.project
+++ b/packages/pigeon/e2e_tests/test_objc/android/.project
@@ -14,4 +14,15 @@
 	<natures>
 		<nature>org.eclipse.buildship.core.gradleprojectnature</nature>
 	</natures>
+	<filteredResources>
+		<filter>
+			<id>1654625234632</id>
+			<name></name>
+			<type>30</type>
+			<matcher>
+				<id>org.eclipse.core.resources.regexFilterMatcher</id>
+				<arguments>node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__</arguments>
+			</matcher>
+		</filter>
+	</filteredResources>
 </projectDescription>
diff --git a/packages/pigeon/e2e_tests/test_objc/android/app/.project b/packages/pigeon/e2e_tests/test_objc/android/app/.project
index ac485d7..52248e4 100644
--- a/packages/pigeon/e2e_tests/test_objc/android/app/.project
+++ b/packages/pigeon/e2e_tests/test_objc/android/app/.project
@@ -20,4 +20,15 @@
 		<nature>org.eclipse.jdt.core.javanature</nature>
 		<nature>org.eclipse.buildship.core.gradleprojectnature</nature>
 	</natures>
+	<filteredResources>
+		<filter>
+			<id>1654625234643</id>
+			<name></name>
+			<type>30</type>
+			<matcher>
+				<id>org.eclipse.core.resources.regexFilterMatcher</id>
+				<arguments>node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__</arguments>
+			</matcher>
+		</filter>
+	</filteredResources>
 </projectDescription>
diff --git a/packages/pigeon/lib/generator_tools.dart b/packages/pigeon/lib/generator_tools.dart
index 17f55b4..dd6295b 100644
--- a/packages/pigeon/lib/generator_tools.dart
+++ b/packages/pigeon/lib/generator_tools.dart
@@ -9,7 +9,7 @@
 import 'ast.dart';
 
 /// The current version of pigeon. This must match the version in pubspec.yaml.
-const String pigeonVersion = '3.1.4';
+const String pigeonVersion = '3.1.5';
 
 /// Read all the content from [stdin] to a String.
 String readStdin() {
diff --git a/packages/pigeon/lib/objc_generator.dart b/packages/pigeon/lib/objc_generator.dart
index 56246b0..dc1661b 100644
--- a/packages/pigeon/lib/objc_generator.dart
+++ b/packages/pigeon/lib/objc_generator.dart
@@ -568,7 +568,7 @@
     if (prefix != null) {
       className = '$prefix$className';
     }
-    return '[$className fromMap:GetNullableObject($dict, @"${field.name}")]';
+    return '[$className nullableFromMap:GetNullableObject($dict, @"${field.name}")]';
   } else {
     return 'GetNullableObject($dict, @"${field.name}")';
   }
@@ -880,6 +880,8 @@
     final String className = _className(options.prefix, klass.name);
     indent.writeln('@interface $className ()');
     indent.writeln('+ ($className *)fromMap:(NSDictionary *)dict;');
+    indent.writeln(
+        '+ (nullable $className *)nullableFromMap:(NSDictionary *)dict;');
     indent.writeln('- (NSDictionary *)toMap;');
     indent.writeln('@end');
   }
@@ -919,6 +921,8 @@
         }
         indent.writeln('return $resultName;');
       });
+      indent.writeln(
+          '+ (nullable $className *)nullableFromMap:(NSDictionary *)dict { return (dict) ? [$className fromMap:dict] : nil; }');
     }
 
     void writeToMap() {
diff --git a/packages/pigeon/pigeons/null_fields.dart b/packages/pigeon/pigeons/null_fields.dart
index 574b4f6..f9a9804 100644
--- a/packages/pigeon/pigeons/null_fields.dart
+++ b/packages/pigeon/pigeons/null_fields.dart
@@ -8,8 +8,11 @@
 import 'package:pigeon/pigeon.dart';
 
 class NullFieldsSearchRequest {
-  NullFieldsSearchRequest(this.query);
+  NullFieldsSearchRequest(this.query, this.identifier);
   String? query;
+  // The following non-null field was added to reproduce
+  // https://github.com/flutter/flutter/issues/104871
+  int identifier;
 }
 
 enum NullFieldsSearchReplyType {
diff --git a/packages/pigeon/platform_tests/android_unit_tests/android/app/src/test/java/com/example/android_unit_tests/NullFieldsTest.java b/packages/pigeon/platform_tests/android_unit_tests/android/app/src/test/java/com/example/android_unit_tests/NullFieldsTest.java
index 1b88470..ffa97fd 100644
--- a/packages/pigeon/platform_tests/android_unit_tests/android/app/src/test/java/com/example/android_unit_tests/NullFieldsTest.java
+++ b/packages/pigeon/platform_tests/android_unit_tests/android/app/src/test/java/com/example/android_unit_tests/NullFieldsTest.java
@@ -16,7 +16,10 @@
   @Test
   public void builderWithValues() {
     NullFields.NullFieldsSearchRequest request =
-        new NullFields.NullFieldsSearchRequest.Builder().setQuery("hello").build();
+        new NullFields.NullFieldsSearchRequest.Builder()
+            .setQuery("hello")
+            .setIdentifier(1L)
+            .build();
 
     NullFields.NullFieldsSearchReply reply =
         new NullFields.NullFieldsSearchReply.Builder()
@@ -37,7 +40,7 @@
   @Test
   public void builderRequestWithNulls() {
     NullFields.NullFieldsSearchRequest request =
-        new NullFields.NullFieldsSearchRequest.Builder().setQuery(null).build();
+        new NullFields.NullFieldsSearchRequest.Builder().setQuery(null).setIdentifier(1L).build();
   }
 
   @Test
@@ -62,6 +65,7 @@
   public void requestFromMapWithValues() {
     HashMap<String, Object> map = new HashMap<>();
     map.put("query", "hello");
+    map.put("identifier", 1L);
 
     NullFields.NullFieldsSearchRequest request = NullFields.NullFieldsSearchRequest.fromMap(map);
     assertEquals(request.getQuery(), "hello");
@@ -71,6 +75,7 @@
   public void requestFromMapWithNulls() {
     HashMap<String, Object> map = new HashMap<>();
     map.put("query", null);
+    map.put("identifier", 1L);
 
     NullFields.NullFieldsSearchRequest request = NullFields.NullFieldsSearchRequest.fromMap(map);
     assertNull(request.getQuery());
@@ -80,6 +85,7 @@
   public void replyFromMapWithValues() {
     HashMap<String, Object> requestMap = new HashMap<>();
     requestMap.put("query", "hello");
+    requestMap.put("identifier", 1L);
 
     HashMap<String, Object> map = new HashMap<>();
     map.put("result", "result");
@@ -116,7 +122,10 @@
   @Test
   public void requestToMapWithValues() {
     NullFields.NullFieldsSearchRequest request =
-        new NullFields.NullFieldsSearchRequest.Builder().setQuery("hello").build();
+        new NullFields.NullFieldsSearchRequest.Builder()
+            .setQuery("hello")
+            .setIdentifier(1L)
+            .build();
 
     Map<String, Object> map = request.toMap();
     assertEquals(map.get("query"), "hello");
@@ -125,7 +134,7 @@
   @Test
   public void requestToMapWithNulls() {
     NullFields.NullFieldsSearchRequest request =
-        new NullFields.NullFieldsSearchRequest.Builder().setQuery(null).build();
+        new NullFields.NullFieldsSearchRequest.Builder().setQuery(null).setIdentifier(1L).build();
 
     Map<String, Object> map = request.toMap();
     assertNull(map.get("query"));
@@ -138,7 +147,11 @@
             .setResult("result")
             .setError("error")
             .setIndices(Arrays.asList(1L, 2L, 3L))
-            .setRequest(new NullFields.NullFieldsSearchRequest.Builder().setQuery("hello").build())
+            .setRequest(
+                new NullFields.NullFieldsSearchRequest.Builder()
+                    .setQuery("hello")
+                    .setIdentifier(1L)
+                    .build())
             .setType(NullFields.NullFieldsSearchReplyType.success)
             .build();
 
diff --git a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/all_datatypes.dart b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/all_datatypes.dart
index bf19653..4b48543 100644
--- a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/all_datatypes.dart
+++ b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/all_datatypes.dart
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
-// Autogenerated from Pigeon (v2.1.0), do not edit directly.
+// Autogenerated from Pigeon (v3.1.4), do not edit directly.
 // See also: https://pub.dev/packages/pigeon
 // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name
 // @dart = 2.12
diff --git a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/multiple_arity.gen.dart b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/multiple_arity.gen.dart
index 9f0ce12..c2856cd 100644
--- a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/multiple_arity.gen.dart
+++ b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/multiple_arity.gen.dart
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
-// Autogenerated from Pigeon (v2.1.0), do not edit directly.
+// Autogenerated from Pigeon (v3.1.4), do not edit directly.
 // See also: https://pub.dev/packages/pigeon
 // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name
 // @dart = 2.12
diff --git a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/non_null_fields.gen.dart b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/non_null_fields.gen.dart
index 9791455..725a5b3 100644
--- a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/non_null_fields.gen.dart
+++ b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/non_null_fields.gen.dart
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
-// Autogenerated from Pigeon (v3.1.0), do not edit directly.
+// Autogenerated from Pigeon (v3.1.4), do not edit directly.
 // See also: https://pub.dev/packages/pigeon
 // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name
 // @dart = 2.12
diff --git a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/null_fields.gen.dart b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/null_fields.gen.dart
index dd7c7c9..5511e41 100644
--- a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/null_fields.gen.dart
+++ b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/null_fields.gen.dart
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
-// Autogenerated from Pigeon (v2.1.0), do not edit directly.
+// Autogenerated from Pigeon (v3.1.4), do not edit directly.
 // See also: https://pub.dev/packages/pigeon
 // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name
 // @dart = 2.12
@@ -20,13 +20,16 @@
 class NullFieldsSearchRequest {
   NullFieldsSearchRequest({
     this.query,
+    required this.identifier,
   });
 
   String? query;
+  int identifier;
 
   Object encode() {
     final Map<Object?, Object?> pigeonMap = <Object?, Object?>{};
     pigeonMap['query'] = query;
+    pigeonMap['identifier'] = identifier;
     return pigeonMap;
   }
 
@@ -34,6 +37,7 @@
     final Map<Object?, Object?> pigeonMap = message as Map<Object?, Object?>;
     return NullFieldsSearchRequest(
       query: pigeonMap['query'] as String?,
+      identifier: pigeonMap['identifier']! as int,
     );
   }
 }
@@ -58,8 +62,8 @@
     pigeonMap['result'] = result;
     pigeonMap['error'] = error;
     pigeonMap['indices'] = indices;
-    pigeonMap['request'] = request == null ? null : request!.encode();
-    pigeonMap['type'] = type == null ? null : type!.index;
+    pigeonMap['request'] = request?.encode();
+    pigeonMap['type'] = type?.index;
     return pigeonMap;
   }
 
diff --git a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/null_safe_pigeon.dart b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/null_safe_pigeon.dart
index a1f8030..676d889 100644
--- a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/null_safe_pigeon.dart
+++ b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/null_safe_pigeon.dart
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
-// Autogenerated from Pigeon (v2.1.0), do not edit directly.
+// Autogenerated from Pigeon (v3.1.4), do not edit directly.
 // See also: https://pub.dev/packages/pigeon
 // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name
 // @dart = 2.12
diff --git a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/nullable_returns.gen.dart b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/nullable_returns.gen.dart
index 757d4a9..3c8e836 100644
--- a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/nullable_returns.gen.dart
+++ b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/nullable_returns.gen.dart
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
-// Autogenerated from Pigeon (v3.1.0), do not edit directly.
+// Autogenerated from Pigeon (v3.1.4), do not edit directly.
 // See also: https://pub.dev/packages/pigeon
 // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name
 // @dart = 2.12
diff --git a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/primitive.dart b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/primitive.dart
index b5da7e5..3454087 100644
--- a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/primitive.dart
+++ b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/primitive.dart
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
-// Autogenerated from Pigeon (v2.1.0), do not edit directly.
+// Autogenerated from Pigeon (v3.1.4), do not edit directly.
 // See also: https://pub.dev/packages/pigeon
 // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name
 // @dart = 2.12
diff --git a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/null_fields_test.dart b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/null_fields_test.dart
index fe030a3..11eddac 100644
--- a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/null_fields_test.dart
+++ b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/null_fields_test.dart
@@ -8,7 +8,7 @@
 void main() {
   test('test constructor with values', () {
     final NullFieldsSearchRequest request =
-        NullFieldsSearchRequest(query: 'query');
+        NullFieldsSearchRequest(query: 'query', identifier: 1);
 
     final NullFieldsSearchReply reply = NullFieldsSearchReply(
       result: 'result',
@@ -27,7 +27,7 @@
 
   test('test request constructor with nulls', () {
     final NullFieldsSearchRequest request =
-        NullFieldsSearchRequest(query: null);
+        NullFieldsSearchRequest(query: null, identifier: 1);
 
     expect(request.query, isNull);
   });
@@ -52,6 +52,7 @@
     final NullFieldsSearchRequest request =
         NullFieldsSearchRequest.decode(<String, dynamic>{
       'query': 'query',
+      'identifier': 1,
     });
 
     expect(request.query, 'query');
@@ -61,6 +62,7 @@
     final NullFieldsSearchRequest request =
         NullFieldsSearchRequest.decode(<String, dynamic>{
       'query': null,
+      'identifier': 1,
     });
 
     expect(request.query, isNull);
@@ -74,6 +76,7 @@
       'indices': <int>[1, 2, 3],
       'request': <String, dynamic>{
         'query': 'query',
+        'identifier': 1,
       },
       'type': NullFieldsSearchReplyType.success.index,
     });
@@ -104,19 +107,21 @@
 
   test('test request encode with values', () {
     final NullFieldsSearchRequest request =
-        NullFieldsSearchRequest(query: 'query');
+        NullFieldsSearchRequest(query: 'query', identifier: 1);
 
     expect(request.encode(), <String, dynamic>{
       'query': 'query',
+      'identifier': 1,
     });
   });
 
   test('test request encode with null', () {
     final NullFieldsSearchRequest request =
-        NullFieldsSearchRequest(query: null);
+        NullFieldsSearchRequest(query: null, identifier: 1);
 
     expect(request.encode(), <String, dynamic>{
       'query': null,
+      'identifier': 1,
     });
   });
 
@@ -125,7 +130,7 @@
       result: 'result',
       error: 'error',
       indices: <int>[1, 2, 3],
-      request: NullFieldsSearchRequest(query: 'query'),
+      request: NullFieldsSearchRequest(query: 'query', identifier: 1),
       type: NullFieldsSearchReplyType.success,
     );
 
@@ -135,6 +140,7 @@
       'indices': <int>[1, 2, 3],
       'request': <String, dynamic>{
         'query': 'query',
+        'identifier': 1,
       },
       'type': NullFieldsSearchReplyType.success.index,
     });
diff --git a/packages/pigeon/platform_tests/ios_unit_tests/ios/RunnerTests/NullFieldsTest.m b/packages/pigeon/platform_tests/ios_unit_tests/ios/RunnerTests/NullFieldsTest.m
index 7c5d25c..a7e1ac6 100644
--- a/packages/pigeon/platform_tests/ios_unit_tests/ios/RunnerTests/NullFieldsTest.m
+++ b/packages/pigeon/platform_tests/ios_unit_tests/ios/RunnerTests/NullFieldsTest.m
@@ -27,7 +27,7 @@
 @implementation NullFieldsTest
 
 - (void)testMakeWithValues {
-  NullFieldsSearchRequest *request = [NullFieldsSearchRequest makeWithQuery:@"hello"];
+  NullFieldsSearchRequest *request = [NullFieldsSearchRequest makeWithQuery:@"hello" identifier:@1];
 
   NullFieldsSearchReply *reply =
       [NullFieldsSearchReply makeWithResult:@"result"
@@ -45,7 +45,7 @@
 }
 
 - (void)testMakeRequestWithNulls {
-  NullFieldsSearchRequest *request = [NullFieldsSearchRequest makeWithQuery:nil];
+  NullFieldsSearchRequest *request = [NullFieldsSearchRequest makeWithQuery:nil identifier:@1];
   XCTAssertNil(request.query);
 }
 
@@ -66,6 +66,7 @@
 - (void)testRequestFromMapWithValues {
   NSDictionary *map = @{
     @"query" : @"hello",
+    @"identifier" : @1,
   };
   NullFieldsSearchRequest *request = [NullFieldsSearchRequest fromMap:map];
   XCTAssertEqualObjects(@"hello", request.query);
@@ -74,6 +75,7 @@
 - (void)testRequestFromMapWithNulls {
   NSDictionary *map = @{
     @"query" : [NSNull null],
+    @"identifier" : @1,
   };
   NullFieldsSearchRequest *request = [NullFieldsSearchRequest fromMap:map];
   XCTAssertNil(request.query);
@@ -86,6 +88,7 @@
     @"indices" : @[ @1, @2, @3 ],
     @"request" : @{
       @"query" : @"hello",
+      @"identifier" : @1,
     },
     @"type" : @0,
   };
@@ -116,24 +119,24 @@
 }
 
 - (void)testRequestToMapWithValuess {
-  NullFieldsSearchRequest *request = [NullFieldsSearchRequest makeWithQuery:@"hello"];
+  NullFieldsSearchRequest *request = [NullFieldsSearchRequest makeWithQuery:@"hello" identifier:@1];
   NSDictionary *dict = [request toMap];
   XCTAssertEqual(@"hello", dict[@"query"]);
 }
 
 - (void)testRequestToMapWithNulls {
-  NullFieldsSearchRequest *request = [NullFieldsSearchRequest makeWithQuery:nil];
+  NullFieldsSearchRequest *request = [NullFieldsSearchRequest makeWithQuery:nil identifier:@1];
   NSDictionary *dict = [request toMap];
   XCTAssertEqual([NSNull null], dict[@"query"]);
 }
 
 - (void)testReplyToMapWithValuess {
-  NullFieldsSearchReply *reply =
-      [NullFieldsSearchReply makeWithResult:@"result"
-                                      error:@"error"
-                                    indices:@[ @1, @2, @3 ]
-                                    request:[NullFieldsSearchRequest makeWithQuery:@"hello"]
-                                       type:NullFieldsSearchReplyTypeSuccess];
+  NullFieldsSearchReply *reply = [NullFieldsSearchReply
+      makeWithResult:@"result"
+               error:@"error"
+             indices:@[ @1, @2, @3 ]
+             request:[NullFieldsSearchRequest makeWithQuery:@"hello" identifier:@1]
+                type:NullFieldsSearchReplyTypeSuccess];
   NSDictionary *dict = [reply toMap];
   NSArray *indices = @[ @1, @2, @3 ];
   XCTAssertEqualObjects(@"result", dict[@"result"]);
diff --git a/packages/pigeon/platform_tests/windows_unit_tests/windows/test/null_fields_test.cpp b/packages/pigeon/platform_tests/windows_unit_tests/windows/test/null_fields_test.cpp
index 87d1f67..1bb5953 100644
--- a/packages/pigeon/platform_tests/windows_unit_tests/windows/test/null_fields_test.cpp
+++ b/packages/pigeon/platform_tests/windows_unit_tests/windows/test/null_fields_test.cpp
@@ -92,19 +92,23 @@
 TEST_F(NullFieldsTest, RequestFromMapWithValues) {
   EncodableMap map{
       {EncodableValue("query"), EncodableValue("hello")},
+      {EncodableValue("identifier"), EncodableValue(1)},
   };
   NullFieldsSearchRequest request = RequestFromMap(map);
 
   EXPECT_EQ(*request.query(), "hello");
+  EXPECT_EQ(request.identifier(), 1);
 }
 
 TEST_F(NullFieldsTest, RequestFromMapWithNulls) {
   EncodableMap map{
       {EncodableValue("query"), EncodableValue()},
+      {EncodableValue("identifier"), EncodableValue(1)},
   };
   NullFieldsSearchRequest request = RequestFromMap(map);
 
   EXPECT_EQ(request.query(), nullptr);
+  EXPECT_EQ(request.identifier(), 1);
 }
 
 TEST_F(NullFieldsTest, ReplyFromMapWithValues) {
@@ -119,6 +123,7 @@
       {EncodableValue("request"),
        EncodableValue(EncodableMap{
            {EncodableValue("query"), EncodableValue("hello")},
+           {EncodableValue("identifier"), EncodableValue(1)},
        })},
       {EncodableValue("type"), EncodableValue(0)},
   };
@@ -128,6 +133,7 @@
   EXPECT_EQ(*reply.error(), "error");
   EXPECT_EQ(reply.indices()->size(), 3);
   EXPECT_EQ(*reply.request()->query(), "hello");
+  EXPECT_EQ(reply.request()->identifier(), 1);
   EXPECT_EQ(*reply.type(), NullFieldsSearchReplyType::success);
 }
 
@@ -151,20 +157,25 @@
 TEST_F(NullFieldsTest, RequestToMapWithValues) {
   NullFieldsSearchRequest request;
   request.set_query("hello");
+  request.set_identifier(1);
 
   EncodableMap map = MapFromRequest(request);
 
-  EXPECT_EQ(map.size(), 1);
+  EXPECT_EQ(map.size(), 2);
   EXPECT_EQ(*ExpectAndGet<std::string>(map, "query"), "hello");
+  EXPECT_EQ(*ExpectAndGet<int64_t>(map, "identifier"), 1);
 }
 
 TEST_F(NullFieldsTest, RequestToMapWithNulls) {
   NullFieldsSearchRequest request;
+  // TODO(gaaclarke): This needs a way to be enforced.
+  request.set_identifier(1);
 
   EncodableMap map = MapFromRequest(request);
 
-  EXPECT_EQ(map.size(), 1);
+  EXPECT_EQ(map.size(), 2);
   EXPECT_TRUE(map[EncodableValue("hello")].IsNull());
+  EXPECT_EQ(*ExpectAndGet<int64_t>(map, "identifier"), 1);
 }
 
 TEST_F(NullFieldsTest, ReplyToMapWithValues) {
diff --git a/packages/pigeon/pubspec.yaml b/packages/pigeon/pubspec.yaml
index 24c9a63..cafee49 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: 3.1.4 # This must match the version in lib/generator_tools.dart
+version: 3.1.5 # This must match the version in lib/generator_tools.dart
 
 environment:
   sdk: ">=2.12.0 <3.0.0"
diff --git a/packages/pigeon/test/objc_generator_test.dart b/packages/pigeon/test/objc_generator_test.dart
index 28e463e..58e6301 100644
--- a/packages/pigeon/test/objc_generator_test.dart
+++ b/packages/pigeon/test/objc_generator_test.dart
@@ -415,7 +415,7 @@
     expect(
         code,
         contains(
-            'pigeonResult.nested = [Input fromMap:GetNullableObject(dict, @"nested")];'));
+            'pigeonResult.nested = [Input nullableFromMap:GetNullableObject(dict, @"nested")];'));
     expect(code, matches('[self.nested toMap].*@"nested"'));
   });