Skip to content

Commit 74188e3

Browse files
authored
refactor(ui)!: add support for non-attachment types in attachment picker (#2293)
1 parent cc69eb3 commit 74188e3

12 files changed

+1183
-754
lines changed

migrations/v10-migration.md

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,13 @@
88

99
This guide includes breaking changes grouped by release phase:
1010

11+
### 🚧 v10.0.0-beta.3
12+
13+
- [AttachmentPickerType](#-attachmentpickertype)
14+
- [StreamAttachmentPickerOption](#-streamattachmentpickeroption)
15+
- [showStreamAttachmentPickerModalBottomSheet](#-showstreamattachmentpickermodalbottomsheet)
16+
- [AttachmentPickerBottomSheet](#-attachmentpickerbottomsheet)
17+
1118
### 🚧 v10.0.0-beta.1
1219

1320
- [StreamReactionPicker](#-streamreactionpicker)
@@ -17,6 +24,183 @@ This guide includes breaking changes grouped by release phase:
1724

1825
---
1926

27+
## 🧪 Migration for v10.0.0-beta.3
28+
29+
### 🛠 AttachmentPickerType
30+
31+
#### Key Changes:
32+
33+
- `AttachmentPickerType` enum replaced with sealed class hierarchy
34+
- Now supports extensible custom types like contact and location pickers
35+
- Use built-in types like `AttachmentPickerType.images` or define your own via `CustomAttachmentPickerType`
36+
37+
#### Migration Steps:
38+
39+
**Before:**
40+
```dart
41+
// Using enum-based attachment types
42+
final attachmentType = AttachmentPickerType.images;
43+
```
44+
45+
**After:**
46+
```dart
47+
// Using sealed class attachment types
48+
final attachmentType = AttachmentPickerType.images;
49+
50+
// For custom types
51+
class LocationAttachmentPickerType extends CustomAttachmentPickerType {
52+
const LocationAttachmentPickerType();
53+
}
54+
```
55+
56+
> ⚠️ **Important:**
57+
> The enum is now a sealed class, but the basic usage remains the same for built-in types.
58+
59+
---
60+
61+
### 🛠 StreamAttachmentPickerOption
62+
63+
#### Key Changes:
64+
65+
- `StreamAttachmentPickerOption` replaced with two sealed classes:
66+
- `SystemAttachmentPickerOption` for system pickers (camera, files)
67+
- `TabbedAttachmentPickerOption` for tabbed pickers (gallery, polls, location)
68+
69+
#### Migration Steps:
70+
71+
**Before:**
72+
```dart
73+
final option = AttachmentPickerOption(
74+
title: 'Gallery',
75+
icon: Icon(Icons.photo_library),
76+
supportedTypes: [AttachmentPickerType.images, AttachmentPickerType.videos],
77+
optionViewBuilder: (context, controller) {
78+
return GalleryPickerView(controller: controller);
79+
},
80+
);
81+
82+
final webOrDesktopOption = WebOrDesktopAttachmentPickerOption(
83+
title: 'File Upload',
84+
icon: Icon(Icons.upload_file),
85+
type: AttachmentPickerType.files,
86+
);
87+
```
88+
89+
**After:**
90+
```dart
91+
// For custom UI pickers (gallery, polls)
92+
final tabbedOption = TabbedAttachmentPickerOption(
93+
title: 'Gallery',
94+
icon: Icon(Icons.photo_library),
95+
supportedTypes: [AttachmentPickerType.images, AttachmentPickerType.videos],
96+
optionViewBuilder: (context, controller) {
97+
return GalleryPickerView(controller: controller);
98+
},
99+
);
100+
101+
// For system pickers (camera, file dialogs)
102+
final systemOption = SystemAttachmentPickerOption(
103+
title: 'Camera',
104+
icon: Icon(Icons.camera_alt),
105+
supportedTypes: [AttachmentPickerType.images],
106+
onTap: (context, controller) => pickFromCamera(),
107+
);
108+
```
109+
110+
> ⚠️ **Important:**
111+
> - Use `SystemAttachmentPickerOption` for system pickers (camera, file dialogs)
112+
> - Use `TabbedAttachmentPickerOption` for custom UI pickers (gallery, polls)
113+
114+
---
115+
116+
### 🛠 showStreamAttachmentPickerModalBottomSheet
117+
118+
#### Key Changes:
119+
120+
- Now returns `StreamAttachmentPickerResult` instead of `AttachmentPickerValue`
121+
- Improved type safety and clearer intent handling
122+
123+
#### Migration Steps:
124+
125+
**Before:**
126+
```dart
127+
final result = await showStreamAttachmentPickerModalBottomSheet(
128+
context: context,
129+
controller: controller,
130+
);
131+
132+
// result is AttachmentPickerValue
133+
```
134+
135+
**After:**
136+
```dart
137+
final result = await showStreamAttachmentPickerModalBottomSheet(
138+
context: context,
139+
controller: controller,
140+
);
141+
142+
// result is StreamAttachmentPickerResult
143+
switch (result) {
144+
case AttachmentsPicked():
145+
// Handle picked attachments
146+
case PollCreated():
147+
// Handle created poll
148+
case AttachmentPickerError():
149+
// Handle error
150+
case CustomAttachmentPickerResult():
151+
// Handle custom result
152+
}
153+
```
154+
155+
> ⚠️ **Important:**
156+
> Always handle the new `StreamAttachmentPickerResult` return type with proper switch cases.
157+
158+
---
159+
160+
### 🛠 AttachmentPickerBottomSheet
161+
162+
#### Key Changes:
163+
164+
- `StreamMobileAttachmentPickerBottomSheet``StreamTabbedAttachmentPickerBottomSheet`
165+
- `StreamWebOrDesktopAttachmentPickerBottomSheet``StreamSystemAttachmentPickerBottomSheet`
166+
167+
#### Migration Steps:
168+
169+
**Before:**
170+
```dart
171+
StreamMobileAttachmentPickerBottomSheet(
172+
context: context,
173+
controller: controller,
174+
customOptions: [option],
175+
);
176+
177+
StreamWebOrDesktopAttachmentPickerBottomSheet(
178+
context: context,
179+
controller: controller,
180+
customOptions: [option],
181+
);
182+
```
183+
184+
**After:**
185+
```dart
186+
StreamTabbedAttachmentPickerBottomSheet(
187+
context: context,
188+
controller: controller,
189+
customOptions: [tabbedOption],
190+
);
191+
192+
StreamSystemAttachmentPickerBottomSheet(
193+
context: context,
194+
controller: controller,
195+
customOptions: [systemOption],
196+
);
197+
```
198+
199+
> ⚠️ **Important:**
200+
> The new names better reflect their respective layouts and functionality.
201+
202+
---
203+
20204
## 🧪 Migration for v10.0.0-beta.1
21205

22206
### 🛠 StreamReactionPicker
@@ -182,6 +366,12 @@ StreamMessageWidget(
182366

183367
## 🎉 You're Ready to Migrate!
184368

369+
### For v10.0.0-beta.3:
370+
- ✅ Update attachment picker options to use `SystemAttachmentPickerOption` or `TabbedAttachmentPickerOption`
371+
- ✅ Handle new `StreamAttachmentPickerResult` return type from attachment picker
372+
- ✅ Use renamed bottom sheet classes (`StreamTabbedAttachmentPickerBottomSheet`, `StreamSystemAttachmentPickerBottomSheet`)
373+
374+
### For v10.0.0-beta.1:
185375
- ✅ Use `StreamReactionPicker.builder` or supply `onReactionPicked`
186376
- ✅ Convert all `StreamMessageAction` instances to type-safe generic usage
187377
- ✅ Centralize handling with `onCustomActionTap`

packages/stream_chat_flutter/CHANGELOG.md

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,19 @@
22

33
🛑️ Breaking
44

5-
- `PollMessage` widget has been removed and replaced with `PollAttachment` for better integration
6-
with the attachment system. Polls can now be customized through `PollAttachmentBuilder` or by
7-
creating custom poll attachment widgets via the attachment builder system.
5+
- `PollMessage` widget has been removed and replaced with `PollAttachment` for better integration with the attachment system. Polls can now be customized through `PollAttachmentBuilder` or by creating custom poll attachment widgets via the attachment builder system.
6+
- `AttachmentPickerType` enum has been replaced with a sealed class to support extensible custom types like contact and location pickers. Use built-in types like `AttachmentPickerType.images` or define your own via `CustomAttachmentPickerType`.
7+
- `StreamAttachmentPickerOption` has been replaced with two sealed classes to support layout-specific picker options: `SystemAttachmentPickerOption` for system pickers (e.g. camera, files) and `TabbedAttachmentPickerOption` for tabbed pickers (e.g. gallery, polls, location).
8+
- `showStreamAttachmentPickerModalBottomSheet` now returns a `StreamAttachmentPickerResult` instead of `AttachmentPickerValue` for improved type safety and clearer intent handling.
9+
- `StreamMobileAttachmentPickerBottomSheet` has been renamed to `StreamTabbedAttachmentPickerBottomSheet`, and `StreamWebOrDesktopAttachmentPickerBottomSheet` has been renamed to `StreamSystemAttachmentPickerBottomSheet` to better reflect their respective layouts.
10+
11+
For more details, please refer to the [migration guide](../../migrations/v10-migration.md).
12+
13+
✅ Added
14+
15+
- Added `extraData` field to `AttachmentPickerValue` to support storing and retrieving custom picker state (e.g. tab-specific config).
16+
- Added `customAttachmentPickerOptions` to `StreamMessageInput` to allow injecting custom picker tabs like contact and location pickers.
17+
- Added `onCustomAttachmentPickerResult` callback to `StreamMessageInput` to handle results returned by custom picker tabs.
818

919
## 10.0.0-beta.2
1020

packages/stream_chat_flutter/lib/src/bottom_sheets/edit_message_sheet.dart

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -115,10 +115,6 @@ class _EditMessageSheetState extends State<EditMessageSheet> {
115115
StreamMessageInput(
116116
elevation: 0,
117117
messageInputController: controller,
118-
// Disallow editing poll for now as it's not supported.
119-
allowedAttachmentPickerTypes: [
120-
...AttachmentPickerType.values,
121-
]..remove(AttachmentPickerType.poll),
122118
preMessageSending: (m) {
123119
FocusScope.of(context).unfocus();
124120
Navigator.of(context).pop();

packages/stream_chat_flutter/lib/src/message_input/attachment_picker/options/stream_gallery_picker.dart

Lines changed: 34 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import 'package:path_provider/path_provider.dart';
66
import 'package:photo_manager/photo_manager.dart';
77
import 'package:stream_chat_flutter/src/icons/stream_svg_icon.dart';
88
import 'package:stream_chat_flutter/src/message_input/attachment_picker/stream_attachment_picker.dart';
9+
import 'package:stream_chat_flutter/src/message_input/attachment_picker/stream_attachment_picker_controller.dart';
910
import 'package:stream_chat_flutter/src/misc/empty_widget.dart';
1011
import 'package:stream_chat_flutter/src/scroll_view/photo_gallery/stream_photo_gallery.dart';
1112
import 'package:stream_chat_flutter/src/scroll_view/photo_gallery/stream_photo_gallery_controller.dart';
@@ -14,7 +15,7 @@ import 'package:stream_chat_flutter/src/utils/utils.dart';
1415
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
1516

1617
/// Max image resolution which can be resized by the CDN.
17-
// Taken from https://getstream.io/chat/docs/flutter-dart/file_uploads/?language=dart#image-resizing
18+
/// Taken from https://getstream.io/chat/docs/flutter-dart/file_uploads/?language=dart#image-resizing
1819
const maxCDNImageResolution = 16800000;
1920

2021
/// Widget used to pick media from the device gallery.
@@ -23,42 +24,23 @@ class StreamGalleryPicker extends StatefulWidget {
2324
const StreamGalleryPicker({
2425
super.key,
2526
this.limit = 50,
27+
GalleryPickerConfig? config,
2628
required this.selectedMediaItems,
2729
required this.onMediaItemSelected,
28-
this.mediaThumbnailSize = const ThumbnailSize(400, 400),
29-
this.mediaThumbnailFormat = ThumbnailFormat.jpeg,
30-
this.mediaThumbnailQuality = 100,
31-
this.mediaThumbnailScale = 1,
32-
});
30+
}) : config = config ?? const GalleryPickerConfig();
3331

3432
/// Maximum number of media items that can be selected.
3533
final int limit;
3634

35+
/// Configuration for the gallery picker.
36+
final GalleryPickerConfig config;
37+
3738
/// List of selected media items.
3839
final Iterable<String> selectedMediaItems;
3940

4041
/// Callback called when an media item is selected.
4142
final ValueSetter<AssetEntity> onMediaItemSelected;
4243

43-
/// Size of the attachment thumbnails.
44-
///
45-
/// Defaults to (400, 400).
46-
final ThumbnailSize mediaThumbnailSize;
47-
48-
/// Format of the attachment thumbnails.
49-
///
50-
/// Defaults to [ThumbnailFormat.jpeg].
51-
final ThumbnailFormat mediaThumbnailFormat;
52-
53-
/// The quality value for the attachment thumbnails.
54-
///
55-
/// Valid from 1 to 100.
56-
/// Defaults to 100.
57-
final int mediaThumbnailQuality;
58-
59-
/// The scale to apply on the [attachmentThumbnailSize].
60-
final double mediaThumbnailScale;
61-
6244
@override
6345
State<StreamGalleryPicker> createState() => _StreamGalleryPickerState();
6446
}
@@ -159,10 +141,10 @@ class _StreamGalleryPickerState extends State<StreamGalleryPicker> {
159141
onMediaTap: widget.onMediaItemSelected,
160142
loadMoreTriggerIndex: 10,
161143
padding: const EdgeInsets.all(2),
162-
thumbnailSize: widget.mediaThumbnailSize,
163-
thumbnailFormat: widget.mediaThumbnailFormat,
164-
thumbnailQuality: widget.mediaThumbnailQuality,
165-
thumbnailScale: widget.mediaThumbnailScale,
144+
thumbnailSize: widget.config.mediaThumbnailSize,
145+
thumbnailFormat: widget.config.mediaThumbnailFormat,
146+
thumbnailQuality: widget.config.mediaThumbnailQuality,
147+
thumbnailScale: widget.config.mediaThumbnailScale,
166148
itemBuilder: (context, mediaItems, index, defaultWidget) {
167149
final media = mediaItems[index];
168150
return defaultWidget.copyWith(
@@ -178,6 +160,29 @@ class _StreamGalleryPickerState extends State<StreamGalleryPicker> {
178160
}
179161
}
180162

163+
/// Configuration for the [StreamGalleryPicker].
164+
class GalleryPickerConfig {
165+
/// Creates a [GalleryPickerConfig] instance.
166+
const GalleryPickerConfig({
167+
this.mediaThumbnailSize = const ThumbnailSize(400, 400),
168+
this.mediaThumbnailFormat = ThumbnailFormat.jpeg,
169+
this.mediaThumbnailQuality = 100,
170+
this.mediaThumbnailScale = 1,
171+
});
172+
173+
/// Size of the attachment thumbnails.
174+
final ThumbnailSize mediaThumbnailSize;
175+
176+
/// Format of the attachment thumbnails.
177+
final ThumbnailFormat mediaThumbnailFormat;
178+
179+
/// The quality value for the attachment thumbnails.
180+
final int mediaThumbnailQuality;
181+
182+
/// The scale to apply on the [mediaThumbnailSize].
183+
final double mediaThumbnailScale;
184+
}
185+
181186
///
182187
extension StreamImagePickerX on StreamAttachmentPickerController {
183188
///

0 commit comments

Comments
 (0)