Feat: Criação de um component de Select e de Upload de Media. Tentativa de criar validadores para o formulário.
This commit is contained in:
parent
feca086aca
commit
0dd0990523
|
@ -5,23 +5,27 @@ import 'package:hub/flutter_flow/flutter_flow_util.dart';
|
||||||
|
|
||||||
class CustomDatePickerUtil extends StatefulWidget {
|
class CustomDatePickerUtil extends StatefulWidget {
|
||||||
final TextEditingController? controller;
|
final TextEditingController? controller;
|
||||||
|
final FocusNode? focusNode;
|
||||||
final String hintText;
|
final String hintText;
|
||||||
final String dateFormat;
|
final String dateFormat;
|
||||||
final String locale;
|
final String locale;
|
||||||
final DateTime? initialDate;
|
final DateTime? initialDate;
|
||||||
final DateTime firstDate;
|
final DateTime firstDate;
|
||||||
final DateTime lastDate;
|
final DateTime? lastDate;
|
||||||
|
final bool timePicker;
|
||||||
final FormFieldValidator<String>? validator;
|
final FormFieldValidator<String>? validator;
|
||||||
|
|
||||||
const CustomDatePickerUtil({
|
const CustomDatePickerUtil({
|
||||||
Key? key,
|
Key? key,
|
||||||
this.controller,
|
this.controller,
|
||||||
|
this.focusNode,
|
||||||
required this.hintText,
|
required this.hintText,
|
||||||
required this.dateFormat,
|
required this.dateFormat,
|
||||||
required this.locale,
|
required this.locale,
|
||||||
|
required this.timePicker,
|
||||||
this.initialDate,
|
this.initialDate,
|
||||||
required this.firstDate,
|
required this.firstDate,
|
||||||
required this.lastDate,
|
this.lastDate,
|
||||||
this.validator,
|
this.validator,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@ -44,7 +48,7 @@ class _CustomDatePickerState extends State<CustomDatePickerUtil> {
|
||||||
context: context,
|
context: context,
|
||||||
initialDate: _selectedDate ?? DateTime.now(),
|
initialDate: _selectedDate ?? DateTime.now(),
|
||||||
firstDate: widget.firstDate,
|
firstDate: widget.firstDate,
|
||||||
lastDate: widget.lastDate,
|
lastDate: widget.lastDate ?? DateTime(2100),
|
||||||
builder: (context, child) {
|
builder: (context, child) {
|
||||||
return wrapInMaterialDatePickerTheme(
|
return wrapInMaterialDatePickerTheme(
|
||||||
context,
|
context,
|
||||||
|
@ -70,48 +74,64 @@ class _CustomDatePickerState extends State<CustomDatePickerUtil> {
|
||||||
);
|
);
|
||||||
|
|
||||||
if (pickedDate != null) {
|
if (pickedDate != null) {
|
||||||
final TimeOfDay? pickedTime = await showTimePicker(
|
if (widget.timePicker == true) {
|
||||||
context: context,
|
final TimeOfDay? pickedTime = await showTimePicker(
|
||||||
initialTime: TimeOfDay.fromDateTime(_selectedDate ?? DateTime.now()),
|
context: context,
|
||||||
builder: (context, child) {
|
initialTime: TimeOfDay.fromDateTime(_selectedDate ?? DateTime.now()),
|
||||||
return wrapInMaterialTimePickerTheme(
|
builder: (context, child) {
|
||||||
context,
|
return wrapInMaterialTimePickerTheme(
|
||||||
child!,
|
context,
|
||||||
headerBackgroundColor: FlutterFlowTheme.of(context).primary,
|
child!,
|
||||||
headerForegroundColor: FlutterFlowTheme.of(context).info,
|
headerBackgroundColor: FlutterFlowTheme.of(context).primary,
|
||||||
headerTextStyle: FlutterFlowTheme.of(context)
|
headerForegroundColor: FlutterFlowTheme.of(context).info,
|
||||||
.headlineLarge
|
headerTextStyle:
|
||||||
.override(
|
FlutterFlowTheme.of(context).headlineLarge.override(
|
||||||
fontFamily: FlutterFlowTheme.of(context).headlineLargeFamily,
|
fontFamily:
|
||||||
fontSize: 32.0,
|
FlutterFlowTheme.of(context).headlineLargeFamily,
|
||||||
letterSpacing: 0.0,
|
fontSize: 32.0,
|
||||||
fontWeight: FontWeight.w600,
|
letterSpacing: 0.0,
|
||||||
useGoogleFonts: GoogleFonts.asMap().containsKey(
|
fontWeight: FontWeight.w600,
|
||||||
FlutterFlowTheme.of(context).headlineLargeFamily),
|
useGoogleFonts: GoogleFonts.asMap().containsKey(
|
||||||
),
|
FlutterFlowTheme.of(context).headlineLargeFamily),
|
||||||
pickerBackgroundColor:
|
),
|
||||||
FlutterFlowTheme.of(context).primaryBackground,
|
pickerBackgroundColor:
|
||||||
pickerForegroundColor: FlutterFlowTheme.of(context).info,
|
FlutterFlowTheme.of(context).primaryBackground,
|
||||||
selectedDateTimeBackgroundColor:
|
pickerForegroundColor: FlutterFlowTheme.of(context).info,
|
||||||
FlutterFlowTheme.of(context).primary,
|
selectedDateTimeBackgroundColor:
|
||||||
selectedDateTimeForegroundColor: FlutterFlowTheme.of(context).info,
|
FlutterFlowTheme.of(context).primary,
|
||||||
pickerDialForegroundColor: FlutterFlowTheme.of(context).primaryText,
|
selectedDateTimeForegroundColor:
|
||||||
actionButtonForegroundColor:
|
FlutterFlowTheme.of(context).info,
|
||||||
FlutterFlowTheme.of(context).primaryText,
|
pickerDialForegroundColor:
|
||||||
iconSize: 24.0,
|
FlutterFlowTheme.of(context).primaryText,
|
||||||
);
|
actionButtonForegroundColor:
|
||||||
},
|
FlutterFlowTheme.of(context).primaryText,
|
||||||
);
|
iconSize: 24.0,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
if (pickedTime != null) {
|
if (pickedTime != null) {
|
||||||
|
setState(() {
|
||||||
|
_selectedDate = DateTime(
|
||||||
|
pickedDate.year,
|
||||||
|
pickedDate.month,
|
||||||
|
pickedDate.day,
|
||||||
|
pickedTime.hour,
|
||||||
|
pickedTime.minute,
|
||||||
|
);
|
||||||
|
widget.controller?.text = dateTimeFormat(
|
||||||
|
widget.dateFormat,
|
||||||
|
_selectedDate,
|
||||||
|
locale: widget.locale,
|
||||||
|
);
|
||||||
|
widget.controller?.selection = TextSelection.collapsed(
|
||||||
|
offset: widget.controller!.text.length,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
setState(() {
|
setState(() {
|
||||||
_selectedDate = DateTime(
|
_selectedDate = pickedDate;
|
||||||
pickedDate.year,
|
|
||||||
pickedDate.month,
|
|
||||||
pickedDate.day,
|
|
||||||
pickedTime.hour,
|
|
||||||
pickedTime.minute,
|
|
||||||
);
|
|
||||||
widget.controller?.text = dateTimeFormat(
|
widget.controller?.text = dateTimeFormat(
|
||||||
widget.dateFormat,
|
widget.dateFormat,
|
||||||
_selectedDate,
|
_selectedDate,
|
||||||
|
@ -140,6 +160,7 @@ class _CustomDatePickerState extends State<CustomDatePickerUtil> {
|
||||||
const EdgeInsetsDirectional.fromSTEB(24.0, 0.0, 24.0, 0.0),
|
const EdgeInsetsDirectional.fromSTEB(24.0, 0.0, 24.0, 0.0),
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
controller: widget.controller,
|
controller: widget.controller,
|
||||||
|
focusNode: widget.focusNode,
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
autovalidateMode: AutovalidateMode.onUserInteraction,
|
autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||||
autofocus: false,
|
autofocus: false,
|
||||||
|
@ -170,31 +191,31 @@ class _CustomDatePickerState extends State<CustomDatePickerUtil> {
|
||||||
),
|
),
|
||||||
enabledBorder: OutlineInputBorder(
|
enabledBorder: OutlineInputBorder(
|
||||||
borderSide: BorderSide(
|
borderSide: BorderSide(
|
||||||
color: FlutterFlowTheme.of(context).accent4,
|
color: FlutterFlowTheme.of(context).customColor6,
|
||||||
width: 0.5,
|
width: 0.5,
|
||||||
),
|
),
|
||||||
borderRadius: BorderRadius.circular(8.0),
|
borderRadius: BorderRadius.circular(10.0),
|
||||||
),
|
),
|
||||||
focusedBorder: OutlineInputBorder(
|
focusedBorder: OutlineInputBorder(
|
||||||
borderSide: BorderSide(
|
borderSide: BorderSide(
|
||||||
color: FlutterFlowTheme.of(context).primary,
|
color: FlutterFlowTheme.of(context).primary,
|
||||||
width: 0.5,
|
width: 0.5,
|
||||||
),
|
),
|
||||||
borderRadius: BorderRadius.circular(8.0),
|
borderRadius: BorderRadius.circular(10.0),
|
||||||
),
|
),
|
||||||
errorBorder: OutlineInputBorder(
|
errorBorder: OutlineInputBorder(
|
||||||
borderSide: BorderSide(
|
borderSide: BorderSide(
|
||||||
color: FlutterFlowTheme.of(context).error,
|
color: FlutterFlowTheme.of(context).error,
|
||||||
width: 0.5,
|
width: 0.5,
|
||||||
),
|
),
|
||||||
borderRadius: BorderRadius.circular(8.0),
|
borderRadius: BorderRadius.circular(10.0),
|
||||||
),
|
),
|
||||||
focusedErrorBorder: OutlineInputBorder(
|
focusedErrorBorder: OutlineInputBorder(
|
||||||
borderSide: BorderSide(
|
borderSide: BorderSide(
|
||||||
color: FlutterFlowTheme.of(context).error,
|
color: FlutterFlowTheme.of(context).error,
|
||||||
width: 0.5,
|
width: 0.5,
|
||||||
),
|
),
|
||||||
borderRadius: BorderRadius.circular(8.0),
|
borderRadius: BorderRadius.circular(10.0),
|
||||||
),
|
),
|
||||||
suffixIcon: Icon(
|
suffixIcon: Icon(
|
||||||
Icons.date_range,
|
Icons.date_range,
|
||||||
|
@ -226,7 +247,7 @@ class _CustomDatePickerState extends State<CustomDatePickerUtil> {
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
height: 80.0,
|
height: 80.0,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(8.0),
|
borderRadius: BorderRadius.circular(10.0),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -0,0 +1,99 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
|
import 'package:hub/flutter_flow/flutter_flow_drop_down.dart';
|
||||||
|
import 'package:hub/flutter_flow/flutter_flow_theme.dart';
|
||||||
|
import 'package:hub/flutter_flow/flutter_flow_util.dart';
|
||||||
|
import 'package:hub/flutter_flow/flutter_flow_widgets.dart';
|
||||||
|
import 'package:hub/flutter_flow/form_field_controller.dart';
|
||||||
|
|
||||||
|
class CustomSelect extends StatefulWidget {
|
||||||
|
final List<String> options;
|
||||||
|
final List<String> optionsLabel;
|
||||||
|
final String hintText;
|
||||||
|
final FormFieldController<String>? controller;
|
||||||
|
|
||||||
|
const CustomSelect({
|
||||||
|
Key? key,
|
||||||
|
required this.options,
|
||||||
|
required this.optionsLabel,
|
||||||
|
required this.hintText,
|
||||||
|
this.controller,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_CustomSelectState createState() => _CustomSelectState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CustomSelectState extends State<CustomSelect> {
|
||||||
|
late FormFieldController<String> _controller;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_controller = widget.controller ?? FormFieldController<String>(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsetsDirectional.fromSTEB(0, 0.0, 0, 10.0),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsetsDirectional.fromSTEB(
|
||||||
|
24.0, 0.0, 24.0, 0.0),
|
||||||
|
child: Container(
|
||||||
|
width: 100.0,
|
||||||
|
height: 48.0,
|
||||||
|
decoration: const BoxDecoration(),
|
||||||
|
child: FlutterFlowDropDown<String>(
|
||||||
|
fillColor:
|
||||||
|
FlutterFlowTheme.of(context).secondaryBackground,
|
||||||
|
controller: _controller,
|
||||||
|
options: widget.options,
|
||||||
|
optionLabels: widget.optionsLabel,
|
||||||
|
onChanged: (val) {
|
||||||
|
setState(() => _controller.value = val);
|
||||||
|
},
|
||||||
|
width: double.infinity,
|
||||||
|
height: double.infinity,
|
||||||
|
textStyle: FlutterFlowTheme.of(context)
|
||||||
|
.bodyMedium
|
||||||
|
.override(
|
||||||
|
fontFamily:
|
||||||
|
FlutterFlowTheme.of(context).bodyMediumFamily,
|
||||||
|
letterSpacing: 0.0,
|
||||||
|
useGoogleFonts: GoogleFonts.asMap().containsKey(
|
||||||
|
FlutterFlowTheme.of(context).bodyMediumFamily),
|
||||||
|
),
|
||||||
|
hintText: widget.hintText,
|
||||||
|
icon: Icon(
|
||||||
|
Icons.keyboard_arrow_down_rounded,
|
||||||
|
color: FlutterFlowTheme.of(context).accent1,
|
||||||
|
size: 24.0,
|
||||||
|
),
|
||||||
|
elevation: 2.0,
|
||||||
|
borderColor: FlutterFlowTheme.of(context).customColor6,
|
||||||
|
borderWidth: 0.5,
|
||||||
|
borderRadius: 10.0,
|
||||||
|
margin: const EdgeInsetsDirectional.fromSTEB(
|
||||||
|
12.0, 0.0, 16.0, 0.0),
|
||||||
|
hidesUnderline: true,
|
||||||
|
isOverButton: true,
|
||||||
|
isSearchable: false,
|
||||||
|
isMultiSelect: false,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,173 @@
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
|
import 'package:hub/flutter_flow/flutter_flow_theme.dart';
|
||||||
|
import 'package:hub/flutter_flow/flutter_flow_widgets.dart';
|
||||||
|
import 'package:hub/flutter_flow/internationalization.dart';
|
||||||
|
import 'package:hub/flutter_flow/upload_data.dart';
|
||||||
|
import 'package:hub/flutter_flow/uploaded_file.dart';
|
||||||
|
|
||||||
|
class MediaUploadButtonUtil extends StatefulWidget {
|
||||||
|
final FFUploadedFile uploadedFile;
|
||||||
|
final bool isUploading;
|
||||||
|
final String labelText;
|
||||||
|
|
||||||
|
const MediaUploadButtonUtil(
|
||||||
|
{Key? key,
|
||||||
|
required this.uploadedFile,
|
||||||
|
required this.isUploading,
|
||||||
|
required this.labelText})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<MediaUploadButtonUtil> createState() => _MediaUploadButtonUtilState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _MediaUploadButtonUtilState extends State<MediaUploadButtonUtil> {
|
||||||
|
late FFUploadedFile _uploadedFile;
|
||||||
|
late bool _isUploading;
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_uploadedFile = widget.uploadedFile ?? FFUploadedFile();
|
||||||
|
_isUploading = widget.isUploading ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsetsDirectional.fromSTEB(24.0, 0.0, 24.0, 0.0),
|
||||||
|
child: Builder(
|
||||||
|
builder: (context) {
|
||||||
|
if ((_uploadedFile.bytes?.isNotEmpty ?? false)) {
|
||||||
|
return InkWell(
|
||||||
|
splashColor: Colors.transparent,
|
||||||
|
focusColor: Colors.transparent,
|
||||||
|
hoverColor: Colors.transparent,
|
||||||
|
highlightColor: Colors.transparent,
|
||||||
|
onTap: () async {
|
||||||
|
setState(() {
|
||||||
|
_isUploading = false;
|
||||||
|
_uploadedFile = FFUploadedFile(bytes: Uint8List.fromList([]));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(8.0),
|
||||||
|
child: Image.memory(
|
||||||
|
_uploadedFile.bytes ?? Uint8List.fromList([]),
|
||||||
|
width: 300.0,
|
||||||
|
height: 200.0,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Stack(
|
||||||
|
children: [
|
||||||
|
Align(
|
||||||
|
alignment: const AlignmentDirectional(0.0, 0.0),
|
||||||
|
child: FFButtonWidget(
|
||||||
|
onPressed: () async {
|
||||||
|
final selectedMedia =
|
||||||
|
await selectMediaWithSourceBottomSheet(
|
||||||
|
context: context,
|
||||||
|
imageQuality: 100,
|
||||||
|
allowPhoto: true,
|
||||||
|
includeDimensions: true,
|
||||||
|
);
|
||||||
|
if (selectedMedia != null) {
|
||||||
|
setState(() => _isUploading = true);
|
||||||
|
var selectedUploadedFiles = <FFUploadedFile>[];
|
||||||
|
|
||||||
|
try {
|
||||||
|
showUploadMessage(
|
||||||
|
context,
|
||||||
|
'Uploading file...',
|
||||||
|
showLoading: true,
|
||||||
|
);
|
||||||
|
selectedUploadedFiles = selectedMedia
|
||||||
|
.map((m) => FFUploadedFile(
|
||||||
|
name: m.storagePath.split('/').last,
|
||||||
|
bytes: m.bytes,
|
||||||
|
height: m.dimensions?.height,
|
||||||
|
width: m.dimensions?.width,
|
||||||
|
// blurHash: m.blurHash,
|
||||||
|
))
|
||||||
|
.toList();
|
||||||
|
} finally {
|
||||||
|
ScaffoldMessenger.of(context).hideCurrentSnackBar();
|
||||||
|
_isUploading = false;
|
||||||
|
}
|
||||||
|
if (selectedUploadedFiles.length ==
|
||||||
|
selectedMedia.length) {
|
||||||
|
setState(() {
|
||||||
|
_uploadedFile = selectedUploadedFiles.first;
|
||||||
|
});
|
||||||
|
showUploadMessage(context, 'Success!');
|
||||||
|
} else {
|
||||||
|
setState(() {});
|
||||||
|
showUploadMessage(context, 'Failed to upload data');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
text: '',
|
||||||
|
icon: Icon(
|
||||||
|
Icons.photo_camera,
|
||||||
|
color: FlutterFlowTheme.of(context).accent1,
|
||||||
|
size: 30.0,
|
||||||
|
),
|
||||||
|
options: FFButtonOptions(
|
||||||
|
width: double.infinity,
|
||||||
|
height: 120.0,
|
||||||
|
padding: const EdgeInsetsDirectional.fromSTEB(
|
||||||
|
0.0, 0.0, 0.0, 20.0),
|
||||||
|
iconPadding: const EdgeInsetsDirectional.fromSTEB(
|
||||||
|
14.0, 0.0, 0.0, 20.0),
|
||||||
|
color: FlutterFlowTheme.of(context).primaryBackground,
|
||||||
|
textStyle: FlutterFlowTheme.of(context)
|
||||||
|
.titleSmall
|
||||||
|
.override(
|
||||||
|
fontFamily:
|
||||||
|
FlutterFlowTheme.of(context).titleSmallFamily,
|
||||||
|
color: FlutterFlowTheme.of(context).primaryText,
|
||||||
|
fontSize: 16.0,
|
||||||
|
letterSpacing: 0.0,
|
||||||
|
useGoogleFonts: GoogleFonts.asMap().containsKey(
|
||||||
|
FlutterFlowTheme.of(context).titleSmallFamily),
|
||||||
|
),
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: FlutterFlowTheme.of(context).accent1,
|
||||||
|
width: 0.2,
|
||||||
|
),
|
||||||
|
borderRadius: BorderRadius.circular(8.0),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Align(
|
||||||
|
alignment: const AlignmentDirectional(0.0, 0.0),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsetsDirectional.fromSTEB(
|
||||||
|
10.0, 65.0, 10.0, 0.0),
|
||||||
|
child: Text(
|
||||||
|
widget.labelText,
|
||||||
|
style: FlutterFlowTheme.of(context).bodyMedium.override(
|
||||||
|
fontFamily:
|
||||||
|
FlutterFlowTheme.of(context).bodyMediumFamily,
|
||||||
|
color: FlutterFlowTheme.of(context).primaryText,
|
||||||
|
letterSpacing: 0.0,
|
||||||
|
useGoogleFonts: GoogleFonts.asMap().containsKey(
|
||||||
|
FlutterFlowTheme.of(context).bodyMediumFamily),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,31 +1,61 @@
|
||||||
import 'dart:async';
|
import 'dart:developer';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hub/flutter_flow/flutter_flow_model.dart';
|
|
||||||
import 'package:hub/flutter_flow/flutter_flow_util.dart';
|
import 'package:hub/flutter_flow/flutter_flow_util.dart';
|
||||||
import 'package:hub/flutter_flow/uploaded_file.dart';
|
import 'package:hub/flutter_flow/form_field_controller.dart';
|
||||||
import 'package:hub/pages/pets_page/pets_page_widget.dart';
|
import 'package:hub/pages/pets_page/pets_page_widget.dart';
|
||||||
|
|
||||||
class PetsPageModel extends FlutterFlowModel<PetsPageWidget> {
|
class PetsPageModel extends FlutterFlowModel<PetsPageWidget> {
|
||||||
late final TabController tabBarController;
|
late final TabController tabBarController;
|
||||||
Timer? _debounceTimer;
|
|
||||||
|
|
||||||
final unfocusNode = FocusNode();
|
final unfocusNode = FocusNode();
|
||||||
|
|
||||||
|
// Controller para o Upload de Arquivos
|
||||||
bool isDataUploading = false;
|
bool isDataUploading = false;
|
||||||
FFUploadedFile uploadedLocalFile =
|
FFUploadedFile uploadedLocalFile =
|
||||||
FFUploadedFile(bytes: Uint8List.fromList([]));
|
FFUploadedFile(bytes: Uint8List.fromList([]));
|
||||||
|
|
||||||
void debounce(Function() fn, Duration time) {
|
// Controller para o DropDown
|
||||||
if (_debounceTimer != null) {
|
String? dropDownValue1;
|
||||||
_debounceTimer!.cancel();
|
FormFieldController<String>? dropDownValueController1;
|
||||||
|
|
||||||
|
String? dropDownValue2;
|
||||||
|
FormFieldController<String>? dropDownValueController2;
|
||||||
|
|
||||||
|
// Controller para o TextField
|
||||||
|
FocusNode? textFieldFocusName;
|
||||||
|
TextEditingController? textControllerName;
|
||||||
|
String? Function(BuildContext, String?)? textControllerNameValidator;
|
||||||
|
String? _textControllerNameValidator(BuildContext context, String? val) {
|
||||||
|
log('Chamou esta merda');
|
||||||
|
if (val == null || val.isEmpty) {
|
||||||
|
return FFLocalizations.of(context).getVariableText(
|
||||||
|
enText: 'This field is required',
|
||||||
|
ptText: 'Este campo é obrigatório',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
_debounceTimer = Timer(time, fn);
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
FocusNode? textFieldFocusSpecies;
|
||||||
void dispose() {
|
TextEditingController? textControllerSpecies;
|
||||||
tabBarController.dispose();
|
String? Function(BuildContext, String?)? textControllerSpeciesValidator;
|
||||||
}
|
|
||||||
|
FocusNode? textFieldFocusRace;
|
||||||
|
TextEditingController? textControllerRace;
|
||||||
|
String? Function(BuildContext, String?)? textControllerRaceValidator;
|
||||||
|
|
||||||
|
FocusNode? textFieldFocusColor;
|
||||||
|
TextEditingController? textControllerColor;
|
||||||
|
String? Function(BuildContext, String?)? textControllerColorValidator;
|
||||||
|
|
||||||
|
FocusNode? textFieldFocusData;
|
||||||
|
TextEditingController? textControllerData;
|
||||||
|
String? Function(BuildContext, String?)? textControllerDataValidator;
|
||||||
|
|
||||||
|
FocusNode? textFieldFocusObservation;
|
||||||
|
TextEditingController? textControllerObservation;
|
||||||
|
String? Function(BuildContext, String?)? textControllerObservationValidator;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState(BuildContext context) {
|
void initState(BuildContext context) {
|
||||||
|
@ -34,5 +64,17 @@ class PetsPageModel extends FlutterFlowModel<PetsPageWidget> {
|
||||||
vsync: Navigator.of(context),
|
vsync: Navigator.of(context),
|
||||||
length: 2,
|
length: 2,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
textFieldFocusName = FocusNode();
|
||||||
|
textControllerName = TextEditingController();
|
||||||
|
textControllerNameValidator =
|
||||||
|
(context, value) => _textControllerNameValidator(context, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
tabBarController.dispose();
|
||||||
|
textFieldFocusName?.dispose();
|
||||||
|
textControllerName?.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +1,25 @@
|
||||||
import 'package:easy_debounce/easy_debounce.dart';
|
import 'dart:developer';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/scheduler.dart';
|
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
|
|
||||||
import 'package:hub/components/atomic_components/shared_components_atoms/appbar.dart';
|
import 'package:hub/components/atomic_components/shared_components_atoms/appbar.dart';
|
||||||
import 'package:hub/components/atomic_components/shared_components_atoms/custom_datepicker.dart';
|
import 'package:hub/components/atomic_components/shared_components_atoms/custom_datepicker.dart';
|
||||||
import 'package:hub/components/atomic_components/shared_components_atoms/custom_input.dart';
|
import 'package:hub/components/atomic_components/shared_components_atoms/custom_input.dart';
|
||||||
|
import 'package:hub/components/atomic_components/shared_components_atoms/custom_select.dart';
|
||||||
|
import 'package:hub/components/atomic_components/shared_components_atoms/media_upload_button.dart';
|
||||||
import 'package:hub/components/atomic_components/shared_components_atoms/tabview.dart';
|
import 'package:hub/components/atomic_components/shared_components_atoms/tabview.dart';
|
||||||
|
|
||||||
import 'package:hub/flutter_flow/flutter_flow_icon_button.dart';
|
|
||||||
import 'package:hub/flutter_flow/flutter_flow_model.dart';
|
|
||||||
import 'package:hub/flutter_flow/flutter_flow_theme.dart';
|
import 'package:hub/flutter_flow/flutter_flow_theme.dart';
|
||||||
import 'package:hub/flutter_flow/flutter_flow_util.dart';
|
import 'package:hub/flutter_flow/flutter_flow_util.dart';
|
||||||
import 'package:hub/flutter_flow/flutter_flow_widgets.dart';
|
import 'package:hub/flutter_flow/flutter_flow_widgets.dart';
|
||||||
import 'package:hub/flutter_flow/internationalization.dart';
|
import 'package:hub/flutter_flow/internationalization.dart';
|
||||||
import 'package:hub/flutter_flow/nav/nav.dart';
|
import 'package:hub/flutter_flow/nav/nav.dart';
|
||||||
import 'package:hub/flutter_flow/upload_data.dart';
|
import 'package:hub/flutter_flow/upload_data.dart';
|
||||||
|
|
||||||
import 'package:hub/pages/pets_page/pets_page_model.dart';
|
import 'package:hub/pages/pets_page/pets_page_model.dart';
|
||||||
|
|
||||||
class PetsPageWidget extends StatefulWidget {
|
class PetsPageWidget extends StatefulWidget {
|
||||||
|
@ -34,6 +38,11 @@ class _PetsPageWidgetState extends State<PetsPageWidget>
|
||||||
super.initState();
|
super.initState();
|
||||||
_model = PetsPageModel();
|
_model = PetsPageModel();
|
||||||
_model.tabBarController = TabController(length: 2, vsync: this);
|
_model.tabBarController = TabController(length: 2, vsync: this);
|
||||||
|
|
||||||
|
_model.textControllerName ??= TextEditingController();
|
||||||
|
_model.textFieldFocusName ??= FocusNode();
|
||||||
|
|
||||||
|
_model.textControllerSpecies ??= TextEditingController();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -42,11 +51,17 @@ class _PetsPageWidgetState extends State<PetsPageWidget>
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _onTextChanged() {
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: appBarPets(context),
|
appBar: appBarPets(context),
|
||||||
body: tabViewPets(context, _model, _model.tabBarController));
|
backgroundColor: FlutterFlowTheme.of(context).primaryBackground,
|
||||||
|
body: tabViewPets(
|
||||||
|
context, _model, _model.tabBarController, safeSetState, setState));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,19 +69,33 @@ PreferredSizeWidget appBarPets(BuildContext context) {
|
||||||
return AppBarUtil(title: 'Pets', onBackButtonPressed: () => context.pop());
|
return AppBarUtil(title: 'Pets', onBackButtonPressed: () => context.pop());
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget tabViewPets(BuildContext context, PetsPageModel _model, controller) {
|
Widget tabViewPets(BuildContext context, PetsPageModel _model, controller,
|
||||||
|
Function safeSetState, Function setState) {
|
||||||
return TabViewUtil(
|
return TabViewUtil(
|
||||||
context: context,
|
context: context,
|
||||||
model: _model,
|
model: _model,
|
||||||
labelTab1: 'Cadastrar',
|
labelTab1: 'Cadastrar',
|
||||||
labelTab2: 'Consultar',
|
labelTab2: 'Consultar',
|
||||||
controller: controller,
|
controller: controller,
|
||||||
widget1: formAddPets(context),
|
widget1: formAddPets(context, _model, safeSetState, setState),
|
||||||
widget2: Center(child: Text('Consultar')),
|
widget2: Center(child: Text('Consultar')),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget formAddPets(BuildContext context) {
|
Widget formAddPets(BuildContext context, PetsPageModel _model,
|
||||||
|
Function safeSetState, Function setState) {
|
||||||
|
bool _isFormValid(BuildContext context) {
|
||||||
|
log('Validando Formulário');
|
||||||
|
if (_model.uploadedLocalFile.bytes?.isEmpty ?? true) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (_model.textControllerName.text.isEmpty) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
final _formKey = GlobalKey<FormState>();
|
||||||
return SingleChildScrollView(
|
return SingleChildScrollView(
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.max,
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
@ -91,12 +120,26 @@ Widget formAddPets(BuildContext context) {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Form(
|
Form(
|
||||||
|
key: _formKey,
|
||||||
autovalidateMode: AutovalidateMode.onUserInteraction,
|
autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.max,
|
mainAxisSize: MainAxisSize.max,
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
children: [
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(0, 10, 0, 20),
|
||||||
|
child: MediaUploadButtonUtil(
|
||||||
|
uploadedFile: _model.uploadedLocalFile,
|
||||||
|
isUploading: _model.isDataUploading,
|
||||||
|
labelText: FFLocalizations.of(context).getVariableText(
|
||||||
|
ptText: 'Clique para adicionar a foto de seu Pet',
|
||||||
|
enText: 'Click to add your Pet\'s photo'),
|
||||||
|
),
|
||||||
|
),
|
||||||
CustomInputUtil(
|
CustomInputUtil(
|
||||||
|
controller: _model.textControllerName,
|
||||||
|
validator:
|
||||||
|
_model.textControllerNameValidator.asValidator(context),
|
||||||
labelText: FFLocalizations.of(context)
|
labelText: FFLocalizations.of(context)
|
||||||
.getVariableText(ptText: 'Nome', enText: 'Name'),
|
.getVariableText(ptText: 'Nome', enText: 'Name'),
|
||||||
hintText: FFLocalizations.of(context)
|
hintText: FFLocalizations.of(context)
|
||||||
|
@ -105,45 +148,160 @@ Widget formAddPets(BuildContext context) {
|
||||||
haveMaxLength: true,
|
haveMaxLength: true,
|
||||||
maxLength: 80,
|
maxLength: 80,
|
||||||
),
|
),
|
||||||
CustomInputUtil(
|
Padding(
|
||||||
labelText: FFLocalizations.of(context)
|
padding: const EdgeInsets.fromLTRB(0, 0, 0, 15),
|
||||||
.getVariableText(ptText: 'Espécie', enText: 'Species'),
|
child: CustomInputUtil(
|
||||||
hintText: FFLocalizations.of(context)
|
controller: _model.textControllerSpecies,
|
||||||
.getVariableText(ptText: 'Espécie', enText: 'Species'),
|
labelText: FFLocalizations.of(context).getVariableText(
|
||||||
suffixIcon: Icons.pest_control,
|
ptText: 'Espécie', enText: 'Species'),
|
||||||
haveMaxLength: false,
|
hintText: FFLocalizations.of(context).getVariableText(
|
||||||
|
ptText: 'Espécie', enText: 'Species'),
|
||||||
|
suffixIcon: Icons.pest_control,
|
||||||
|
haveMaxLength: false,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(0, 0, 0, 15),
|
||||||
|
child: CustomInputUtil(
|
||||||
|
controller: _model.textControllerRace,
|
||||||
|
labelText: FFLocalizations.of(context)
|
||||||
|
.getVariableText(ptText: 'Raça', enText: 'Race'),
|
||||||
|
hintText: FFLocalizations.of(context)
|
||||||
|
.getVariableText(ptText: 'Raça', enText: 'Race'),
|
||||||
|
suffixIcon: Icons.pets,
|
||||||
|
haveMaxLength: false,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(0, 0, 0, 15),
|
||||||
|
child: CustomInputUtil(
|
||||||
|
controller: _model.textControllerColor,
|
||||||
|
labelText: FFLocalizations.of(context)
|
||||||
|
.getVariableText(ptText: 'Cor', enText: 'Color'),
|
||||||
|
hintText: FFLocalizations.of(context)
|
||||||
|
.getVariableText(ptText: 'Cor', enText: 'Color'),
|
||||||
|
suffixIcon: Icons.invert_colors,
|
||||||
|
haveMaxLength: false,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(0, 0, 0, 15),
|
||||||
|
child: CustomDatePickerUtil(
|
||||||
|
controller: _model.textControllerData,
|
||||||
|
focusNode: _model.textFieldFocusData,
|
||||||
|
hintText: FFLocalizations.of(context).getVariableText(
|
||||||
|
ptText: 'Data de Nascimento', enText: 'Birth Date'),
|
||||||
|
dateFormat: 'dd/MM/yyyy HH:mm:ss',
|
||||||
|
locale: FFLocalizations.of(context).languageCode,
|
||||||
|
firstDate: DateTime(2000),
|
||||||
|
lastDate: DateTime.now(),
|
||||||
|
timePicker: false,
|
||||||
|
validator: (value) {
|
||||||
|
if (value == null || value.isEmpty) {
|
||||||
|
return 'Por favor, selecione uma data';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Align(
|
||||||
|
alignment: const AlignmentDirectional(-1.0, 0.0),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsetsDirectional.fromSTEB(
|
||||||
|
24.0, 0, 0.0, 15),
|
||||||
|
child: Text(
|
||||||
|
FFLocalizations.of(context).getVariableText(
|
||||||
|
ptText: 'Selecione as opções disponíveis',
|
||||||
|
enText: 'Select the available options',
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.start,
|
||||||
|
style: FlutterFlowTheme.of(context).bodySmall.override(
|
||||||
|
fontFamily:
|
||||||
|
FlutterFlowTheme.of(context).bodySmallFamily,
|
||||||
|
letterSpacing: 0.0,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
useGoogleFonts: GoogleFonts.asMap().containsKey(
|
||||||
|
FlutterFlowTheme.of(context)
|
||||||
|
.bodyMediumFamily),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(0, 0, 0, 15),
|
||||||
|
child: CustomSelect(
|
||||||
|
options: const ['MAC', 'FEM'],
|
||||||
|
controller: _model.dropDownValueController1,
|
||||||
|
optionsLabel: [
|
||||||
|
FFLocalizations.of(context)
|
||||||
|
.getVariableText(ptText: 'Macho', enText: 'Male'),
|
||||||
|
FFLocalizations.of(context).getVariableText(
|
||||||
|
ptText: 'Fêmea', enText: 'Female')
|
||||||
|
],
|
||||||
|
hintText: FFLocalizations.of(context).getVariableText(
|
||||||
|
ptText: 'Selecione o gênero do Pet',
|
||||||
|
enText: 'Select the gender of the Pet')),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(0, 0, 0, 15),
|
||||||
|
child: CustomSelect(
|
||||||
|
options: const ['MIN', 'PEQ', 'MED', 'GRA', 'GIG'],
|
||||||
|
controller: _model.dropDownValueController1,
|
||||||
|
optionsLabel: [
|
||||||
|
FFLocalizations.of(context)
|
||||||
|
.getVariableText(ptText: 'Mini', enText: 'Mini'),
|
||||||
|
FFLocalizations.of(context).getVariableText(
|
||||||
|
ptText: 'Pequeno', enText: 'Small'),
|
||||||
|
FFLocalizations.of(context).getVariableText(
|
||||||
|
ptText: 'Médio', enText: 'Medium'),
|
||||||
|
FFLocalizations.of(context)
|
||||||
|
.getVariableText(ptText: 'Grande', enText: 'Big'),
|
||||||
|
FFLocalizations.of(context).getVariableText(
|
||||||
|
ptText: 'Gigante', enText: 'Giant'),
|
||||||
|
],
|
||||||
|
hintText: FFLocalizations.of(context).getVariableText(
|
||||||
|
ptText: 'Selecione o porte do Pet',
|
||||||
|
enText: 'Select the size of the Pet')),
|
||||||
|
),
|
||||||
|
Align(
|
||||||
|
alignment: const AlignmentDirectional(-1.0, 0.0),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsetsDirectional.fromSTEB(
|
||||||
|
24.0, 0, 0.0, 15),
|
||||||
|
child: Text(
|
||||||
|
FFLocalizations.of(context).getVariableText(
|
||||||
|
ptText: 'Você tem alguma observação sobre o seu Pet?',
|
||||||
|
enText:
|
||||||
|
'Do you have any observations about your Pet?',
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.start,
|
||||||
|
style: FlutterFlowTheme.of(context).bodySmall.override(
|
||||||
|
fontFamily:
|
||||||
|
FlutterFlowTheme.of(context).bodySmallFamily,
|
||||||
|
letterSpacing: 0.0,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
useGoogleFonts: GoogleFonts.asMap().containsKey(
|
||||||
|
FlutterFlowTheme.of(context)
|
||||||
|
.bodyMediumFamily),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
CustomInputUtil(
|
CustomInputUtil(
|
||||||
labelText: FFLocalizations.of(context)
|
controller: _model.textControllerObservation,
|
||||||
.getVariableText(ptText: 'Raça', enText: 'Race'),
|
labelText: FFLocalizations.of(context).getVariableText(
|
||||||
hintText: FFLocalizations.of(context)
|
ptText: 'Escreva as suas observações aqui...',
|
||||||
.getVariableText(ptText: 'Raça', enText: 'Race'),
|
enText: 'Write your observations here...'),
|
||||||
suffixIcon: Icons.pets,
|
|
||||||
haveMaxLength: false,
|
|
||||||
),
|
|
||||||
CustomInputUtil(
|
|
||||||
labelText: FFLocalizations.of(context)
|
|
||||||
.getVariableText(ptText: 'Cor', enText: 'Color'),
|
|
||||||
hintText: FFLocalizations.of(context)
|
|
||||||
.getVariableText(ptText: 'Cor', enText: 'Color'),
|
|
||||||
suffixIcon: Icons.invert_colors,
|
|
||||||
haveMaxLength: false,
|
|
||||||
),
|
|
||||||
CustomDatePickerUtil(
|
|
||||||
// controller: ,
|
|
||||||
hintText: FFLocalizations.of(context).getVariableText(
|
hintText: FFLocalizations.of(context).getVariableText(
|
||||||
ptText: 'Data de Nascimento', enText: 'Birth Date'),
|
ptText: 'Escreva as suas observações aqui...',
|
||||||
dateFormat: 'dd/MM/yyyy HH:mm:ss',
|
enText: 'Write your observations here...'),
|
||||||
locale: FFLocalizations.of(context).languageCode,
|
suffixIcon: Icons.text_fields,
|
||||||
firstDate: DateTime(2000),
|
haveMaxLength: true,
|
||||||
lastDate: DateTime(2050),
|
maxLength: 80,
|
||||||
validator: (value) {
|
|
||||||
if (value == null || value.isEmpty) {
|
|
||||||
return 'Por favor, selecione uma data';
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: _isFormValid(context) ? () {} : () {},
|
||||||
|
child: Text('Cadastrar')),
|
||||||
])),
|
])),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
Loading…
Reference in New Issue