From acfaba2f239fc934ed5b6f309b508093cd38b845 Mon Sep 17 00:00:00 2001 From: PRANTA Dutta Date: Sat, 13 Jun 2026 00:39:21 +0600 Subject: [PATCH] [pigeon] Add usage docs to generated Dart event channel methods The generated event-channel method creates a new EventChannel each time it is called, but nothing in the generated code documented that, making it easy to call repeatedly for the same instance or to assume the returned stream is single-subscription. Document on each generated method that it should not be called multiple times for the same instanceName and that the returned stream is a broadcast stream. Also emit the user's own documentation comments on event channel methods, which were previously dropped (only the API-level comments were emitted). Fixes https://github.com/flutter/flutter/issues/177776 --- packages/pigeon/CHANGELOG.md | 5 +++ .../app/lib/src/event_channel_messages.g.dart | 6 +++ .../pigeon/lib/src/dart/dart_generator.dart | 13 ++++++ packages/pigeon/lib/src/generator_tools.dart | 2 +- .../generated/event_channel_tests.gen.dart | 18 +++++++++ ...ent_channel_without_classes_tests.gen.dart | 6 +++ packages/pigeon/pubspec.yaml | 2 +- packages/pigeon/test/dart_generator_test.dart | 40 +++++++++++++++++++ 8 files changed, 90 insertions(+), 2 deletions(-) diff --git a/packages/pigeon/CHANGELOG.md b/packages/pigeon/CHANGELOG.md index a87e4e99eee6..803f62bc5118 100644 --- a/packages/pigeon/CHANGELOG.md +++ b/packages/pigeon/CHANGELOG.md @@ -1,3 +1,8 @@ +## 27.1.1 + +* [dart] Adds usage documentation to generated event channel methods, and + emits the user's documentation comments on them. + ## 27.1.0 * [swift] Adds `CaseIterable` conformance to generated enums. diff --git a/packages/pigeon/example/app/lib/src/event_channel_messages.g.dart b/packages/pigeon/example/app/lib/src/event_channel_messages.g.dart index 28217b3755f5..d13b926f2a08 100644 --- a/packages/pigeon/example/app/lib/src/event_channel_messages.g.dart +++ b/packages/pigeon/example/app/lib/src/event_channel_messages.g.dart @@ -188,6 +188,12 @@ class _PigeonCodec extends StandardMessageCodec { const StandardMethodCodec pigeonMethodCodec = StandardMethodCodec(_PigeonCodec()); +/// Returns a broadcast [Stream] of events from the `streamEvents` event channel. +/// +/// Each call to this method creates a new [EventChannel], so it should +/// not be called multiple times for the same `instanceName`. To deliver +/// events to multiple listeners, call this method once and listen to the +/// returned broadcast stream multiple times instead. Stream streamEvents({String instanceName = ''}) { if (instanceName.isNotEmpty) { instanceName = '.$instanceName'; diff --git a/packages/pigeon/lib/src/dart/dart_generator.dart b/packages/pigeon/lib/src/dart/dart_generator.dart index ae0614a4a788..3218103e5953 100644 --- a/packages/pigeon/lib/src/dart/dart_generator.dart +++ b/packages/pigeon/lib/src/dart/dart_generator.dart @@ -632,6 +632,19 @@ final BinaryMessenger? ${varNamePrefix}binaryMessenger; indent.newln(); addDocumentationComments(indent, api.documentationComments, docCommentSpec); for (final Method func in api.methods) { + addDocumentationComments( + indent, + func.documentationComments, + docCommentSpec, + generatorComments: [ + 'Returns a broadcast [Stream] of events from the `${func.name}` event channel.', + '', + 'Each call to this method creates a new [EventChannel], so it should', + 'not be called multiple times for the same `instanceName`. To deliver', + 'events to multiple listeners, call this method once and listen to the', + 'returned broadcast stream multiple times instead.', + ], + ); indent.format(''' Stream<${func.returnType.baseName}> ${func.name}(${_getMethodParameterSignature(func.parameters, addTrailingComma: true)} {String instanceName = ''}) { if (instanceName.isNotEmpty) { diff --git a/packages/pigeon/lib/src/generator_tools.dart b/packages/pigeon/lib/src/generator_tools.dart index 4cc20cfd1cfa..dbc56d4823b8 100644 --- a/packages/pigeon/lib/src/generator_tools.dart +++ b/packages/pigeon/lib/src/generator_tools.dart @@ -15,7 +15,7 @@ import 'generator.dart'; /// The current version of pigeon. /// /// This must match the version in pubspec.yaml. -const String pigeonVersion = '27.1.0'; +const String pigeonVersion = '27.1.1'; /// Default plugin package name. const String defaultPluginPackageName = 'dev.flutter.pigeon'; diff --git a/packages/pigeon/platform_tests/shared_test_plugin_code/lib/src/generated/event_channel_tests.gen.dart b/packages/pigeon/platform_tests/shared_test_plugin_code/lib/src/generated/event_channel_tests.gen.dart index 48d008b00929..960283770445 100644 --- a/packages/pigeon/platform_tests/shared_test_plugin_code/lib/src/generated/event_channel_tests.gen.dart +++ b/packages/pigeon/platform_tests/shared_test_plugin_code/lib/src/generated/event_channel_tests.gen.dart @@ -663,6 +663,12 @@ class _PigeonCodec extends StandardMessageCodec { const StandardMethodCodec pigeonMethodCodec = StandardMethodCodec(_PigeonCodec()); +/// Returns a broadcast [Stream] of events from the `streamInts` event channel. +/// +/// Each call to this method creates a new [EventChannel], so it should +/// not be called multiple times for the same `instanceName`. To deliver +/// events to multiple listeners, call this method once and listen to the +/// returned broadcast stream multiple times instead. Stream streamInts({String instanceName = ''}) { if (instanceName.isNotEmpty) { instanceName = '.$instanceName'; @@ -676,6 +682,12 @@ Stream streamInts({String instanceName = ''}) { }); } +/// Returns a broadcast [Stream] of events from the `streamEvents` event channel. +/// +/// Each call to this method creates a new [EventChannel], so it should +/// not be called multiple times for the same `instanceName`. To deliver +/// events to multiple listeners, call this method once and listen to the +/// returned broadcast stream multiple times instead. Stream streamEvents({String instanceName = ''}) { if (instanceName.isNotEmpty) { instanceName = '.$instanceName'; @@ -689,6 +701,12 @@ Stream streamEvents({String instanceName = ''}) { }); } +/// Returns a broadcast [Stream] of events from the `streamConsistentNumbers` event channel. +/// +/// Each call to this method creates a new [EventChannel], so it should +/// not be called multiple times for the same `instanceName`. To deliver +/// events to multiple listeners, call this method once and listen to the +/// returned broadcast stream multiple times instead. Stream streamConsistentNumbers({String instanceName = ''}) { if (instanceName.isNotEmpty) { instanceName = '.$instanceName'; diff --git a/packages/pigeon/platform_tests/shared_test_plugin_code/lib/src/generated/event_channel_without_classes_tests.gen.dart b/packages/pigeon/platform_tests/shared_test_plugin_code/lib/src/generated/event_channel_without_classes_tests.gen.dart index 1bc0680e378b..d6c39f9ea9a1 100644 --- a/packages/pigeon/platform_tests/shared_test_plugin_code/lib/src/generated/event_channel_without_classes_tests.gen.dart +++ b/packages/pigeon/platform_tests/shared_test_plugin_code/lib/src/generated/event_channel_without_classes_tests.gen.dart @@ -36,6 +36,12 @@ class _PigeonCodec extends StandardMessageCodec { const StandardMethodCodec pigeonMethodCodec = StandardMethodCodec(_PigeonCodec()); +/// Returns a broadcast [Stream] of events from the `streamIntsAgain` event channel. +/// +/// Each call to this method creates a new [EventChannel], so it should +/// not be called multiple times for the same `instanceName`. To deliver +/// events to multiple listeners, call this method once and listen to the +/// returned broadcast stream multiple times instead. Stream streamIntsAgain({String instanceName = ''}) { if (instanceName.isNotEmpty) { instanceName = '.$instanceName'; diff --git a/packages/pigeon/pubspec.yaml b/packages/pigeon/pubspec.yaml index 65afaca2890f..bf3b9563ca90 100644 --- a/packages/pigeon/pubspec.yaml +++ b/packages/pigeon/pubspec.yaml @@ -2,7 +2,7 @@ name: pigeon 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%3A%22p%3A+pigeon%22 -version: 27.1.0 # This must match the version in lib/src/generator_tools.dart +version: 27.1.1 # This must match the version in lib/src/generator_tools.dart environment: sdk: ^3.10.0 diff --git a/packages/pigeon/test/dart_generator_test.dart b/packages/pigeon/test/dart_generator_test.dart index 695b9a97c8c5..dce12d0c2034 100644 --- a/packages/pigeon/test/dart_generator_test.dart +++ b/packages/pigeon/test/dart_generator_test.dart @@ -79,6 +79,46 @@ void main() { expect(code, contains(' two,')); }); + test('gen event channel api with usage docs on the generated method', () { + final api = AstEventChannelApi( + name: 'EventApi', + methods: [ + Method( + name: 'streamEvents', + location: ApiLocation.host, + returnType: const TypeDeclaration(baseName: 'int', isNullable: false), + parameters: [], + documentationComments: [' An example event stream.'], + ), + ], + ); + final root = Root(apis: [api], classes: [], enums: []); + final sink = StringBuffer(); + const generator = DartGenerator(); + generator.generate( + const InternalDartOptions(ignoreLints: false), + root, + sink, + dartPackageName: DEFAULT_PACKAGE_NAME, + ); + final code = sink.toString(); + expect(code, contains('Stream streamEvents(')); + // The user's own documentation comments are emitted. + expect(code, contains('/// An example event stream.')); + // The generated usage documentation explains the channel-owning and + // broadcast semantics. + expect( + code, + contains('/// Returns a broadcast [Stream] of events from the `streamEvents` event channel.'), + ); + expect(code, contains('not be called multiple times for the same `instanceName`')); + // The usage documentation is attached directly above the generated method. + expect( + code.indexOf('not be called multiple times'), + lessThan(code.indexOf('Stream streamEvents(')), + ); + }); + test('gen one host api', () { final root = Root( apis: [