[pigeon] Fixes bug where Dart `FlutterApi`s would assert that a nullable argument was nonnull (#1515)

diff --git a/analysis_options.yaml b/analysis_options.yaml
index 21a3c26..a6b55d7 100644
--- a/analysis_options.yaml
+++ b/analysis_options.yaml
@@ -22,6 +22,9 @@
     todo: ignore
     # Turned off until null-safe rollout is complete.
     unnecessary_null_comparison: ignore
+  exclude:
+    # Ignore generated files
+    - '**/*.mocks.dart' # Mockito @GenerateMocks
 
 linter:
   rules:
diff --git a/packages/pigeon/CHANGELOG.md b/packages/pigeon/CHANGELOG.md
index 915f0db..a53b3b7 100644
--- a/packages/pigeon/CHANGELOG.md
+++ b/packages/pigeon/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 2.0.4
+
+* Fixes bug where Dart `FlutterApi`s would assert that a nullable argument was nonnull.
+
 ## 2.0.3
 
 * Makes the generated Java Builder class final.
diff --git a/packages/pigeon/lib/dart_generator.dart b/packages/pigeon/lib/dart_generator.dart
index 95ecbcb..0325c29 100644
--- a/packages/pigeon/lib/dart_generator.dart
+++ b/packages/pigeon/lib/dart_generator.dart
@@ -338,13 +338,17 @@
 
                   indent.writeln(
                       'final $argType$nullTag $argName = ($argsArray[$count] as $genericArgType$nullTag)${castCall.isEmpty ? '' : '$nullTag$castCall'};');
-                  indent.writeln(
-                      'assert($argName != null, \'Argument for $channelName was null, expected non-null $argType.\');');
+                  if (!arg.type.isNullable) {
+                    indent.writeln(
+                        'assert($argName != null, \'Argument for $channelName was null, expected non-null $argType.\');');
+                  }
                 });
                 final Iterable<String> argNames =
-                    indexMap(func.arguments, argNameFunc);
-                call =
-                    'api.${func.name}(${argNames.map<String>((String x) => '$x$unwrapOperator').join(', ')})';
+                    indexMap(func.arguments, (int index, NamedType field) {
+                  final String name = _getSafeArgumentName(index, field);
+                  return '$name${field.type.isNullable ? '' : unwrapOperator}';
+                });
+                call = 'api.${func.name}(${argNames.join(', ')})';
               }
               if (func.returnType.isVoid) {
                 if (isAsync) {
diff --git a/packages/pigeon/lib/generator_tools.dart b/packages/pigeon/lib/generator_tools.dart
index 0a9be43..f32f202 100644
--- a/packages/pigeon/lib/generator_tools.dart
+++ b/packages/pigeon/lib/generator_tools.dart
@@ -8,7 +8,7 @@
 import 'ast.dart';
 
 /// The current version of pigeon. This must match the version in pubspec.yaml.
-const String pigeonVersion = '2.0.3';
+const String pigeonVersion = '2.0.4';
 
 /// Read all the content from [stdin] to a String.
 String readStdin() {
diff --git a/packages/pigeon/mock_handler_tester/test/message.dart b/packages/pigeon/mock_handler_tester/test/message.dart
index 9ce127d..bade840 100644
--- a/packages/pigeon/mock_handler_tester/test/message.dart
+++ b/packages/pigeon/mock_handler_tester/test/message.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 (v0.3.0), do not edit directly.
+// Autogenerated from Pigeon (v2.0.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
@@ -19,6 +19,12 @@
 }
 
 class SearchRequest {
+  SearchRequest({
+    this.query,
+    this.anInt,
+    this.aBool,
+  });
+
   String? query;
   int? anInt;
   bool? aBool;
@@ -33,14 +39,21 @@
 
   static SearchRequest decode(Object message) {
     final Map<Object?, Object?> pigeonMap = message as Map<Object?, Object?>;
-    return SearchRequest()
-      ..query = pigeonMap['query'] as String?
-      ..anInt = pigeonMap['anInt'] as int?
-      ..aBool = pigeonMap['aBool'] as bool?;
+    return SearchRequest(
+      query: pigeonMap['query'] as String?,
+      anInt: pigeonMap['anInt'] as int?,
+      aBool: pigeonMap['aBool'] as bool?,
+    );
   }
 }
 
 class SearchReply {
+  SearchReply({
+    this.result,
+    this.error,
+    this.state,
+  });
+
   String? result;
   String? error;
   RequestState? state;
@@ -55,16 +68,21 @@
 
   static SearchReply decode(Object message) {
     final Map<Object?, Object?> pigeonMap = message as Map<Object?, Object?>;
-    return SearchReply()
-      ..result = pigeonMap['result'] as String?
-      ..error = pigeonMap['error'] as String?
-      ..state = pigeonMap['state'] != null
+    return SearchReply(
+      result: pigeonMap['result'] as String?,
+      error: pigeonMap['error'] as String?,
+      state: pigeonMap['state'] != null
           ? RequestState.values[pigeonMap['state']! as int]
-          : null;
+          : null,
+    );
   }
 }
 
 class Nested {
+  Nested({
+    this.request,
+  });
+
   SearchRequest? request;
 
   Object encode() {
@@ -75,10 +93,11 @@
 
   static Nested decode(Object message) {
     final Map<Object?, Object?> pigeonMap = message as Map<Object?, Object?>;
-    return Nested()
-      ..request = pigeonMap['request'] != null
+    return Nested(
+      request: pigeonMap['request'] != null
           ? SearchRequest.decode(pigeonMap['request']!)
-          : null;
+          : null,
+    );
   }
 }
 
@@ -132,7 +151,6 @@
       throw PlatformException(
         code: 'channel-error',
         message: 'Unable to establish connection on channel.',
-        details: null,
       );
     } else if (replyMap['error'] != null) {
       final Map<Object?, Object?> error =
@@ -152,12 +170,11 @@
         'dev.flutter.pigeon.Api.search', codec,
         binaryMessenger: _binaryMessenger);
     final Map<Object?, Object?>? replyMap =
-        await channel.send(<Object>[arg_request]) as Map<Object?, Object?>?;
+        await channel.send(<Object?>[arg_request]) as Map<Object?, Object?>?;
     if (replyMap == null) {
       throw PlatformException(
         code: 'channel-error',
         message: 'Unable to establish connection on channel.',
-        details: null,
       );
     } else if (replyMap['error'] != null) {
       final Map<Object?, Object?> error =
@@ -167,6 +184,11 @@
         message: error['message'] as String?,
         details: error['details'],
       );
+    } else if (replyMap['result'] == null) {
+      throw PlatformException(
+        code: 'null-error',
+        message: 'Host platform returned null value for non-null return value.',
+      );
     } else {
       return (replyMap['result'] as SearchReply?)!;
     }
@@ -183,6 +205,9 @@
     } else if (value is SearchReply) {
       buffer.putUint8(129);
       writeValue(buffer, value.encode());
+    } else if (value is SearchRequest) {
+      buffer.putUint8(130);
+      writeValue(buffer, value.encode());
     } else {
       super.writeValue(buffer, value);
     }
@@ -197,6 +222,9 @@
       case 129:
         return SearchReply.decode(readValue(buffer)!);
 
+      case 130:
+        return SearchRequest.decode(readValue(buffer)!);
+
       default:
         return super.readValueOfType(type, buffer);
     }
@@ -219,12 +247,11 @@
         'dev.flutter.pigeon.NestedApi.search', codec,
         binaryMessenger: _binaryMessenger);
     final Map<Object?, Object?>? replyMap =
-        await channel.send(<Object>[arg_nested]) as Map<Object?, Object?>?;
+        await channel.send(<Object?>[arg_nested]) as Map<Object?, Object?>?;
     if (replyMap == null) {
       throw PlatformException(
         code: 'channel-error',
         message: 'Unable to establish connection on channel.',
-        details: null,
       );
     } else if (replyMap['error'] != null) {
       final Map<Object?, Object?> error =
@@ -234,6 +261,11 @@
         message: error['message'] as String?,
         details: error['details'],
       );
+    } else if (replyMap['result'] == null) {
+      throw PlatformException(
+        code: 'null-error',
+        message: 'Host platform returned null value for non-null return value.',
+      );
     } else {
       return (replyMap['result'] as SearchReply?)!;
     }
@@ -274,10 +306,11 @@
   static const MessageCodec<Object?> codec = _FlutterSearchApiCodec();
 
   SearchReply search(SearchRequest request);
-  static void setup(FlutterSearchApi? api) {
+  static void setup(FlutterSearchApi? api, {BinaryMessenger? binaryMessenger}) {
     {
-      const BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
-          'dev.flutter.pigeon.FlutterSearchApi.search', codec);
+      final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+          'dev.flutter.pigeon.FlutterSearchApi.search', codec,
+          binaryMessenger: binaryMessenger);
       if (api == null) {
         channel.setMessageHandler(null);
       } else {
@@ -285,7 +318,7 @@
           assert(message != null,
               'Argument for dev.flutter.pigeon.FlutterSearchApi.search was null.');
           final List<Object?> args = (message as List<Object?>?)!;
-          final SearchRequest? arg_request = args[0] as SearchRequest?;
+          final SearchRequest? arg_request = (args[0] as SearchRequest?);
           assert(arg_request != null,
               'Argument for dev.flutter.pigeon.FlutterSearchApi.search was null, expected non-null SearchRequest.');
           final SearchReply output = api.search(arg_request!);
diff --git a/packages/pigeon/mock_handler_tester/test/test.dart b/packages/pigeon/mock_handler_tester/test/test.dart
index 808044a..2abd3a6 100644
--- a/packages/pigeon/mock_handler_tester/test/test.dart
+++ b/packages/pigeon/mock_handler_tester/test/test.dart
@@ -2,9 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
-// Autogenerated from Pigeon (v0.3.0), do not edit directly.
+// Autogenerated from Pigeon (v2.0.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
+// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis
+// ignore_for_file: avoid_relative_lib_imports
 // @dart = 2.12
 import 'dart:async';
 import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List;
@@ -49,10 +50,11 @@
 
   void initialize();
   SearchReply search(SearchRequest request);
-  static void setup(TestHostApi? api) {
+  static void setup(TestHostApi? api, {BinaryMessenger? binaryMessenger}) {
     {
-      const BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
-          'dev.flutter.pigeon.Api.initialize', codec);
+      final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+          'dev.flutter.pigeon.Api.initialize', codec,
+          binaryMessenger: binaryMessenger);
       if (api == null) {
         channel.setMockMessageHandler(null);
       } else {
@@ -64,8 +66,9 @@
       }
     }
     {
-      const BasicMessageChannel<Object?> channel =
-          BasicMessageChannel<Object?>('dev.flutter.pigeon.Api.search', codec);
+      final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+          'dev.flutter.pigeon.Api.search', codec,
+          binaryMessenger: binaryMessenger);
       if (api == null) {
         channel.setMockMessageHandler(null);
       } else {
@@ -73,7 +76,7 @@
           assert(message != null,
               'Argument for dev.flutter.pigeon.Api.search was null.');
           final List<Object?> args = (message as List<Object?>?)!;
-          final SearchRequest? arg_request = args[0] as SearchRequest?;
+          final SearchRequest? arg_request = (args[0] as SearchRequest?);
           assert(arg_request != null,
               'Argument for dev.flutter.pigeon.Api.search was null, expected non-null SearchRequest.');
           final SearchReply output = api.search(arg_request!);
@@ -94,6 +97,9 @@
     } else if (value is SearchReply) {
       buffer.putUint8(129);
       writeValue(buffer, value.encode());
+    } else if (value is SearchRequest) {
+      buffer.putUint8(130);
+      writeValue(buffer, value.encode());
     } else {
       super.writeValue(buffer, value);
     }
@@ -108,6 +114,9 @@
       case 129:
         return SearchReply.decode(readValue(buffer)!);
 
+      case 130:
+        return SearchRequest.decode(readValue(buffer)!);
+
       default:
         return super.readValueOfType(type, buffer);
     }
@@ -118,10 +127,11 @@
   static const MessageCodec<Object?> codec = _TestNestedApiCodec();
 
   SearchReply search(Nested nested);
-  static void setup(TestNestedApi? api) {
+  static void setup(TestNestedApi? api, {BinaryMessenger? binaryMessenger}) {
     {
-      const BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
-          'dev.flutter.pigeon.NestedApi.search', codec);
+      final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+          'dev.flutter.pigeon.NestedApi.search', codec,
+          binaryMessenger: binaryMessenger);
       if (api == null) {
         channel.setMockMessageHandler(null);
       } else {
@@ -129,7 +139,7 @@
           assert(message != null,
               'Argument for dev.flutter.pigeon.NestedApi.search was null.');
           final List<Object?> args = (message as List<Object?>?)!;
-          final Nested? arg_nested = args[0] as Nested?;
+          final Nested? arg_nested = (args[0] as Nested?);
           assert(arg_nested != null,
               'Argument for dev.flutter.pigeon.NestedApi.search was null, expected non-null Nested.');
           final SearchReply output = api.search(arg_nested!);
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 ae7444f..4a1e2a0 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 (v1.0.19), do not edit directly.
+// Autogenerated from Pigeon (v2.0.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 e5926f0..7f7aa0d 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 (v1.0.19), do not edit directly.
+// Autogenerated from Pigeon (v2.0.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 a06d692..9dbcebe 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 (v1.0.19), do not edit directly.
+// Autogenerated from Pigeon (v2.0.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 9f332d2..4ac7d91 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.0.2), do not edit directly.
+// Autogenerated from Pigeon (v2.0.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_safe_pigeon.dart b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/null_safe_pigeon.dart
index 994ac01..2ed1294 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 (v1.0.19), do not edit directly.
+// Autogenerated from Pigeon (v2.0.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 3766d06..cf07297 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 (v1.0.19), do not edit directly.
+// Autogenerated from Pigeon (v2.0.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
@@ -146,9 +146,7 @@
               'Argument for dev.flutter.pigeon.NullableArgFlutterApi.doit was null.');
           final List<Object?> args = (message as List<Object?>?)!;
           final int? arg_x = (args[0] as int?);
-          assert(arg_x != null,
-              'Argument for dev.flutter.pigeon.NullableArgFlutterApi.doit was null, expected non-null int.');
-          final int output = api.doit(arg_x!);
+          final int output = api.doit(arg_x);
           return output;
         });
       }
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 bd94b3c..c398ef2 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 (v1.0.19), do not edit directly.
+// Autogenerated from Pigeon (v2.0.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/all_datatypes_test.mocks.dart b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/all_datatypes_test.mocks.dart
index 0690c48..1a5de52 100644
--- a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/all_datatypes_test.mocks.dart
+++ b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/all_datatypes_test.mocks.dart
@@ -11,8 +11,7 @@
 
 // ignore_for_file: comment_references
 // ignore_for_file: unnecessary_parenthesis
-// ignore_for_file: always_specify_types
-// ignore_for_file: implicit_dynamic_type
+
 // ignore_for_file: prefer_const_constructors
 
 // ignore_for_file: avoid_redundant_argument_values
diff --git a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/multiple_arity_test.mocks.dart b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/multiple_arity_test.mocks.dart
index 814ea41..ff0400d 100644
--- a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/multiple_arity_test.mocks.dart
+++ b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/multiple_arity_test.mocks.dart
@@ -1,5 +1,5 @@
 // Mocks generated by Mockito 5.0.7 from annotations
-// in flutter_unit_tests/test/null_safe_test.dart.
+// in flutter_unit_tests/test/multiple_arity_test.dart.
 // Do not manually edit this file.
 
 import 'dart:async' as _i3;
@@ -11,11 +11,10 @@
 
 // ignore_for_file: comment_references
 // ignore_for_file: unnecessary_parenthesis
-// ignore_for_file: always_specify_types
-// Added manually; several methods have moved to
-// flutter_test/lib/src/deprecated.dart on master, but that hasn't reached
-// stable yet.
-// ignore_for_file: override_on_non_overriding_member
+
+// ignore_for_file: prefer_const_constructors
+
+// ignore_for_file: avoid_redundant_argument_values
 
 /// A class which mocks [BinaryMessenger].
 ///
@@ -31,8 +30,7 @@
       (super.noSuchMethod(
           Invocation.method(#handlePlatformMessage, [channel, data, callback]),
           returnValue: Future<void>.value(null),
-          returnValueForMissingStub:
-              Future<dynamic>.value()) as _i3.Future<void>);
+          returnValueForMissingStub: Future.value()) as _i3.Future<void>);
   @override
   _i3.Future<_i4.ByteData?>? send(String? channel, _i4.ByteData? message) =>
       (super.noSuchMethod(Invocation.method(#send, [channel, message]))
diff --git a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/null_safe_test.dart b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/null_safe_test.dart
index ca4479e..9c0ab20 100644
--- a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/null_safe_test.dart
+++ b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/null_safe_test.dart
@@ -13,8 +13,10 @@
 import 'null_safe_test.mocks.dart';
 import 'test_util.dart';
 
-@GenerateMocks(<Type>[BinaryMessenger])
+@GenerateMocks(<Type>[BinaryMessenger, NullableArgFlutterApi])
 void main() {
+  TestWidgetsFlutterBinding.ensureInitialized();
+
   test('with values filled', () {
     final SearchReply reply = SearchReply()
       ..result = 'foo'
@@ -102,4 +104,31 @@
         NullableArgHostApi(binaryMessenger: mockMessenger);
     expect(await api.doit(null), 123);
   });
+
+  test('receive null parameters', () {
+    final MockNullableArgFlutterApi mockFlutterApi =
+        MockNullableArgFlutterApi();
+    when(mockFlutterApi.doit(null)).thenReturn(14);
+
+    NullableArgFlutterApi.setup(mockFlutterApi);
+
+    final Completer<int> resultCompleter = Completer<int>();
+    // Null check operator is used because ServicesBinding.instance is nullable
+    // in earlier versions of Flutter.
+    // ignore: unnecessary_non_null_assertion
+    ServicesBinding.instance!.defaultBinaryMessenger.handlePlatformMessage(
+      'dev.flutter.pigeon.NullableArgFlutterApi.doit',
+      NullableArgFlutterApi.codec.encodeMessage(<Object?>[null]),
+      (ByteData? data) {
+        resultCompleter.complete(
+          NullableArgFlutterApi.codec.decodeMessage(data)! as int,
+        );
+      },
+    );
+
+    expect(resultCompleter.future, completion(14));
+
+    // Removes message handlers from global default binary messenger.
+    NullableArgFlutterApi.setup(null);
+  });
 }
diff --git a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/null_safe_test.mocks.dart b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/null_safe_test.mocks.dart
index 45fdc63..5f8f129 100644
--- a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/null_safe_test.mocks.dart
+++ b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/null_safe_test.mocks.dart
@@ -1,4 +1,4 @@
-// Mocks generated by Mockito 5.0.4 from annotations
+// Mocks generated by Mockito 5.0.7 from annotations
 // in flutter_unit_tests/test/null_safe_test.dart.
 // Do not manually edit this file.
 
@@ -7,15 +7,15 @@
 import 'dart:ui' as _i5;
 
 import 'package:flutter/src/services/binary_messenger.dart' as _i2;
+import 'package:flutter_unit_tests/nullable_returns.gen.dart' as _i6;
 import 'package:mockito/mockito.dart' as _i1;
 
 // ignore_for_file: comment_references
 // ignore_for_file: unnecessary_parenthesis
-// ignore_for_file: always_specify_types
-// Added manually; several methods have moved to
-// flutter_test/lib/src/deprecated.dart on master, but that hasn't reached
-// stable yet.
-// ignore_for_file: override_on_non_overriding_member
+
+// ignore_for_file: prefer_const_constructors
+
+// ignore_for_file: avoid_redundant_argument_values
 
 /// A class which mocks [BinaryMessenger].
 ///
@@ -31,8 +31,7 @@
       (super.noSuchMethod(
           Invocation.method(#handlePlatformMessage, [channel, data, callback]),
           returnValue: Future<void>.value(null),
-          returnValueForMissingStub:
-              Future<dynamic>.value()) as _i3.Future<void>);
+          returnValueForMissingStub: Future.value()) as _i3.Future<void>);
   @override
   _i3.Future<_i4.ByteData?>? send(String? channel, _i4.ByteData? message) =>
       (super.noSuchMethod(Invocation.method(#send, [channel, message]))
@@ -41,19 +40,19 @@
   void setMessageHandler(String? channel, _i2.MessageHandler? handler) => super
       .noSuchMethod(Invocation.method(#setMessageHandler, [channel, handler]),
           returnValueForMissingStub: null);
+}
+
+/// A class which mocks [NullableArgFlutterApi].
+///
+/// See the documentation for Mockito's code generation for more information.
+class MockNullableArgFlutterApi extends _i1.Mock
+    implements _i6.NullableArgFlutterApi {
+  MockNullableArgFlutterApi() {
+    _i1.throwOnMissingStub(this);
+  }
+
   @override
-  bool checkMessageHandler(String? channel, _i2.MessageHandler? handler) =>
-      (super.noSuchMethod(
-          Invocation.method(#checkMessageHandler, [channel, handler]),
-          returnValue: false) as bool);
-  @override
-  void setMockMessageHandler(String? channel, _i2.MessageHandler? handler) =>
-      super.noSuchMethod(
-          Invocation.method(#setMockMessageHandler, [channel, handler]),
-          returnValueForMissingStub: null);
-  @override
-  bool checkMockMessageHandler(String? channel, _i2.MessageHandler? handler) =>
-      (super.noSuchMethod(
-          Invocation.method(#checkMockMessageHandler, [channel, handler]),
-          returnValue: false) as bool);
+  int doit(int? x) =>
+      (super.noSuchMethod(Invocation.method(#doit, [x]), returnValue: 0)
+          as int);
 }
diff --git a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/primitive_test.mocks.dart b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/primitive_test.mocks.dart
index b5769f9..77923e5 100644
--- a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/primitive_test.mocks.dart
+++ b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/primitive_test.mocks.dart
@@ -16,9 +16,6 @@
 // ignore_for_file: prefer_const_constructors
 
 // ignore_for_file: avoid_redundant_argument_values
-// ignore_for_file: always_specify_types
-// ignore_for_file: implicit_dynamic_type
-// ignore_for_file: subtype_of_disallowed_type
 
 class _FakeInt32List extends _i1.Fake implements _i2.Int32List {}
 
diff --git a/packages/pigeon/pubspec.yaml b/packages/pigeon/pubspec.yaml
index b5bfbbe..8f78999 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: 2.0.3 # This must match the version in lib/generator_tools.dart
+version: 2.0.4 # This must match the version in lib/generator_tools.dart
 
 environment:
   sdk: ">=2.12.0 <3.0.0"