segini dulu lah input file
This commit is contained in:
parent
521c54dfa8
commit
abbd0d8cc8
|
@ -12,7 +12,7 @@ import '_dashed_rect.dart';
|
|||
class InputFile extends StatefulWidget {
|
||||
const InputFile({
|
||||
Key? key,
|
||||
this.files = const [],
|
||||
this.files,
|
||||
required this.onChange,
|
||||
this.allowMultiple = false,
|
||||
this.allowedExtensions = const ['jpg', 'jpeg', 'png', 'gif'],
|
||||
|
@ -24,7 +24,7 @@ class InputFile extends StatefulWidget {
|
|||
this.errorMessage,
|
||||
}) : super(key: key);
|
||||
|
||||
final List<File> files;
|
||||
final List<File>? files;
|
||||
final void Function(List<File> files) onChange;
|
||||
final bool? allowMultiple;
|
||||
final List<String>? allowedExtensions;
|
||||
|
@ -41,37 +41,27 @@ class InputFile extends StatefulWidget {
|
|||
|
||||
class _InputFileState extends State<InputFile> {
|
||||
List<File> files = [];
|
||||
StreamController<List<File>> controller = StreamController();
|
||||
late Stream<List<File>> stream;
|
||||
|
||||
void addFileToSink(List<File> addedFile) {
|
||||
controller.sink.add(files);
|
||||
files = addedFile;
|
||||
widget.onChange.call(files);
|
||||
}
|
||||
|
||||
void removeFile(File file) {
|
||||
files.removeAt(files.indexOf(file));
|
||||
controller.sink.add(files);
|
||||
}
|
||||
|
||||
void close() {
|
||||
controller.close();
|
||||
setState(() {
|
||||
files.removeAt(files.indexOf(file));
|
||||
widget.onChange.call(files);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
// tambahkan initial component file value
|
||||
files = widget.files;
|
||||
addFileToSink(files);
|
||||
// dapatkan stream dan listen ke onchange
|
||||
stream = controller.stream;
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
close();
|
||||
super.dispose();
|
||||
if (widget.files != null) {
|
||||
files = widget.files!;
|
||||
addFileToSink(files);
|
||||
}
|
||||
}
|
||||
|
||||
/// ketika user memilih menggunakan camera saat input file
|
||||
|
@ -121,8 +111,7 @@ class _InputFileState extends State<InputFile> {
|
|||
Widget build(BuildContext context) {
|
||||
final theme = GolekTheme.of(context);
|
||||
print("INPUT FILE");
|
||||
print(widget.isError!);
|
||||
print(widget.isTouched!);
|
||||
print(files);
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
|
@ -147,11 +136,7 @@ class _InputFileState extends State<InputFile> {
|
|||
children: [
|
||||
const SizedBox(height: 21),
|
||||
OutlinedButton(
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
|
||||
});
|
||||
showModalFileToPick(
|
||||
onPressed: () => showModalFileToPick(
|
||||
context: context,
|
||||
onPickCamera: (context) async {
|
||||
await pickFileFromCamera(context);
|
||||
|
@ -161,8 +146,7 @@ class _InputFileState extends State<InputFile> {
|
|||
await pickFileFromGallery(context);
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
style: OutlinedButton.styleFrom(
|
||||
side: BorderSide(
|
||||
width: 1,
|
||||
|
@ -181,16 +165,10 @@ class _InputFileState extends State<InputFile> {
|
|||
),
|
||||
),
|
||||
),
|
||||
StreamBuilder(
|
||||
stream: stream,
|
||||
builder: (context, snapshot) {
|
||||
widget.onChange.call(files);
|
||||
return ListFilesBuilder(
|
||||
files: files,
|
||||
multiple: widget.allowMultiple!,
|
||||
onRemoveFile: removeFile,
|
||||
);
|
||||
},
|
||||
ListFilesBuilder(
|
||||
files: files,
|
||||
multiple: widget.allowMultiple!,
|
||||
onRemoveFile: removeFile,
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
Text(
|
||||
|
|
|
@ -27,17 +27,16 @@ class _LandingPageState extends State<LandingPage> {
|
|||
void dispose() {
|
||||
super.dispose();
|
||||
}
|
||||
final form = FormGroup({
|
||||
'file_pem': FormControl<List<File>>(
|
||||
value: [],
|
||||
validators: [requiredFile],
|
||||
),
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = GolekTheme.of(context);
|
||||
final form = FormGroup({
|
||||
'foto': FormControl<List<File>>(
|
||||
value: [],
|
||||
validators: [requiredFile],
|
||||
),
|
||||
});
|
||||
|
||||
return Scaffold(
|
||||
appBar: LandingAppBar(),
|
||||
body: SafeArea(
|
||||
|
@ -46,25 +45,22 @@ class _LandingPageState extends State<LandingPage> {
|
|||
child: Column(
|
||||
children: [
|
||||
ReactiveValueListenableBuilder(
|
||||
formControlName: 'foto',
|
||||
formControlName: 'file_pem',
|
||||
builder: (context, control, child) {
|
||||
print('DEBUG DI VALUE LISTEN');
|
||||
print(control.value);
|
||||
print(control.touched);
|
||||
print(control.errors.entries);
|
||||
return Container();
|
||||
},
|
||||
),
|
||||
ReactiveInputFile(
|
||||
name: 'foto',
|
||||
name: 'file_pem',
|
||||
allowMultiple: true,
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
form.markAllAsTouched();
|
||||
print(form.control('foto').hasErrors);
|
||||
print(form.control('foto').errors);
|
||||
print(form.control('foto').touched);
|
||||
print(form.control('file_pem').value);
|
||||
print(form.control('file_pem').hasErrors);
|
||||
print(form.control('file_pem').errors);
|
||||
print(form.control('file_pem').touched);
|
||||
if (form.valid){
|
||||
print("FORM IS VALID");
|
||||
}
|
||||
|
|
|
@ -12,20 +12,14 @@ class ErrorMessages {
|
|||
Map<String, String>? widgetCustomMessages,
|
||||
}) {
|
||||
Map<String, Object>? mergedErrorMessages = {
|
||||
...?widgetCustomMessages,
|
||||
...messages,
|
||||
...?widgetCustomMessages,
|
||||
};
|
||||
|
||||
MapEntry<String, dynamic> message = const MapEntry('', '');
|
||||
print('GET UI ERROR ');
|
||||
print(control.errors);
|
||||
if (control.errors.entries.isNotEmpty) {
|
||||
message = mergedErrorMessages.entries.firstWhere(
|
||||
(element) => element.key == control.errors.entries.first.key,
|
||||
orElse: () => const MapEntry('', ''),
|
||||
);
|
||||
}
|
||||
|
||||
final message = mergedErrorMessages.entries.firstWhere(
|
||||
(element) => control.errors.entries.isNotEmpty && element.key == control.errors.entries.first.key,
|
||||
orElse: () => const MapEntry('', ''),
|
||||
);
|
||||
return message.value.toString().replaceFirst('{_field_}', label);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:component_library/component_library.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:reactive_forms/reactive_forms.dart';
|
||||
|
||||
import 'error_messages.dart';
|
||||
|
||||
class ReactiveInputFile extends ReactiveFormField<List<File>, List<File>> {
|
||||
|
||||
ReactiveInputFile({
|
||||
required String name,
|
||||
allowMultiple = false,
|
||||
|
@ -19,25 +17,20 @@ class ReactiveInputFile extends ReactiveFormField<List<File>, List<File>> {
|
|||
formControlName: name,
|
||||
builder: (field) {
|
||||
return InputFile(
|
||||
files: field.value ?? [],
|
||||
files: field.value,
|
||||
allowMultiple: allowMultiple,
|
||||
descriptionLabel: descriptionLabel,
|
||||
guidanceLabel: guidanceLabel,
|
||||
uploadButtonLabel: uploadButtonLabel,
|
||||
allowedExtensions: allowedExtensions,
|
||||
errorMessage: ErrorMessages.getUiErrorMessage(
|
||||
control: field.control,
|
||||
label: '',
|
||||
widgetCustomMessages: {
|
||||
'requiredFile': 'Foto harus diisi'
|
||||
}
|
||||
control: field.control,
|
||||
label: '',
|
||||
widgetCustomMessages: {'required': 'Foto harus diisi'},
|
||||
),
|
||||
isTouched: field.control.touched,
|
||||
isError: field.control.hasErrors,
|
||||
onChange: (files) {
|
||||
print('ON CAHNEF');
|
||||
print(field.control.touched);
|
||||
print(field.control.hasErrors);
|
||||
field.didChange(files);
|
||||
},
|
||||
);
|
||||
|
|
|
@ -3,5 +3,5 @@ import 'package:reactive_forms/reactive_forms.dart';
|
|||
Map<String, dynamic>? requiredFile(AbstractControl<dynamic> control) {
|
||||
return control.isNotNull && control.value?.isNotEmpty == true
|
||||
? null
|
||||
: {'requiredFile': true};
|
||||
: {'required': true};
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue