Skip to content

Commit 94a3f98

Browse files
committed
Merge remote-tracking branch 'origin/master'
# Conflicts: # .idea/libraries/Flutter_Plugins.xml # .idea/workspace.xml
2 parents f110007 + e040399 commit 94a3f98

File tree

8 files changed

+211
-2
lines changed

8 files changed

+211
-2
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ The currently supported fields include:
212212
email, urls etc by using different configurations and validators
213213
* `FormBuilderTouchSpin` - Selection of a number by tapping on a plus or minus icon
214214
* `FormBuilderTypeAhead` - Auto-completes user input from a list of items
215+
* `FormBuilderImagePicker` - Picker a image from Gallery or Camera and stores it in a List of images, File or String. **Note**: This picker is available for iOS and Android.
215216

216217
In order to create an input field in the form, along with the label, and any applicable validation, there are several attributes that are supported by all types of inputs namely:
217218

example/.flutter-plugins-dependencies

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"_info":"// This is a generated file; do not edit or check into version control.","dependencyGraph":[{"name":"flutter_keyboard_visibility","dependencies":[]}]}
1+
{"_info":"// This is a generated file; do not edit or check into version control.","dependencyGraph":[{"name":"flutter_keyboard_visibility","dependencies":[]},{"name":"flutter_plugin_android_lifecycle","dependencies":[]},{"name":"image_picker","dependencies":["flutter_plugin_android_lifecycle"]}]}

example/lib/main.dart

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,18 @@ class MyHomePageState extends State<MyHomePage> {
511511
},
512512
),
513513
),
514+
FormBuilderImagePicker(
515+
attribute: "images",
516+
validators: [
517+
FormBuilderValidators.required(),
518+
(images) {
519+
if (images.length < 2) {
520+
return "Two or more images is required.";
521+
}
522+
return null;
523+
}
524+
],
525+
),
514526
FormBuilderSignaturePad(
515527
decoration: InputDecoration(labelText: "Signature"),
516528
attribute: "signature",

flutter_form_builder.iml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@
4242
</content>
4343
<orderEntry type="jdk" jdkName="Android API 25 Platform" jdkType="Android SDK" />
4444
<orderEntry type="sourceFolder" forTests="false" />
45-
<orderEntry type="library" name="Dart Packages" level="project" />
4645
<orderEntry type="library" name="Dart SDK" level="project" />
4746
<orderEntry type="library" name="Flutter Plugins" level="project" />
4847
</component>

lib/flutter_form_builder.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,6 @@ export './src/fields/form_builder_text_field.dart';
2424
export './src/fields/form_builder_touch_spin.dart';
2525
export './src/fields/form_builder_typeahead.dart';
2626
export './src/fields/form_builder_signature_pad.dart';
27+
export './src/fields/form_builder_image_picker.dart';
2728

2829
export 'package:flutter_typeahead/flutter_typeahead.dart';
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:flutter_form_builder/flutter_form_builder.dart';
3+
import 'package:flutter_form_builder/src/widgets/image_source_sheet.dart';
4+
5+
class FormBuilderImagePicker extends StatefulWidget {
6+
final String attribute;
7+
final List<FormFieldValidator> validators;
8+
final List initialValue;
9+
final bool readOnly;
10+
final String labelText;
11+
final ValueTransformer valueTransformer;
12+
final ValueChanged onChanged;
13+
14+
final double imageWidth;
15+
final double imageHeight;
16+
final EdgeInsets imageMargin;
17+
final FormFieldSetter onSaved;
18+
19+
const FormBuilderImagePicker({
20+
Key key,
21+
@required this.attribute,
22+
this.initialValue,
23+
this.validators = const [],
24+
this.valueTransformer,
25+
this.labelText,
26+
this.onChanged,
27+
this.imageWidth = 130,
28+
this.imageHeight = 130,
29+
this.imageMargin,
30+
this.readOnly = false,
31+
this.onSaved
32+
}) : super(key: key);
33+
34+
@override
35+
_FormBuilderImagePickerState createState() => _FormBuilderImagePickerState();
36+
}
37+
38+
class _FormBuilderImagePickerState extends State<FormBuilderImagePicker> {
39+
bool _readOnly = false;
40+
List _initialValue;
41+
final GlobalKey<FormFieldState> _fieldKey = GlobalKey<FormFieldState>();
42+
FormBuilderState _formState;
43+
44+
@override
45+
void initState() {
46+
_formState = FormBuilder.of(context);
47+
_formState?.registerFieldKey(widget.attribute, _fieldKey);
48+
_initialValue = List.of(widget.initialValue ??
49+
(_formState.initialValue.containsKey(widget.attribute)
50+
? _formState.initialValue[widget.attribute]
51+
: []));
52+
super.initState();
53+
}
54+
55+
@override
56+
void dispose() {
57+
_formState?.unregisterFieldKey(widget.attribute);
58+
super.dispose();
59+
}
60+
61+
@override
62+
Widget build(BuildContext context) {
63+
_readOnly = (_formState?.readOnly == true) ? true : widget.readOnly;
64+
65+
return FormField<List>(
66+
key: _fieldKey,
67+
enabled: !_readOnly,
68+
initialValue: _initialValue,
69+
validator: (val) {
70+
for (int i = 0; i < widget.validators.length; i++) {
71+
if (widget.validators[i](val) != null)
72+
return widget.validators[i](val);
73+
}
74+
return null;
75+
},
76+
onSaved: (val) {
77+
var transformed;
78+
if (widget.valueTransformer != null) {
79+
transformed = widget.valueTransformer(val);
80+
_formState?.setAttributeValue(widget.attribute, transformed);
81+
} else
82+
_formState?.setAttributeValue(widget.attribute, val);
83+
if (widget.onSaved != null) {
84+
widget.onSaved(transformed ?? val);
85+
}
86+
},
87+
builder: (state) => Column(
88+
crossAxisAlignment: CrossAxisAlignment.start,
89+
children: <Widget>[
90+
Text(
91+
widget.labelText != null ? widget.labelText : 'Images',
92+
style: TextStyle(
93+
color: _readOnly ? Theme.of(context).disabledColor : Theme.of(context).primaryColor
94+
),
95+
),
96+
SizedBox(
97+
height: 8,
98+
),
99+
Container(
100+
height: widget.imageHeight,
101+
child: ListView(
102+
scrollDirection: Axis.horizontal,
103+
children: state.value.map<Widget>((item) {
104+
return Container(
105+
width: widget.imageWidth,
106+
height: widget.imageHeight,
107+
margin: widget.imageMargin,
108+
child: GestureDetector(
109+
child: item is String ?
110+
Image.network(item, fit: BoxFit.cover) :
111+
Image.file(item, fit: BoxFit.cover),
112+
onLongPress: _readOnly ? null : () {
113+
state.didChange(state.value..remove(item));
114+
},
115+
),
116+
);
117+
}).toList()
118+
..add(
119+
GestureDetector(
120+
child: Container(
121+
width: widget.imageWidth,
122+
height: widget.imageHeight,
123+
child: Icon(
124+
Icons.camera_enhance,
125+
color: _readOnly ? Theme.of(context).disabledColor : Theme.of(context).primaryColor
126+
),
127+
color: (_readOnly ? Theme.of(context).disabledColor : Theme.of(context).primaryColor).withAlpha(50)
128+
),
129+
onTap: _readOnly ? null : () {
130+
showModalBottomSheet(context: context, builder: (_) {
131+
return ImageSourceSheet(
132+
onImageSelected: (image) {
133+
state.didChange(state.value..add(image));
134+
Navigator.of(context).pop();
135+
},
136+
);
137+
});
138+
},
139+
)
140+
)
141+
),
142+
),
143+
state.hasError
144+
? Text(
145+
state.errorText,
146+
style: TextStyle(color: Theme.of(context).errorColor, fontSize: 12),
147+
) : Container()
148+
],
149+
),
150+
);
151+
}
152+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import 'dart:io';
2+
3+
import 'package:flutter/material.dart';
4+
import 'package:image_picker/image_picker.dart';
5+
6+
class ImageSourceSheet extends StatelessWidget {
7+
8+
final Function(File) onImageSelected;
9+
10+
ImageSourceSheet({ @required this.onImageSelected });
11+
12+
void imageSelected(File image) async {
13+
if (image != null) {
14+
onImageSelected(image);
15+
}
16+
}
17+
18+
@override
19+
Widget build(BuildContext context) {
20+
return Container(
21+
child: Wrap(
22+
children: <Widget>[
23+
ListTile(
24+
leading: Icon(Icons.camera_enhance),
25+
title: Text('Camera'),
26+
onTap: () async {
27+
File image = await ImagePicker.pickImage(source: ImageSource.camera);
28+
imageSelected(image);
29+
},
30+
),
31+
ListTile(
32+
leading: Icon(Icons.image),
33+
title: Text('Gallery'),
34+
onTap: () async {
35+
File image = await ImagePicker.pickImage(source: ImageSource.gallery);
36+
imageSelected(image);
37+
},
38+
)
39+
],
40+
),
41+
);
42+
}
43+
}

pubspec.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ dependencies:
2121
validators: ^2.0.0+1
2222
date_range_picker: ^1.0.6
2323
flutter_touch_spin: ^1.0.1
24+
image_picker: ^0.6.4
2425

2526
dev_dependencies:
2627
flutter_test:

0 commit comments

Comments
 (0)