From 2656767ae507fa357167dcda7496642c85551f4b Mon Sep 17 00:00:00 2001 From: devgianlu Date: Fri, 26 Sep 2025 16:13:02 +0200 Subject: [PATCH 1/5] [pigeon] Encode custom enum as long in Kotlin generator --- packages/pigeon/CHANGELOG.md | 3 ++- .../main/kotlin/dev/flutter/pigeon_example_app/Messages.g.kt | 2 +- packages/pigeon/lib/src/generator_tools.dart | 2 +- packages/pigeon/lib/src/kotlin/kotlin_generator.dart | 4 +++- .../src/main/kotlin/com/example/test_plugin/CoreTests.gen.kt | 4 ++-- .../kotlin/com/example/test_plugin/EventChannelTests.gen.kt | 4 ++-- .../main/kotlin/com/example/test_plugin/ProxyApiTests.gen.kt | 2 +- packages/pigeon/pubspec.yaml | 2 +- 8 files changed, 13 insertions(+), 10 deletions(-) diff --git a/packages/pigeon/CHANGELOG.md b/packages/pigeon/CHANGELOG.md index d36975e5b0f..6d580c9fa2c 100644 --- a/packages/pigeon/CHANGELOG.md +++ b/packages/pigeon/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 26.0.2 * Updates minimum supported SDK version to Flutter 3.29/Dart 3.7. +* [kotlin] Serialize custom enums as `Long` instead of `Int` to avoid `ClassCastException` on decoding. ## 26.0.1 diff --git a/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/Messages.g.kt b/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/Messages.g.kt index e6850db636e..b42861360c3 100644 --- a/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/Messages.g.kt +++ b/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/Messages.g.kt @@ -142,7 +142,7 @@ private open class MessagesPigeonCodec : StandardMessageCodec() { when (value) { is Code -> { stream.write(129) - writeValue(stream, value.raw) + writeValue(stream, value.raw.toLong()) } is MessageData -> { stream.write(130) diff --git a/packages/pigeon/lib/src/generator_tools.dart b/packages/pigeon/lib/src/generator_tools.dart index 464750e928a..fea24b6ef45 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 = '26.0.1'; +const String pigeonVersion = '26.0.2'; /// Read all the content from [stdin] to a String. String readStdin() { diff --git a/packages/pigeon/lib/src/kotlin/kotlin_generator.dart b/packages/pigeon/lib/src/kotlin/kotlin_generator.dart index 79801c4239e..b8a62c1fe23 100644 --- a/packages/pigeon/lib/src/kotlin/kotlin_generator.dart +++ b/packages/pigeon/lib/src/kotlin/kotlin_generator.dart @@ -475,7 +475,9 @@ class KotlinGenerator extends StructuredGenerator { void writeEncodeLogic(EnumeratedType customType) { final String encodeString = - customType.type == CustomTypes.customClass ? 'toList()' : 'raw'; + customType.type == CustomTypes.customClass + ? 'toList()' + : 'raw.toLong()'; final String valueString = customType.enumeration < maximumCodecFieldKey ? 'value.$encodeString' 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 da69ba67a1e..52a7627b06a 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 @@ -704,11 +704,11 @@ private open class CoreTestsPigeonCodec : StandardMessageCodec() { when (value) { is AnEnum -> { stream.write(129) - writeValue(stream, value.raw) + writeValue(stream, value.raw.toLong()) } is AnotherEnum -> { stream.write(130) - writeValue(stream, value.raw) + writeValue(stream, value.raw.toLong()) } is UnusedClass -> { stream.write(131) diff --git a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/EventChannelTests.gen.kt b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/EventChannelTests.gen.kt index 086269a7adc..f9d126ac190 100644 --- a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/EventChannelTests.gen.kt +++ b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/EventChannelTests.gen.kt @@ -477,11 +477,11 @@ private open class EventChannelTestsPigeonCodec : StandardMessageCodec() { when (value) { is EventEnum -> { stream.write(129) - writeValue(stream, value.raw) + writeValue(stream, value.raw.toLong()) } is AnotherEventEnum -> { stream.write(130) - writeValue(stream, value.raw) + writeValue(stream, value.raw.toLong()) } is EventAllNullableTypes -> { stream.write(131) diff --git a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/ProxyApiTests.gen.kt b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/ProxyApiTests.gen.kt index 47a98f16d66..260e25ea600 100644 --- a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/ProxyApiTests.gen.kt +++ b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/ProxyApiTests.gen.kt @@ -508,7 +508,7 @@ private open class ProxyApiTestsPigeonCodec : StandardMessageCodec() { when (value) { is ProxyApiTestEnum -> { stream.write(129) - writeValue(stream, value.raw) + writeValue(stream, value.raw.toLong()) } else -> super.writeValue(stream, value) } diff --git a/packages/pigeon/pubspec.yaml b/packages/pigeon/pubspec.yaml index 3197a83dd4e..3e247f4cfad 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: 26.0.1 # This must match the version in lib/src/generator_tools.dart +version: 26.0.2 # This must match the version in lib/src/generator_tools.dart environment: sdk: ^3.7.0 From 0d04fa031f660ad7437ced760e30f4f6b951596a Mon Sep 17 00:00:00 2001 From: devgianlu Date: Fri, 26 Sep 2025 16:51:23 +0200 Subject: [PATCH 2/5] [pigeon] Add enums to Kotlin AllDatatypes test --- .../test/kotlin/com/example/test_plugin/AllDatatypesTest.kt | 5 +++++ 1 file changed, 5 insertions(+) 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 c04a0dead5c..b3b2f7d83cc 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 @@ -58,10 +58,13 @@ internal class AllDatatypesTest { aNullable4ByteArray = intArrayOf(1, 2, 3, 4), aNullable8ByteArray = longArrayOf(1, 2, 3, 4), aNullableFloatArray = doubleArrayOf(0.5, 0.25, 1.5, 1.25), + aNullableEnum = AnEnum.TWO, + anotherNullableEnum = AnotherEnum.JUST_IN_CASE, aNullableObject = 0, list = listOf(1, 2, 3), stringList = stringList, boolList = listOf(true, false), + enumList = listOf(AnEnum.ONE, AnEnum.TWO), intList = listOf(1, 2), doubleList = listOf(1.1, 2.2), objectList = listOf(1, 2, 3), @@ -71,6 +74,8 @@ internal class AllDatatypesTest { stringMap = mapOf("hello" to "you"), intMap = mapOf(1L to 0L), objectMap = mapOf("hello" to 1234), + enumMap = + mapOf(AnEnum.ONE to AnEnum.FORTY_TWO, AnEnum.TWO to AnEnum.FOUR_HUNDRED_TWENTY_TWO), listMap = mapOf(1L to stringList), mapMap = mapOf(1L to mapOf())) val binaryMessenger = mockk() From bba17105cf71ee32d49f5d94b63d27eb562583ed Mon Sep 17 00:00:00 2001 From: devgianlu Date: Wed, 22 Oct 2025 09:36:23 +0200 Subject: [PATCH 3/5] [pigeon] Add roundtrip test for Kotlin AllDatatypes --- .../example/test_plugin/AllDatatypesTest.kt | 86 ++++++++++++------- 1 file changed, 55 insertions(+), 31 deletions(-) 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 b3b2f7d83cc..e2f80062c6e 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 @@ -46,38 +46,52 @@ internal class AllDatatypesTest { } @Test - fun testHasValues() { + fun testRoundtripNullValues() { + val everything = AllNullableTypes() + val codec = FlutterIntegrationCoreApi.codec + val encoded = codec.encodeMessage(everything) + encoded?.rewind() + val decoded = codec.decodeMessage(encoded) + assertEquals(everything, decoded) + } + + fun getFullyPopulatedAllNullableTypes(): AllNullableTypes { val stringList = listOf("string", "another one") - val everything = - AllNullableTypes( - aNullableBool = false, - aNullableInt = 1234L, - aNullableDouble = 2.0, - aNullableString = "hello", - aNullableByteArray = byteArrayOf(1, 2, 3, 4), - aNullable4ByteArray = intArrayOf(1, 2, 3, 4), - aNullable8ByteArray = longArrayOf(1, 2, 3, 4), - aNullableFloatArray = doubleArrayOf(0.5, 0.25, 1.5, 1.25), - aNullableEnum = AnEnum.TWO, - anotherNullableEnum = AnotherEnum.JUST_IN_CASE, - aNullableObject = 0, - list = listOf(1, 2, 3), - stringList = stringList, - boolList = listOf(true, false), - enumList = listOf(AnEnum.ONE, AnEnum.TWO), - intList = listOf(1, 2), - doubleList = listOf(1.1, 2.2), - objectList = listOf(1, 2, 3), - listList = listOf(stringList, stringList.toList()), - mapList = listOf(mapOf("hello" to 1234), mapOf("hello" to 1234)), - map = mapOf("hello" to 1234), - stringMap = mapOf("hello" to "you"), - intMap = mapOf(1L to 0L), - objectMap = mapOf("hello" to 1234), - enumMap = - mapOf(AnEnum.ONE to AnEnum.FORTY_TWO, AnEnum.TWO to AnEnum.FOUR_HUNDRED_TWENTY_TWO), - listMap = mapOf(1L to stringList), - mapMap = mapOf(1L to mapOf())) + + return AllNullableTypes( + aNullableBool = false, + aNullableInt = 1234L, + aNullableDouble = 2.0, + aNullableString = "hello", + aNullableByteArray = byteArrayOf(1, 2, 3, 4), + aNullable4ByteArray = intArrayOf(1, 2, 3, 4), + aNullable8ByteArray = longArrayOf(1, 2, 3, 4), + aNullableFloatArray = doubleArrayOf(0.5, 0.25, 1.5, 1.25), + aNullableEnum = AnEnum.TWO, + anotherNullableEnum = AnotherEnum.JUST_IN_CASE, + aNullableObject = 0, + list = listOf(1, 2, 3), + stringList = stringList, + boolList = listOf(true, false), + enumList = listOf(AnEnum.ONE, AnEnum.TWO), + intList = listOf(1, 2), + doubleList = listOf(1.1, 2.2), + objectList = listOf(1, 2, 3), + listList = listOf(stringList, stringList.toList()), + mapList = listOf(mapOf("hello" to 1234), mapOf("hello" to 1234)), + map = mapOf("hello" to 1234), + stringMap = mapOf("hello" to "you"), + intMap = mapOf(1L to 0L), + objectMap = mapOf("hello" to 1234), + enumMap = + mapOf(AnEnum.ONE to AnEnum.FORTY_TWO, AnEnum.TWO to AnEnum.FOUR_HUNDRED_TWENTY_TWO), + listMap = mapOf(1L to stringList), + mapMap = mapOf(1L to mapOf())) + } + + @Test + fun testHasValues() { + val everything = getFullyPopulatedAllNullableTypes() val binaryMessenger = mockk() val api = FlutterIntegrationCoreApi(binaryMessenger) @@ -102,6 +116,16 @@ internal class AllDatatypesTest { assertTrue(didCall) } + @Test + fun testRoundtripHasValues() { + val everything = getFullyPopulatedAllNullableTypes() + val codec = FlutterIntegrationCoreApi.codec + val encoded = codec.encodeMessage(everything) + encoded?.rewind() + val decoded = codec.decodeMessage(encoded) + assertEquals(everything, decoded) + } + private val correctList = listOf("a", 2, "three") private val matchingList = correctList.toMutableList() private val differentList = listOf("a", 2, "three", 4.0) From 3738fd537598975d051a2742a29f0f573eabce53 Mon Sep 17 00:00:00 2001 From: Tarrin Neal Date: Thu, 6 Nov 2025 12:59:34 -0800 Subject: [PATCH 4/5] Update generator_tools.dart --- packages/pigeon/lib/src/generator_tools.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pigeon/lib/src/generator_tools.dart b/packages/pigeon/lib/src/generator_tools.dart index 1e95428bcea..eef39cb00bd 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 = '26.0.4'; +const String pigeonVersion = '26.0.5'; /// Read all the content from [stdin] to a String. String readStdin() { From a600c6abe331ec04fa63fbad95086ba4e9b5c4b1 Mon Sep 17 00:00:00 2001 From: Tarrin Neal Date: Thu, 6 Nov 2025 12:59:55 -0800 Subject: [PATCH 5/5] Update pubspec.yaml --- packages/pigeon/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pigeon/pubspec.yaml b/packages/pigeon/pubspec.yaml index ba360b2f690..e7e2860629e 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: 26.0.4 # This must match the version in lib/src/generator_tools.dart +version: 26.0.5 # This must match the version in lib/src/generator_tools.dart environment: sdk: ^3.7.0