Skip to content

Commit 24ba997

Browse files
Merge branch 'main' of github.com:flutter-form-builder-ecosystem/form_builder_image_picker
2 parents ea2b2cb + 33b3053 commit 24ba997

File tree

6 files changed

+156
-34
lines changed

6 files changed

+156
-34
lines changed

README.md

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ ___
1414
- [Use](#use)
1515
- [Setup](#setup)
1616
- [Basic use](#basic-use)
17-
- [Especific uses](#especific-uses)
1817
- [Support](#support)
1918
- [Contribute](#contribute)
2019
- [Questions and answers](#questions-and-answers)
@@ -36,7 +35,7 @@ ___
3635

3736
Since this package makes use of [image_picker](https://pub.dev/packages/image_picker) package, for platform specific setup, follow the instructions [here](https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker#installation)
3837

39-
### Basuc use
38+
### Basic use
4039

4140
```dart
4241
FormBuilder(
@@ -53,7 +52,22 @@ FormBuilder(
5352
),
5453
```
5554

56-
See [pud.dev example tab](https://pub.dev/packages/form_builder_image_picker/example) or [github code](example/lib/main.dart) for more details
55+
### Only specific pickers
56+
```dart
57+
FormBuilder(
58+
child: Column(
59+
mainAxisAlignment: MainAxisAlignment.center,
60+
children: <Widget>[
61+
FormBuilderImagePicker(
62+
name: 'noCamera',
63+
availableImageSources: const [ImageSourceOption.gallery],
64+
),
65+
],
66+
),
67+
),
68+
```
69+
70+
See [pub.dev example tab](https://pub.dev/packages/form_builder_image_picker/example) or [github code](example/lib/main.dart) for more details
5771

5872
## Support
5973

example/lib/main.dart

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import 'package:flutter/cupertino.dart';
12
import 'package:flutter/material.dart';
23
import 'package:flutter_form_builder/flutter_form_builder.dart';
34

@@ -26,6 +27,7 @@ class MyApp extends StatelessWidget {
2627
class ApiImage {
2728
final String imageUrl;
2829
final String id;
30+
2931
ApiImage({
3032
required this.imageUrl,
3133
required this.id,
@@ -138,6 +140,50 @@ class MyHomePage extends StatelessWidget {
138140
iconColor: Colors.white,
139141
icon: Icons.ac_unit_rounded,
140142
),
143+
const SizedBox(height: 15),
144+
FormBuilderImagePicker(
145+
name: 'onlyCamera',
146+
decoration: const InputDecoration(
147+
labelText: 'Pick Photos (only from camera)'),
148+
availableImageSources: const [ImageSourceOption.camera],
149+
),
150+
const SizedBox(height: 15),
151+
FormBuilderImagePicker(
152+
name: 'onlyGallery',
153+
decoration: const InputDecoration(
154+
labelText: 'Pick Photos (only from gallery)'),
155+
availableImageSources: const [ImageSourceOption.gallery],
156+
),
157+
FormBuilderImagePicker(
158+
decoration: const InputDecoration(
159+
labelText: 'Pick Photos (with custom view)'),
160+
name: "CupertinoActionSheet",
161+
optionsBuilder: (cameraPicker, galleryPicker) =>
162+
CupertinoActionSheet(
163+
title: const Text('Image'),
164+
message: const Text('Pick an image from given options'),
165+
actions: [
166+
CupertinoActionSheetAction(
167+
isDefaultAction: true,
168+
onPressed: () {
169+
cameraPicker();
170+
},
171+
child: const Text('Camera'),
172+
),
173+
CupertinoActionSheetAction(
174+
isDefaultAction: true,
175+
onPressed: () {
176+
galleryPicker();
177+
},
178+
child: const Text('Gallery'),
179+
)
180+
],
181+
),
182+
onTap: (child) => showCupertinoModalPopup(
183+
context: context,
184+
builder: (context) => child,
185+
),
186+
),
141187
ElevatedButton(
142188
child: const Text('Submit'),
143189
onPressed: () {

lib/form_builder_image_picker.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
library form_builder_image_picker;
22

33
export 'src/form_builder_image_picker.dart';
4+
export 'src/image_source_option.dart';

lib/src/form_builder_image_picker.dart

Lines changed: 50 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
55
import 'package:flutter_form_builder/flutter_form_builder.dart';
66
import 'package:image_picker/image_picker.dart';
77

8+
import 'image_source_option.dart';
89
import 'image_source_sheet.dart';
910

1011
/// Field for picking image(s) from Gallery or Camera.
@@ -95,6 +96,22 @@ class FormBuilderImagePicker extends FormBuilderField<List<dynamic>> {
9596
/// fit for each image
9697
final BoxFit fit;
9798

99+
/// The sources available to pick from.
100+
/// Either [ImageSourceOption.gallery], [ImageSourceOption.camera] or both.
101+
final List<ImageSourceOption> availableImageSources;
102+
103+
///A callback that returns a pickup options
104+
///ListTile(inside Wrap) by Default
105+
///use optionsBuilder to return a widget of your choice
106+
final ValueChanged<ImageSourceBottomSheet>? onTap;
107+
108+
/// use this callback if you want custom view for options
109+
/// call cameraPicker() to picks image from camera
110+
/// call galleryPicker() to picks image from gallery
111+
final Widget Function(
112+
FutureVoidCallBack cameraPicker, FutureVoidCallBack galleryPicker)?
113+
optionsBuilder;
114+
98115
FormBuilderImagePicker({
99116
Key? key,
100117
//From Super
@@ -135,6 +152,12 @@ class FormBuilderImagePicker extends FormBuilderField<List<dynamic>> {
135152
this.galleryLabel = const Text('Gallery'),
136153
this.bottomSheetPadding = EdgeInsets.zero,
137154
this.placeholderImage,
155+
this.onTap,
156+
this.optionsBuilder,
157+
this.availableImageSources = const [
158+
ImageSourceOption.camera,
159+
ImageSourceOption.gallery,
160+
],
138161
}) : assert(maxImages == null || maxImages >= 0),
139162
super(
140163
key: key,
@@ -188,29 +211,35 @@ class FormBuilderImagePicker extends FormBuilderField<List<dynamic>> {
188211
onTap: () async {
189212
final remainingImages =
190213
maxImages == null ? null : maxImages - value.length;
191-
await showModalBottomSheet<void>(
192-
context: state.context,
193-
builder: (_) {
194-
return ImageSourceBottomSheet(
195-
maxHeight: maxHeight,
196-
maxWidth: maxWidth,
197-
preventPop: preventPop,
198-
remainingImages: remainingImages,
199-
imageQuality: imageQuality,
200-
preferredCameraDevice: preferredCameraDevice,
201-
bottomSheetPadding: bottomSheetPadding,
202-
cameraIcon: cameraIcon,
203-
cameraLabel: cameraLabel,
204-
galleryIcon: galleryIcon,
205-
galleryLabel: galleryLabel,
206-
onImageSelected: (image) {
207-
state.requestFocus();
208-
field.didChange([...value, ...image]);
209-
Navigator.pop(state.context);
210-
},
211-
);
214+
215+
final imageSourceSheet = ImageSourceBottomSheet(
216+
maxHeight: maxHeight,
217+
maxWidth: maxWidth,
218+
preventPop: preventPop,
219+
remainingImages: remainingImages,
220+
imageQuality: imageQuality,
221+
preferredCameraDevice: preferredCameraDevice,
222+
bottomSheetPadding: bottomSheetPadding,
223+
cameraIcon: cameraIcon,
224+
cameraLabel: cameraLabel,
225+
galleryIcon: galleryIcon,
226+
galleryLabel: galleryLabel,
227+
optionsBuilder: optionsBuilder,
228+
availableImageSources: availableImageSources,
229+
onImageSelected: (image) {
230+
state.requestFocus();
231+
field.didChange([...value, ...image]);
232+
Navigator.pop(state.context);
212233
},
213234
);
235+
onTap != null
236+
? onTap(imageSourceSheet)
237+
: await showModalBottomSheet<void>(
238+
context: state.context,
239+
builder: (_) {
240+
return imageSourceSheet;
241+
},
242+
);
214243
},
215244
);
216245

lib/src/image_source_option.dart

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/// Options where a user can pick images from
2+
enum ImageSourceOption {
3+
/// From the device camera
4+
/// (via [ImageSource.camera])
5+
camera,
6+
7+
/// From the gallery (or local files on the web)
8+
/// (via [ImageSource.gallery])
9+
gallery
10+
}

lib/src/image_source_sheet.dart

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import 'package:flutter/material.dart';
22
import 'package:image_picker/image_picker.dart';
33

4+
import 'image_source_option.dart';
5+
6+
typedef FutureVoidCallBack = Future<void> Function();
7+
48
class ImageSourceBottomSheet extends StatefulWidget {
59
/// Optional maximum height of image
610
final double? maxHeight;
@@ -24,13 +28,21 @@ class ImageSourceBottomSheet extends StatefulWidget {
2428
/// Callback when an image is selected.
2529
final void Function(Iterable<XFile> files) onImageSelected;
2630

31+
/// The sources to create ListTiles for.
32+
/// Either [ImageSourceOption.gallery], [ImageSourceOption.camera] or both.
33+
final List<ImageSourceOption> availableImageSources;
34+
2735
final Widget? cameraIcon;
2836
final Widget? galleryIcon;
2937
final Widget? cameraLabel;
3038
final Widget? galleryLabel;
3139
final EdgeInsets? bottomSheetPadding;
3240
final bool preventPop;
3341

42+
final Widget Function(
43+
FutureVoidCallBack cameraPicker, FutureVoidCallBack galleryPicker)?
44+
optionsBuilder;
45+
3446
const ImageSourceBottomSheet({
3547
Key? key,
3648
this.remainingImages,
@@ -45,6 +57,8 @@ class ImageSourceBottomSheet extends StatefulWidget {
4557
this.cameraLabel,
4658
this.galleryLabel,
4759
this.bottomSheetPadding,
60+
this.optionsBuilder,
61+
required this.availableImageSources,
4862
}) : super(key: key);
4963

5064
@override
@@ -90,20 +104,28 @@ class ImageSourceBottomSheetState extends State<ImageSourceBottomSheet> {
90104

91105
@override
92106
Widget build(BuildContext context) {
107+
if (widget.optionsBuilder != null) {
108+
return widget.optionsBuilder!(
109+
() => _onPickImage(ImageSource.camera),
110+
() => _onPickImage(ImageSource.gallery),
111+
);
112+
}
93113
Widget res = Container(
94114
padding: widget.bottomSheetPadding,
95115
child: Wrap(
96116
children: <Widget>[
97-
ListTile(
98-
leading: widget.cameraIcon,
99-
title: widget.cameraLabel,
100-
onTap: () => _onPickImage(ImageSource.camera),
101-
),
102-
ListTile(
103-
leading: widget.galleryIcon,
104-
title: widget.galleryLabel,
105-
onTap: () => _onPickImage(ImageSource.gallery),
106-
),
117+
if (widget.availableImageSources.contains(ImageSourceOption.camera))
118+
ListTile(
119+
leading: widget.cameraIcon,
120+
title: widget.cameraLabel,
121+
onTap: () => _onPickImage(ImageSource.camera),
122+
),
123+
if (widget.availableImageSources.contains(ImageSourceOption.gallery))
124+
ListTile(
125+
leading: widget.galleryIcon,
126+
title: widget.galleryLabel,
127+
onTap: () => _onPickImage(ImageSource.gallery),
128+
),
107129
],
108130
),
109131
);

0 commit comments

Comments
 (0)