Skip to content

Commit

Permalink
impl image picker. close #99
Browse files Browse the repository at this point in the history
  • Loading branch information
goxiaoy committed Jul 12, 2023
1 parent 6dec18c commit 75ba741
Show file tree
Hide file tree
Showing 7 changed files with 310 additions and 110 deletions.
101 changes: 92 additions & 9 deletions lib/ui/elements/image.dart
Original file line number Diff line number Diff line change
@@ -1,29 +1,112 @@
import 'package:flutter/material.dart';
import 'package:flutter_survey_js/ui/video_widget.dart';
import 'package:path/path.dart' as p;

Widget urlToImage(String? link, {double width = 100, double height = 100}) {
Widget urlToImage(String? link, {double? width, double? height}) {
Widget inner = Container();
if (link == null) {
return SizedBox(
width: width,
height: height,
child: inner,
);
} else {
final url = Uri.tryParse(link);
if (url == null) {
return SizedBox(
width: width,
height: height,
child: inner,
);
}
if (url.data != null && url.data!.isBase64) {
return Image.memory(url.data!.contentAsBytes(), gaplessPlayback: true);
inner = Image.memory(url.data!.contentAsBytes(), gaplessPlayback: true);
} else if (url.isAbsolute) {
if (UrlTypeHelper.getType(url) == UrlType.video) {
inner = VideoWidget(uri: url);
} else {
inner = Image.network(link, gaplessPlayback: true);
}
}
if (url.isAbsolute) {
return Image.network(link, gaplessPlayback: true);
} else {
return SizedBox(
width: width,
height: height,
);

return SizedBox(
width: width,
height: height,
child: inner,
);
}
}

enum UrlType { image, video, unknown }

class UrlTypeHelper {
static final List<String> _imageTypes = [
'jpg',
'jpeg',
'jfif',
'pjpeg',
'pjp',
'png',
'svg',
'gif',
'apng',
'webp',
'avif'
];

static final List<String> _videoTypes = [
"3g2",
"3gp",
"aaf",
"asf",
"avchd",
"avi",
"drc",
"flv",
"m2v",
"m3u8",
"m4p",
"m4v",
"mkv",
"mng",
"mov",
"mp2",
"mp4",
"mpe",
"mpeg",
"mpg",
"mpv",
"mxf",
"nsv",
"ogg",
"ogv",
"qt",
"rm",
"rmvb",
"roq",
"svi",
"vob",
"webm",
"wmv",
"yuv"
];

static UrlType getType(Uri uri) {
try {
String extension = p.extension(uri.path).toLowerCase();
if (extension.isEmpty) {
return UrlType.unknown;
}

extension = extension.split('.').last;
if (_imageTypes.contains(extension)) {
return UrlType.image;
} else if (_videoTypes.contains(extension)) {
return UrlType.video;
}
} catch (e) {
return UrlType.unknown;
}
return UrlType.unknown;
}
}
192 changes: 93 additions & 99 deletions lib/ui/elements/image_picker.dart
Original file line number Diff line number Diff line change
@@ -1,99 +1,93 @@
// import 'package:flutter/material.dart';
// import 'package:flutter_colorpicker/flutter_colorpicker.dart';
// import 'package:flutter_survey_js_model/flutter_survey_js_model.dart' as s;
// import 'package:reactive_forms/reactive_forms.dart';
//
// import 'image.dart';
// import 'question_title.dart';
// import 'survey_element_factory.dart';
//
// final SurveyElementBuilder imagePickerBuilder =
// (context, element, {ElementConfiguration? configuration}) {
// return ImagePickerElement(
// formControlName: element.name!,
// element: element as s.ImagePicker,
// ).wrapQuestionTitle(element, hasTitle: hasTitle);
// };
//
// class ImagePickerElement extends StatelessWidget {
// final String formControlName;
// final s.ImagePicker element;
// const ImagePickerElement(
// {Key? key, required this.formControlName, required this.element})
// : super(key: key);
// @override
// Widget build(BuildContext context) {
// //TODO multiple select
// final choices = element.choices ?? [];
// return ReactiveBlockItemPicker<dynamic, s.ImageItemValue>(
// formControlName: formControlName,
// valueAccessor: _ValueAccessor(choices),
// items: choices.map<ItemBlockValue<s.ImageItemValue>>((e) {
// return ItemBlockValue<s.ImageItemValue>(
// value: e, label: (_) => Text(e.text ?? e.value?.toString() ?? ''));
// }).toList(),
// itemBuilder: (BuildContext context, ItemBlockValue<s.ImageItemValue> item,
// bool isCurrentItem, Function changeItem) {
// const color = Colors.grey;
// return Column(
// crossAxisAlignment: CrossAxisAlignment.center,
// mainAxisAlignment: MainAxisAlignment.start,
// children: [
// Container(
// height: 100,
// width: 100,
// child: Center(
// child: Material(
// color: Colors.transparent,
// child: InkWell(
// onTap: changeItem as void Function()?,
// child: Stack(
// children: [
// urlToImage(item.value.imageLink, width: 90, height: 90),
// AnimatedOpacity(
// duration: const Duration(milliseconds: 210),
// opacity: isCurrentItem ? 1.0 : 0.0,
// child: Icon(
// Icons.done,
// color: useWhiteForeground(color)
// ? Colors.white
// : Colors.black,
// ),
// )
// ],
// ),
// ),
// ),
// ),
// ),
// Flexible(
// child: Container(
// padding: EdgeInsets.only(top: 2), child: item.label(context)),
// ),
// ],
// );
// },
// );
// }
// }
//
// class _ValueAccessor extends ControlValueAccessor<dynamic, s.ImageItemValue> {
// final List<s.ImageItemValue> choices;
//
// _ValueAccessor(this.choices);
//
// @override
// s.ImageItemValue? modelToViewValue(dynamic modelValue) {
// if (modelValue == null) {
// return null;
// }
// return choices.cast<s.ImageItemValue?>().firstWhere(
// (element) => element!.value == modelValue,
// orElse: () => null);
// }
//
// @override
// dynamic viewToModelValue(s.ImageItemValue? viewValue) {
// return viewValue?.value;
// }
// }
import 'package:flutter/material.dart';
import 'package:flutter_colorpicker/flutter_colorpicker.dart';
import 'package:flutter_survey_js/ui/reactive/reactive_grid_item_picker.dart';
import 'package:flutter_survey_js_model/flutter_survey_js_model.dart' as s;
import 'package:reactive_forms/reactive_forms.dart';
import 'package:flutter_survey_js/ui/survey_configuration.dart';
import 'image.dart';

Widget imagePickerBuilder(BuildContext context, s.Elementbase element,
{ElementConfiguration? configuration}) {
return ImagePickerElement(
formControlName: element.name!,
element: element as s.Imagepicker,
).wrapQuestionTitle(context, element, configuration: configuration);
}

class ImagePickerElement extends StatelessWidget {
final String formControlName;
final s.Imagepicker element;
const ImagePickerElement(
{Key? key, required this.formControlName, required this.element})
: super(key: key);
@override
Widget build(BuildContext context) {
//TODO multiple select
final choices = element.choices
?.map((p) => p.castToImageitemvalue())
.where((p) => p != null)
.cast<s.Imageitemvalue>()
.toList() ??
[];

return ReactiveGridItemPicker<Object, s.Imageitemvalue>(
formControlName: formControlName,
valueAccessor: _ValueAccessor(choices),
items: choices,
builder: (s.Imageitemvalue item, bool isCurrentItem,
void Function() changeItem) {
Color? foregroundColor;
if (isCurrentItem) {
foregroundColor =
Theme.of(context).colorScheme.primary.withOpacity(0.4);
}
return InkWell(
onTap: changeItem,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Stack(
alignment: Alignment.center,
children: [
Container(
foregroundDecoration: BoxDecoration(
color: foregroundColor,
),
child: urlToImage(item.imageLink,
width: element.imageWidth?.realValue,
height: element.imageHeight?.realValue)),
if (isCurrentItem) const Icon(Icons.done)
],
),
Flexible(
child: Container(
padding: EdgeInsets.only(top: 2),
child: Text(item.text ?? item.value?.toString() ?? "")),
),
],
));
},
);
}
}

class _ValueAccessor extends ControlValueAccessor<Object, s.Imageitemvalue> {
final List<s.Imageitemvalue> choices;

_ValueAccessor(this.choices);

@override
s.Imageitemvalue? modelToViewValue(dynamic modelValue) {
if (modelValue == null) {
return null;
}
return choices.cast<s.Imageitemvalue?>().firstWhere(
(element) => element?.value?.value == modelValue,
orElse: () => null);
}

@override
dynamic viewToModelValue(s.Imageitemvalue? viewValue) {
return viewValue?.value?.value;
}
}
40 changes: 40 additions & 0 deletions lib/ui/reactive/reactive_grid_item_picker.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import 'package:flutter/widgets.dart';
import 'package:reactive_forms/reactive_forms.dart';

typedef GridItemBuilder<T, V> = Widget Function(
V item, bool isCurrentValue, void Function() changeItem);

class ReactiveGridItemPicker<T, V> extends ReactiveFocusableFormField<T, V> {
ReactiveGridItemPicker(
{Key? key,
String? formControlName,
FormControl<T>? formControl,
Map<String, ValidationMessageFunction>? validationMessages,
ShowErrorsFunction? showErrors,
required ControlValueAccessor<T, V> valueAccessor,
required List<V> items,
required GridItemBuilder<T, V> builder})
: super(
key: key,
formControl: formControl,
formControlName: formControlName,
validationMessages: validationMessages,
showErrors: showErrors,
valueAccessor: valueAccessor,
builder: (field) {
return Column(
mainAxisSize: MainAxisSize.min,
children: items.map((e) {
final isCurrent = field.valueAccessor.viewToModelValue(e) ==
field.valueAccessor.viewToModelValue(field.value);
return builder(e, isCurrent, () {
if (isCurrent) {
field.didChange(null);
} else {
field.didChange(e);
}
});
}).toList(),
);
});
}
4 changes: 2 additions & 2 deletions lib/ui/survey_element_factory.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import 'package:flutter_survey_js/ui/elements/boolean.dart';
import 'package:flutter_survey_js/ui/elements/comment.dart';
import 'package:flutter_survey_js/ui/elements/matrix_dropdown.dart';
import 'package:flutter_survey_js/ui/elements/panel.dart';
import 'package:flutter_survey_js/ui/reactive/always_update_form_array.dart';
import 'package:flutter_survey_js/ui/reactive/reactive.dart';
import 'package:flutter_survey_js/ui/reactive/reactive_signature_string.dart';
import 'package:flutter_survey_js/ui/survey_configuration.dart';
Expand All @@ -16,6 +15,7 @@ import 'package:reactive_forms/reactive_forms.dart';
import 'elements/checkbox.dart';
import 'elements/dropdown.dart';
import 'elements/image.dart';
import 'elements/image_picker.dart';
import 'elements/matrix.dart';
import 'elements/matrix_dropdown_base.dart';
import 'elements/matrix_dynamic.dart';
Expand Down Expand Up @@ -140,7 +140,7 @@ class SurveyElementFactory {
return urlToImage((element as s.Image).imageLink)
.wrapQuestionTitle(context, element, configuration: configuration);
});
// register<s.ImagePicker>(imagePickerBuilder);
register<s.Imagepicker>(imagePickerBuilder);

register<s.Dropdown>(dropdownBuilder,
control: (context, element, {validators = const [], value}) =>
Expand Down
Loading

0 comments on commit 75ba741

Please sign in to comment.