segini dulu lah input file

This commit is contained in:
Golek 2022-12-07 15:58:09 +07:00
parent 521c54dfa8
commit abbd0d8cc8
5 changed files with 40 additions and 79 deletions

View File

@ -12,7 +12,7 @@ import '_dashed_rect.dart';
class InputFile extends StatefulWidget { class InputFile extends StatefulWidget {
const InputFile({ const InputFile({
Key? key, Key? key,
this.files = const [], this.files,
required this.onChange, required this.onChange,
this.allowMultiple = false, this.allowMultiple = false,
this.allowedExtensions = const ['jpg', 'jpeg', 'png', 'gif'], this.allowedExtensions = const ['jpg', 'jpeg', 'png', 'gif'],
@ -24,7 +24,7 @@ class InputFile extends StatefulWidget {
this.errorMessage, this.errorMessage,
}) : super(key: key); }) : super(key: key);
final List<File> files; final List<File>? files;
final void Function(List<File> files) onChange; final void Function(List<File> files) onChange;
final bool? allowMultiple; final bool? allowMultiple;
final List<String>? allowedExtensions; final List<String>? allowedExtensions;
@ -41,37 +41,27 @@ class InputFile extends StatefulWidget {
class _InputFileState extends State<InputFile> { class _InputFileState extends State<InputFile> {
List<File> files = []; List<File> files = [];
StreamController<List<File>> controller = StreamController();
late Stream<List<File>> stream;
void addFileToSink(List<File> addedFile) { void addFileToSink(List<File> addedFile) {
controller.sink.add(files);
files = addedFile; files = addedFile;
widget.onChange.call(files);
} }
void removeFile(File file) { void removeFile(File file) {
files.removeAt(files.indexOf(file)); setState(() {
controller.sink.add(files); files.removeAt(files.indexOf(file));
} widget.onChange.call(files);
});
void close() {
controller.close();
} }
@override @override
void initState() { void initState() {
super.initState(); super.initState();
// tambahkan initial component file value // tambahkan initial component file value
files = widget.files; if (widget.files != null) {
addFileToSink(files); files = widget.files!;
// dapatkan stream dan listen ke onchange addFileToSink(files);
stream = controller.stream; }
}
@override
void dispose() {
close();
super.dispose();
} }
/// ketika user memilih menggunakan camera saat input file /// ketika user memilih menggunakan camera saat input file
@ -121,8 +111,7 @@ class _InputFileState extends State<InputFile> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final theme = GolekTheme.of(context); final theme = GolekTheme.of(context);
print("INPUT FILE"); print("INPUT FILE");
print(widget.isError!); print(files);
print(widget.isTouched!);
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -147,11 +136,7 @@ class _InputFileState extends State<InputFile> {
children: [ children: [
const SizedBox(height: 21), const SizedBox(height: 21),
OutlinedButton( OutlinedButton(
onPressed: () { onPressed: () => showModalFileToPick(
setState(() {
});
showModalFileToPick(
context: context, context: context,
onPickCamera: (context) async { onPickCamera: (context) async {
await pickFileFromCamera(context); await pickFileFromCamera(context);
@ -161,8 +146,7 @@ class _InputFileState extends State<InputFile> {
await pickFileFromGallery(context); await pickFileFromGallery(context);
Navigator.of(context).pop(); Navigator.of(context).pop();
}, },
); ),
},
style: OutlinedButton.styleFrom( style: OutlinedButton.styleFrom(
side: BorderSide( side: BorderSide(
width: 1, width: 1,
@ -181,16 +165,10 @@ class _InputFileState extends State<InputFile> {
), ),
), ),
), ),
StreamBuilder( ListFilesBuilder(
stream: stream, files: files,
builder: (context, snapshot) { multiple: widget.allowMultiple!,
widget.onChange.call(files); onRemoveFile: removeFile,
return ListFilesBuilder(
files: files,
multiple: widget.allowMultiple!,
onRemoveFile: removeFile,
);
},
), ),
const SizedBox(height: 5), const SizedBox(height: 5),
Text( Text(

View File

@ -27,17 +27,16 @@ class _LandingPageState extends State<LandingPage> {
void dispose() { void dispose() {
super.dispose(); super.dispose();
} }
final form = FormGroup({
'file_pem': FormControl<List<File>>(
value: [],
validators: [requiredFile],
),
});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final theme = GolekTheme.of(context); final theme = GolekTheme.of(context);
final form = FormGroup({
'foto': FormControl<List<File>>(
value: [],
validators: [requiredFile],
),
});
return Scaffold( return Scaffold(
appBar: LandingAppBar(), appBar: LandingAppBar(),
body: SafeArea( body: SafeArea(
@ -46,25 +45,22 @@ class _LandingPageState extends State<LandingPage> {
child: Column( child: Column(
children: [ children: [
ReactiveValueListenableBuilder( ReactiveValueListenableBuilder(
formControlName: 'foto', formControlName: 'file_pem',
builder: (context, control, child) { builder: (context, control, child) {
print('DEBUG DI VALUE LISTEN');
print(control.value);
print(control.touched);
print(control.errors.entries);
return Container(); return Container();
}, },
), ),
ReactiveInputFile( ReactiveInputFile(
name: 'foto', name: 'file_pem',
allowMultiple: true, allowMultiple: true,
), ),
ElevatedButton( ElevatedButton(
onPressed: () { onPressed: () {
form.markAllAsTouched(); form.markAllAsTouched();
print(form.control('foto').hasErrors); print(form.control('file_pem').value);
print(form.control('foto').errors); print(form.control('file_pem').hasErrors);
print(form.control('foto').touched); print(form.control('file_pem').errors);
print(form.control('file_pem').touched);
if (form.valid){ if (form.valid){
print("FORM IS VALID"); print("FORM IS VALID");
} }

View File

@ -12,20 +12,14 @@ class ErrorMessages {
Map<String, String>? widgetCustomMessages, Map<String, String>? widgetCustomMessages,
}) { }) {
Map<String, Object>? mergedErrorMessages = { Map<String, Object>? mergedErrorMessages = {
...?widgetCustomMessages,
...messages, ...messages,
...?widgetCustomMessages,
}; };
MapEntry<String, dynamic> message = const MapEntry('', ''); final message = mergedErrorMessages.entries.firstWhere(
print('GET UI ERROR '); (element) => control.errors.entries.isNotEmpty && element.key == control.errors.entries.first.key,
print(control.errors); orElse: () => const MapEntry('', ''),
if (control.errors.entries.isNotEmpty) { );
message = mergedErrorMessages.entries.firstWhere(
(element) => element.key == control.errors.entries.first.key,
orElse: () => const MapEntry('', ''),
);
}
return message.value.toString().replaceFirst('{_field_}', label); return message.value.toString().replaceFirst('{_field_}', label);
} }
} }

View File

@ -1,13 +1,11 @@
import 'dart:io'; import 'dart:io';
import 'package:component_library/component_library.dart'; import 'package:component_library/component_library.dart';
import 'package:flutter/material.dart';
import 'package:reactive_forms/reactive_forms.dart'; import 'package:reactive_forms/reactive_forms.dart';
import 'error_messages.dart'; import 'error_messages.dart';
class ReactiveInputFile extends ReactiveFormField<List<File>, List<File>> { class ReactiveInputFile extends ReactiveFormField<List<File>, List<File>> {
ReactiveInputFile({ ReactiveInputFile({
required String name, required String name,
allowMultiple = false, allowMultiple = false,
@ -19,25 +17,20 @@ class ReactiveInputFile extends ReactiveFormField<List<File>, List<File>> {
formControlName: name, formControlName: name,
builder: (field) { builder: (field) {
return InputFile( return InputFile(
files: field.value ?? [], files: field.value,
allowMultiple: allowMultiple, allowMultiple: allowMultiple,
descriptionLabel: descriptionLabel, descriptionLabel: descriptionLabel,
guidanceLabel: guidanceLabel, guidanceLabel: guidanceLabel,
uploadButtonLabel: uploadButtonLabel, uploadButtonLabel: uploadButtonLabel,
allowedExtensions: allowedExtensions, allowedExtensions: allowedExtensions,
errorMessage: ErrorMessages.getUiErrorMessage( errorMessage: ErrorMessages.getUiErrorMessage(
control: field.control, control: field.control,
label: '', label: '',
widgetCustomMessages: { widgetCustomMessages: {'required': 'Foto harus diisi'},
'requiredFile': 'Foto harus diisi'
}
), ),
isTouched: field.control.touched, isTouched: field.control.touched,
isError: field.control.hasErrors, isError: field.control.hasErrors,
onChange: (files) { onChange: (files) {
print('ON CAHNEF');
print(field.control.touched);
print(field.control.hasErrors);
field.didChange(files); field.didChange(files);
}, },
); );

View File

@ -3,5 +3,5 @@ import 'package:reactive_forms/reactive_forms.dart';
Map<String, dynamic>? requiredFile(AbstractControl<dynamic> control) { Map<String, dynamic>? requiredFile(AbstractControl<dynamic> control) {
return control.isNotNull && control.value?.isNotEmpty == true return control.isNotNull && control.value?.isNotEmpty == true
? null ? null
: {'requiredFile': true}; : {'required': true};
} }