Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FormBuilderImagePicker Widget. :) #250

Merged
merged 8 commits into from
May 1, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ The currently supported fields include:
email, urls etc by using different configurations and validators
* `FormBuilderTouchSpin` - Selection of a number by tapping on a plus or minus icon
* `FormBuilderTypeAhead` - Auto-completes user input from a list of items
* `FormBuilderImagePicker` - Picker a image from Gallery or Camera and stores it in a List of images, File or String.

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:

Expand Down
2 changes: 1 addition & 1 deletion example/.flutter-plugins-dependencies
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"_info":"// This is a generated file; do not edit or check into version control.","dependencyGraph":[{"name":"flutter_keyboard_visibility","dependencies":[]}]}
{"_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"]}]}
12 changes: 12 additions & 0 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,18 @@ class MyHomePageState extends State<MyHomePage> {
},
),
),
FormBuilderImagePicker(
attribute: "images",
validators: [
FormBuilderValidators.required(),
(images) {
if (images.length < 2) {
return "Two or more images is required.";
}
return null;
}
],
),
FormBuilderSignaturePad(
decoration: InputDecoration(labelText: "Signature"),
attribute: "signature",
Expand Down
4 changes: 1 addition & 3 deletions flutter_form_builder.iml
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,11 @@
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/libphonenumber/example/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/libphonenumber/example/.pub" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/libphonenumber/example/build" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/Flutter/App.framework/flutter_assets/packages" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/Flutter/flutter_assets/packages" />
</content>
<orderEntry type="jdk" jdkName="Android API 25 Platform" jdkType="Android SDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Dart Packages" level="project" />
<orderEntry type="library" name="Dart SDK" level="project" />
<orderEntry type="library" name="Flutter Plugins" level="project" />
<orderEntry type="library" name="Dart Packages" level="project" />
</component>
</module>
1 change: 1 addition & 0 deletions lib/flutter_form_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,6 @@ export './src/fields/form_builder_text_field.dart';
export './src/fields/form_builder_touch_spin.dart';
export './src/fields/form_builder_typeahead.dart';
export './src/fields/form_builder_signature_pad.dart';
export './src/fields/form_builder_image_picker.dart';

export 'package:flutter_typeahead/flutter_typeahead.dart';
152 changes: 152 additions & 0 deletions lib/src/fields/form_builder_image_picker.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import 'package:flutter/material.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart';
import 'package:flutter_form_builder/src/widgets/image_source_sheet.dart';

class FormBuilderImagePicker extends StatefulWidget {
final String attribute;
final List<FormFieldValidator> validators;
final List initialValue;
final bool readOnly;
final String labelText;
final ValueTransformer valueTransformer;
final ValueChanged onChanged;

final double imageWidth;
final double imageHeight;
final EdgeInsets imageMargin;
final FormFieldSetter onSaved;

const FormBuilderImagePicker({
Key key,
@required this.attribute,
this.initialValue,
this.validators = const [],
this.valueTransformer,
this.labelText,
this.onChanged,
this.imageWidth = 130,
this.imageHeight = 130,
this.imageMargin,
this.readOnly = false,
this.onSaved
}) : super(key: key);

@override
_FormBuilderImagePickerState createState() => _FormBuilderImagePickerState();
}

class _FormBuilderImagePickerState extends State<FormBuilderImagePicker> {
bool _readOnly = false;
List _initialValue;
final GlobalKey<FormFieldState> _fieldKey = GlobalKey<FormFieldState>();
FormBuilderState _formState;

@override
void initState() {
_formState = FormBuilder.of(context);
_formState?.registerFieldKey(widget.attribute, _fieldKey);
_initialValue = List.of(widget.initialValue ??
(_formState.initialValue.containsKey(widget.attribute)
? _formState.initialValue[widget.attribute]
: []));
super.initState();
}

@override
void dispose() {
_formState?.unregisterFieldKey(widget.attribute);
super.dispose();
}

@override
Widget build(BuildContext context) {
_readOnly = (_formState?.readOnly == true) ? true : widget.readOnly;

return FormField<List>(
key: _fieldKey,
enabled: !_readOnly,
initialValue: _initialValue,
validator: (val) {
for (int i = 0; i < widget.validators.length; i++) {
if (widget.validators[i](val) != null)
return widget.validators[i](val);
}
return null;
},
onSaved: (val) {
var transformed;
if (widget.valueTransformer != null) {
transformed = widget.valueTransformer(val);
_formState?.setAttributeValue(widget.attribute, transformed);
} else
_formState?.setAttributeValue(widget.attribute, val);
if (widget.onSaved != null) {
widget.onSaved(transformed ?? val);
}
},
builder: (state) => Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
widget.labelText != null ? widget.labelText : 'Images',
style: TextStyle(
color: _readOnly ? Theme.of(context).disabledColor : Theme.of(context).primaryColor
),
),
SizedBox(
height: 8,
),
Container(
height: widget.imageHeight,
child: ListView(
scrollDirection: Axis.horizontal,
children: state.value.map<Widget>((item) {
return Container(
width: widget.imageWidth,
height: widget.imageHeight,
margin: widget.imageMargin,
child: GestureDetector(
child: item is String ?
Image.network(item, fit: BoxFit.cover) :
Image.file(item, fit: BoxFit.cover),
onLongPress: _readOnly ? null : () {
state.didChange(state.value..remove(item));
},
),
);
}).toList()
..add(
GestureDetector(
child: Container(
width: widget.imageWidth,
height: widget.imageHeight,
child: Icon(
Icons.camera_enhance,
color: _readOnly ? Theme.of(context).disabledColor : Theme.of(context).primaryColor
),
color: (_readOnly ? Theme.of(context).disabledColor : Theme.of(context).primaryColor).withAlpha(50)
),
onTap: _readOnly ? null : () {
showModalBottomSheet(context: context, builder: (_) {
return ImageSourceSheet(
onImageSelected: (image) {
state.didChange(state.value..add(image));
Navigator.of(context).pop();
},
);
});
},
)
)
),
),
state.hasError
? Text(
state.errorText,
style: TextStyle(color: Theme.of(context).errorColor, fontSize: 12),
) : Container()
],
),
);
}
}
43 changes: 43 additions & 0 deletions lib/src/widgets/image_source_sheet.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';

class ImageSourceSheet extends StatelessWidget {

final Function(File) onImageSelected;

ImageSourceSheet({ @required this.onImageSelected });

void imageSelected(File image) async {
if (image != null) {
onImageSelected(image);
}
}

@override
Widget build(BuildContext context) {
return Container(
child: Wrap(
children: <Widget>[
ListTile(
leading: Icon(Icons.camera_enhance),
title: Text('Camera'),
onTap: () async {
File image = await ImagePicker.pickImage(source: ImageSource.camera);
imageSelected(image);
},
),
ListTile(
leading: Icon(Icons.image),
title: Text('Gallery'),
onTap: () async {
File image = await ImagePicker.pickImage(source: ImageSource.gallery);
imageSelected(image);
},
)
],
),
);
}
}
16 changes: 15 additions & 1 deletion pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.7.0"
flutter_plugin_android_lifecycle:
dependency: transitive
description:
name: flutter_plugin_android_lifecycle
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.6"
flutter_test:
dependency: "direct dev"
description: flutter
Expand Down Expand Up @@ -186,6 +193,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.4"
image_picker:
dependency: "direct main"
description:
name: image_picker
url: "https://pub.dartlang.org"
source: hosted
version: "0.6.4"
intl:
dependency: "direct main"
description:
Expand Down Expand Up @@ -480,4 +494,4 @@ packages:
version: "2.1.15"
sdks:
dart: ">=2.6.0 <3.0.0"
flutter: ">=1.5.4 <2.0.0"
flutter: ">=1.12.13 <2.0.0"
1 change: 1 addition & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ dependencies:
validators: ^2.0.0+1
date_range_picker: ^1.0.6
flutter_touch_spin: ^1.0.1
image_picker: ^0.6.4

dev_dependencies:
flutter_test:
Expand Down