From 81dcab036b967b652624d67445124abb0491d591 Mon Sep 17 00:00:00 2001 From: Yasmany Cubela Medina Date: Tue, 25 Aug 2026 17:15:57 +0000 Subject: [PATCH 1/4] fix(ui): migrate to file_picker 12 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit file_picker 12 removed the eager PlatformFile.bytes/.size getters in favour of readAsBytes()/readAsByteStream(), and pickFiles() now returns List instead of a FilePickerResult wrapper. - PlatformFileX.toAttachmentFile becomes async, mirroring the XFileX extension that already sat directly below it in the same file. - Both attachment handlers use pickFile() (singular), which returns the PlatformFile? they already wanted instead of taking .files.first — that also removes a throw on an empty selection. - withData/withReadStream are no longer forwarded (deprecated in 12; the content is now read on demand) but stay in the public signature, so this is not a breaking change for callers. --- .../handler/stream_attachment_handler_html.dart | 15 ++++++++++----- .../handler/stream_attachment_handler_io.dart | 15 ++++++++++----- .../lib/src/utils/extensions.dart | 13 +++++++++---- packages/stream_chat_flutter/pubspec.yaml | 2 +- 4 files changed, 30 insertions(+), 15 deletions(-) diff --git a/packages/stream_chat_flutter/lib/src/attachment/handler/stream_attachment_handler_html.dart b/packages/stream_chat_flutter/lib/src/attachment/handler/stream_attachment_handler_html.dart index db70fa7ed5..0e5d77d64e 100644 --- a/packages/stream_chat_flutter/lib/src/attachment/handler/stream_attachment_handler_html.dart +++ b/packages/stream_chat_flutter/lib/src/attachment/handler/stream_attachment_handler_html.dart @@ -26,19 +26,24 @@ class StreamAttachmentHandler extends StreamAttachmentHandlerBase { bool withReadStream = false, bool lockParentWindow = true, }) async { - final result = await FilePicker.pickFiles( + // pickFile (singular) since file_picker 12: it returns the `PlatformFile?` + // this method already wanted, instead of a result wrapper we immediately + // took `.files.first` from — which threw on an empty selection. + // + // `withData` / `withReadStream` are no longer forwarded: file_picker 12 + // deprecated them in favour of reading through PlatformFile.readAsBytes() + // / readAsByteStream(), which is what toAttachmentFile now does. They stay + // in this method's signature so this is not a breaking change for callers. + final result = await FilePicker.pickFile( dialogTitle: dialogTitle, initialDirectory: initialDirectory, type: type, allowedExtensions: allowedExtensions, onFileLoading: onFileLoading, compressionQuality: compressionQuality, - withData: withData, - withReadStream: withReadStream, - lockParentWindow: lockParentWindow, ); - return result?.files.first.toAttachment(type: type.toAttachmentType()); + return await result?.toAttachment(type: type.toAttachmentType()); } @override diff --git a/packages/stream_chat_flutter/lib/src/attachment/handler/stream_attachment_handler_io.dart b/packages/stream_chat_flutter/lib/src/attachment/handler/stream_attachment_handler_io.dart index a7017caa87..ad10cd073c 100644 --- a/packages/stream_chat_flutter/lib/src/attachment/handler/stream_attachment_handler_io.dart +++ b/packages/stream_chat_flutter/lib/src/attachment/handler/stream_attachment_handler_io.dart @@ -135,19 +135,24 @@ class StreamAttachmentHandler extends StreamAttachmentHandlerBase { bool withReadStream = false, bool lockParentWindow = true, }) async { - final result = await FilePicker.pickFiles( + // pickFile (singular) since file_picker 12: it returns the `PlatformFile?` + // this method already wanted, instead of a result wrapper we immediately + // took `.files.first` from — which threw on an empty selection. + // + // `withData` / `withReadStream` are no longer forwarded: file_picker 12 + // deprecated them in favour of reading through PlatformFile.readAsBytes() + // / readAsByteStream(), which is what toAttachmentFile now does. They stay + // in this method's signature so this is not a breaking change for callers. + final result = await FilePicker.pickFile( dialogTitle: dialogTitle, initialDirectory: initialDirectory, type: type, allowedExtensions: allowedExtensions, onFileLoading: onFileLoading, compressionQuality: compressionQuality, - withData: withData, - withReadStream: withReadStream, - lockParentWindow: lockParentWindow, ); - return result?.files.first.toAttachment(type: type.toAttachmentType()); + return await result?.toAttachment(type: type.toAttachmentType()); } @override diff --git a/packages/stream_chat_flutter/lib/src/utils/extensions.dart b/packages/stream_chat_flutter/lib/src/utils/extensions.dart index a3cc755e77..903ed550f1 100644 --- a/packages/stream_chat_flutter/lib/src/utils/extensions.dart +++ b/packages/stream_chat_flutter/lib/src/utils/extensions.dart @@ -116,19 +116,24 @@ extension IterableExtension on Iterable { /// Useful extension for [PlatformFile] extension PlatformFileX on PlatformFile { /// Converts the [PlatformFile] into [AttachmentFile] - AttachmentFile get toAttachmentFile { + /// + /// Asynchronous since file_picker 12: `PlatformFile` no longer exposes the + /// eagerly-loaded `bytes`/`size` getters, the content is read on demand. + /// Mirrors [XFileX.toAttachmentFile] below. + Future get toAttachmentFile async { + final bytes = await readAsBytes(); return AttachmentFile( // Path is not supported on web. path: CurrentPlatform.isWeb ? null : path, name: name, + size: bytes.length, bytes: bytes, - size: size, ); } /// Converts the [PlatformFile] to a [Attachment]. - Attachment toAttachment({required String type}) { - final file = toAttachmentFile; + Future toAttachment({required String type}) async { + final file = await toAttachmentFile; final extraDataMap = {}; final mimeType = file.mediaType?.mimeType; diff --git a/packages/stream_chat_flutter/pubspec.yaml b/packages/stream_chat_flutter/pubspec.yaml index b01e4f218d..184be46c37 100644 --- a/packages/stream_chat_flutter/pubspec.yaml +++ b/packages/stream_chat_flutter/pubspec.yaml @@ -30,7 +30,7 @@ dependencies: diacritic: ^0.1.6 dio: ^5.11.0 ezanimation: ^0.6.0 - file_picker: ^11.0.0 + file_picker: ^12.1.0 file_selector: ^1.1.0 flutter: sdk: flutter From 1ea89a338bc2b1bded6cdfb495e9e79c075b1400 Mon Sep 17 00:00:00 2001 From: Yasmany Cubela Medina Date: Wed, 26 Aug 2026 12:36:55 +0000 Subject: [PATCH 2/4] fix(ui): bump file_picker in melos.yaml, restore lockParentWindow melos.yaml is the source of truth for dependency versions, so bootstrap rewrote the package manifest back to file_picker 11 and every job that runs it failed on `pickFile` and `readAsBytes` being undefined. Restores lockParentWindow through WindowsOptions/LinuxOptions, which is where file_picker 12 moved it, drops the migration commentary from the handlers, and calls the async extensions out as breaking in their own changelog section rather than inside the dependency note. --- melos.yaml | 2 +- .../handler/stream_attachment_handler_html.dart | 10 ++-------- .../handler/stream_attachment_handler_io.dart | 10 ++-------- .../stream_chat_flutter/lib/src/utils/extensions.dart | 4 +--- 4 files changed, 6 insertions(+), 20 deletions(-) diff --git a/melos.yaml b/melos.yaml index 11f0ad0b4e..6ad9be9d60 100644 --- a/melos.yaml +++ b/melos.yaml @@ -52,7 +52,7 @@ command: # upper bounds once FlutterFire ships a fixed release. firebase_crashlytics: '>=5.2.0 <5.2.5' firebase_messaging: '>=16.0.0 <16.4.2' - file_picker: ^11.0.0 + file_picker: ^12.1.0 file_selector: ^1.1.0 app_badge_plus: ^1.3.2 flutter_local_notifications: ^21.0.0 diff --git a/packages/stream_chat_flutter/lib/src/attachment/handler/stream_attachment_handler_html.dart b/packages/stream_chat_flutter/lib/src/attachment/handler/stream_attachment_handler_html.dart index 0e5d77d64e..7ba92bd53c 100644 --- a/packages/stream_chat_flutter/lib/src/attachment/handler/stream_attachment_handler_html.dart +++ b/packages/stream_chat_flutter/lib/src/attachment/handler/stream_attachment_handler_html.dart @@ -26,14 +26,6 @@ class StreamAttachmentHandler extends StreamAttachmentHandlerBase { bool withReadStream = false, bool lockParentWindow = true, }) async { - // pickFile (singular) since file_picker 12: it returns the `PlatformFile?` - // this method already wanted, instead of a result wrapper we immediately - // took `.files.first` from — which threw on an empty selection. - // - // `withData` / `withReadStream` are no longer forwarded: file_picker 12 - // deprecated them in favour of reading through PlatformFile.readAsBytes() - // / readAsByteStream(), which is what toAttachmentFile now does. They stay - // in this method's signature so this is not a breaking change for callers. final result = await FilePicker.pickFile( dialogTitle: dialogTitle, initialDirectory: initialDirectory, @@ -41,6 +33,8 @@ class StreamAttachmentHandler extends StreamAttachmentHandlerBase { allowedExtensions: allowedExtensions, onFileLoading: onFileLoading, compressionQuality: compressionQuality, + windowsOptions: WindowsOptions(lockParentWindow: lockParentWindow), + linuxOptions: LinuxOptions(lockParentWindow: lockParentWindow), ); return await result?.toAttachment(type: type.toAttachmentType()); diff --git a/packages/stream_chat_flutter/lib/src/attachment/handler/stream_attachment_handler_io.dart b/packages/stream_chat_flutter/lib/src/attachment/handler/stream_attachment_handler_io.dart index ad10cd073c..3ef5b18e72 100644 --- a/packages/stream_chat_flutter/lib/src/attachment/handler/stream_attachment_handler_io.dart +++ b/packages/stream_chat_flutter/lib/src/attachment/handler/stream_attachment_handler_io.dart @@ -135,14 +135,6 @@ class StreamAttachmentHandler extends StreamAttachmentHandlerBase { bool withReadStream = false, bool lockParentWindow = true, }) async { - // pickFile (singular) since file_picker 12: it returns the `PlatformFile?` - // this method already wanted, instead of a result wrapper we immediately - // took `.files.first` from — which threw on an empty selection. - // - // `withData` / `withReadStream` are no longer forwarded: file_picker 12 - // deprecated them in favour of reading through PlatformFile.readAsBytes() - // / readAsByteStream(), which is what toAttachmentFile now does. They stay - // in this method's signature so this is not a breaking change for callers. final result = await FilePicker.pickFile( dialogTitle: dialogTitle, initialDirectory: initialDirectory, @@ -150,6 +142,8 @@ class StreamAttachmentHandler extends StreamAttachmentHandlerBase { allowedExtensions: allowedExtensions, onFileLoading: onFileLoading, compressionQuality: compressionQuality, + windowsOptions: WindowsOptions(lockParentWindow: lockParentWindow), + linuxOptions: LinuxOptions(lockParentWindow: lockParentWindow), ); return await result?.toAttachment(type: type.toAttachmentType()); diff --git a/packages/stream_chat_flutter/lib/src/utils/extensions.dart b/packages/stream_chat_flutter/lib/src/utils/extensions.dart index 903ed550f1..52a1b4c7b9 100644 --- a/packages/stream_chat_flutter/lib/src/utils/extensions.dart +++ b/packages/stream_chat_flutter/lib/src/utils/extensions.dart @@ -117,9 +117,7 @@ extension IterableExtension on Iterable { extension PlatformFileX on PlatformFile { /// Converts the [PlatformFile] into [AttachmentFile] /// - /// Asynchronous since file_picker 12: `PlatformFile` no longer exposes the - /// eagerly-loaded `bytes`/`size` getters, the content is read on demand. - /// Mirrors [XFileX.toAttachmentFile] below. + /// Reads the file content on demand, so the result is asynchronous. Future get toAttachmentFile async { final bytes = await readAsBytes(); return AttachmentFile( From cd7f4576553e6a4bc5fffa0b3f05b37d58edd49d Mon Sep 17 00:00:00 2001 From: Yasmany Cubela Medina Date: Wed, 26 Aug 2026 15:02:40 +0000 Subject: [PATCH 3/4] fix(sample): evaluate plugin projects after :app file_picker 12 pulls in android_file_picker, whose build script reads the `flutter` Gradle extension at configuration time. The Flutter Gradle plugin only registers that extension on plugin projects while `:app` is being configured, and plugin projects evaluate first by default, so the Android build failed with "Extension with name 'flutter' does not exist". The current Flutter app template carries this same block; sample_app predates it. It goes after the existing subprojects block rather than inside it: forcing evaluation from there runs `:app` before its own afterEvaluate is registered, which Gradle rejects outright. --- sample_app/android/build.gradle | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/sample_app/android/build.gradle b/sample_app/android/build.gradle index 22f412dfaa..79709112b1 100644 --- a/sample_app/android/build.gradle +++ b/sample_app/android/build.gradle @@ -25,6 +25,17 @@ subprojects { } +// Plugin projects evaluate before `:app` by default, but the Flutter Gradle +// plugin only registers the `flutter` extension on them while `:app` is being +// configured. A plugin whose build script reads that extension at configuration +// time — android_file_picker, pulled in by file_picker 12 — fails without this. +// Present in the current Flutter app template; this module predates it. Kept in +// its own block, after the one above: forcing evaluation from inside that block +// would run `:app` before its `afterEvaluate` is registered. +subprojects { + project.evaluationDependsOn(":app") +} + tasks.register("clean", Delete) { delete rootProject.layout.buildDirectory } \ No newline at end of file From 272ba9f75b1f8de9ecf58c49b122235125336b0a Mon Sep 17 00:00:00 2001 From: Yasmany Cubela Medina Date: Wed, 16 Sep 2026 15:10:21 +0000 Subject: [PATCH 4/4] fix(ui): widen file_picker range and deprecate ignored read flags - Constrain file_picker to '>=12.0.0 <14.0.0' in melos.yaml and the package pubspec; every symbol the handlers use is unchanged in 13.x. - Deprecate withData and withReadStream on StreamAttachmentHandler.pickFile (base, io, html) and on StreamFilePicker, which no longer forwards them. - Add the changelog entries under Upcoming, including the breaking async PlatformFileX change and the evaluationDependsOn(":app") note for apps on pre-3.44 Android templates. --- melos.yaml | 2 +- packages/stream_chat_flutter/CHANGELOG.md | 8 ++++++++ .../handler/stream_attachment_handler_base.dart | 4 ++-- .../handler/stream_attachment_handler_html.dart | 4 ++-- .../attachment/handler/stream_attachment_handler_io.dart | 4 ++-- .../attachment_picker/options/stream_file_picker.dart | 8 ++++---- packages/stream_chat_flutter/pubspec.yaml | 2 +- 7 files changed, 20 insertions(+), 12 deletions(-) diff --git a/melos.yaml b/melos.yaml index 6ad9be9d60..eefa73ba1c 100644 --- a/melos.yaml +++ b/melos.yaml @@ -52,7 +52,7 @@ command: # upper bounds once FlutterFire ships a fixed release. firebase_crashlytics: '>=5.2.0 <5.2.5' firebase_messaging: '>=16.0.0 <16.4.2' - file_picker: ^12.1.0 + file_picker: '>=12.0.0 <14.0.0' file_selector: ^1.1.0 app_badge_plus: ^1.3.2 flutter_local_notifications: ^21.0.0 diff --git a/packages/stream_chat_flutter/CHANGELOG.md b/packages/stream_chat_flutter/CHANGELOG.md index 2d3d9b635e..b502c58e0e 100644 --- a/packages/stream_chat_flutter/CHANGELOG.md +++ b/packages/stream_chat_flutter/CHANGELOG.md @@ -1,5 +1,9 @@ ## Upcoming +🛑️ Breaking + +- `PlatformFileX.toAttachmentFile` and `PlatformFileX.toAttachment` are now asynchronous, returning `Future` and `Future`. `file_picker` 12 removed `PlatformFile`'s eagerly-loaded `bytes` and `size` getters, so the content is read on demand; this matches the existing `XFileX` extensions. Any `PlatformFile` you hold came from `FilePicker.pickFiles()`, whose return type also changed in `file_picker` 12, so that call site needs rewriting anyway and the added `await` goes in the same edit. + ✅ Added - Added `StreamMessageItem.semanticsLabel`, which replaces the announcement composed for a message row, and `StreamMessageItem.excludeFromSemantics`, which leaves the row unlabeled so the bubble and footer announce their own parts. @@ -8,6 +12,9 @@ ⚠️ Changed +- Bumped `file_picker` to `>=12.0.0 <14.0.0`. +- Deprecated `withData` and `withReadStream` on `StreamAttachmentHandler.pickFile` and `StreamFilePicker`. Content is now read on demand, so both are ignored. +- Android apps built from a Flutter template older than 3.44 must add `subprojects { project.evaluationDependsOn(":app") }` to their root `android/build.gradle`. Without it the build fails with `cannot find symbol: class FilePickerPlugin`, an error that says nothing about its cause. - Video thumbnails now use `stream_thumbnail` on every platform, and the `thumblr` dependency is gone. - Linux builds now need the FFmpeg and libwebp development packages — on Debian/Ubuntu: @@ -17,6 +24,7 @@ 🐞 Fixed +- Fixed `StreamAttachmentHandler.pickFile` throwing when the picker returned an empty selection: it took `.files.first` unconditionally. It now returns `null`. - Fixed `StreamAttachmentHandler` throwing `UnimplementedError` on WebAssembly builds. - Improved the screen-reader experience in the message list: each message is announced as a single phrase naming the sender, the body, the time, the edited marker and the delivery status, while the attachments, reaction chips, quoted message and replies row stay reachable one level deeper. - Fixed the message body being announced as its markdown source, so link and emphasis syntax is no longer read aloud. diff --git a/packages/stream_chat_flutter/lib/src/attachment/handler/stream_attachment_handler_base.dart b/packages/stream_chat_flutter/lib/src/attachment/handler/stream_attachment_handler_base.dart index 4f4605c677..5b3c7c08ed 100644 --- a/packages/stream_chat_flutter/lib/src/attachment/handler/stream_attachment_handler_base.dart +++ b/packages/stream_chat_flutter/lib/src/attachment/handler/stream_attachment_handler_base.dart @@ -32,8 +32,8 @@ abstract class StreamAttachmentHandlerBase { List? allowedExtensions, Function(FilePickerStatus)? onFileLoading, int compressionQuality = 0, - bool withData = true, - bool withReadStream = false, + @Deprecated('Content is read on demand; this no longer has any effect.') bool withData = true, + @Deprecated('Content is read on demand; this no longer has any effect.') bool withReadStream = false, bool lockParentWindow = true, }) { throw UnimplementedError('pickFile is not implemented'); diff --git a/packages/stream_chat_flutter/lib/src/attachment/handler/stream_attachment_handler_html.dart b/packages/stream_chat_flutter/lib/src/attachment/handler/stream_attachment_handler_html.dart index 7ba92bd53c..c13ea5ab56 100644 --- a/packages/stream_chat_flutter/lib/src/attachment/handler/stream_attachment_handler_html.dart +++ b/packages/stream_chat_flutter/lib/src/attachment/handler/stream_attachment_handler_html.dart @@ -22,8 +22,8 @@ class StreamAttachmentHandler extends StreamAttachmentHandlerBase { List? allowedExtensions, Function(FilePickerStatus)? onFileLoading, int compressionQuality = 0, - bool withData = true, - bool withReadStream = false, + @Deprecated('Content is read on demand; this no longer has any effect.') bool withData = true, + @Deprecated('Content is read on demand; this no longer has any effect.') bool withReadStream = false, bool lockParentWindow = true, }) async { final result = await FilePicker.pickFile( diff --git a/packages/stream_chat_flutter/lib/src/attachment/handler/stream_attachment_handler_io.dart b/packages/stream_chat_flutter/lib/src/attachment/handler/stream_attachment_handler_io.dart index 3ef5b18e72..091f6aa44f 100644 --- a/packages/stream_chat_flutter/lib/src/attachment/handler/stream_attachment_handler_io.dart +++ b/packages/stream_chat_flutter/lib/src/attachment/handler/stream_attachment_handler_io.dart @@ -131,8 +131,8 @@ class StreamAttachmentHandler extends StreamAttachmentHandlerBase { List? allowedExtensions, Function(FilePickerStatus)? onFileLoading, int compressionQuality = 0, - bool withData = true, - bool withReadStream = false, + @Deprecated('Content is read on demand; this no longer has any effect.') bool withData = true, + @Deprecated('Content is read on demand; this no longer has any effect.') bool withReadStream = false, bool lockParentWindow = true, }) async { final result = await FilePicker.pickFile( diff --git a/packages/stream_chat_flutter/lib/src/message_input/attachment_picker/options/stream_file_picker.dart b/packages/stream_chat_flutter/lib/src/message_input/attachment_picker/options/stream_file_picker.dart index d3f296eca6..cc98355d3a 100644 --- a/packages/stream_chat_flutter/lib/src/message_input/attachment_picker/options/stream_file_picker.dart +++ b/packages/stream_chat_flutter/lib/src/message_input/attachment_picker/options/stream_file_picker.dart @@ -19,8 +19,8 @@ class StreamFilePicker extends StatelessWidget { this.allowedExtensions, this.onFileLoading, this.compressionQuality = 0, - this.withData = false, - this.withReadStream = false, + @Deprecated('Content is read on demand; this no longer has any effect.') this.withData = false, + @Deprecated('Content is read on demand; this no longer has any effect.') this.withReadStream = false, this.lockParentWindow = false, }); @@ -46,9 +46,11 @@ class StreamFilePicker extends StatelessWidget { final int compressionQuality; /// Whether to include the file data in the [Attachment]. + @Deprecated('Content is read on demand; this no longer has any effect.') final bool withData; /// Whether to include the file read stream in the [Attachment]. + @Deprecated('Content is read on demand; this no longer has any effect.') final bool withReadStream; /// Whether to lock the parent window when the file picker is open. @@ -69,8 +71,6 @@ class StreamFilePicker extends StatelessWidget { allowedExtensions: allowedExtensions, onFileLoading: onFileLoading, compressionQuality: compressionQuality, - withData: withData, - withReadStream: withReadStream, lockParentWindow: lockParentWindow, ); }); diff --git a/packages/stream_chat_flutter/pubspec.yaml b/packages/stream_chat_flutter/pubspec.yaml index 184be46c37..b5518ad503 100644 --- a/packages/stream_chat_flutter/pubspec.yaml +++ b/packages/stream_chat_flutter/pubspec.yaml @@ -30,7 +30,7 @@ dependencies: diacritic: ^0.1.6 dio: ^5.11.0 ezanimation: ^0.6.0 - file_picker: ^12.1.0 + file_picker: '>=12.0.0 <14.0.0' file_selector: ^1.1.0 flutter: sdk: flutter