651 lines
22 KiB
Dart
651 lines
22 KiB
Dart
|
import 'dart:developer';
|
||
|
import 'dart:io';
|
||
|
|
||
|
import 'package:dio/dio.dart';
|
||
|
import 'package:dotted_border/dotted_border.dart';
|
||
|
import 'package:ebroker/Ui/screens/widgets/AnimatedRoutes/blur_page_route.dart';
|
||
|
import 'package:ebroker/data/cubits/project/manage_project_cubit.dart';
|
||
|
import 'package:ebroker/data/model/project_model.dart';
|
||
|
import 'package:ebroker/exports/main_export.dart';
|
||
|
import 'package:ebroker/utils/CloudState/cloud_state.dart';
|
||
|
import 'package:ebroker/utils/Extensions/extensions.dart';
|
||
|
import 'package:ebroker/utils/responsiveSize.dart';
|
||
|
import 'package:ebroker/utils/string_extenstion.dart';
|
||
|
import 'package:ebroker/utils/ui_utils.dart';
|
||
|
import 'package:file_picker/file_picker.dart';
|
||
|
import 'package:flutter/material.dart';
|
||
|
import 'package:geocoding/geocoding.dart';
|
||
|
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||
|
import 'package:hive/hive.dart';
|
||
|
|
||
|
import '../../../../data/Repositories/location_repository.dart';
|
||
|
import '../../../../data/model/google_place_model.dart';
|
||
|
import '../../../../utils/AppIcon.dart';
|
||
|
import '../../../../utils/hive_keys.dart';
|
||
|
import '../../proprties/AddProperyScreens/add_property_details.dart';
|
||
|
import '../../widgets/adaptive_image_picker.dart';
|
||
|
import '../../widgets/custom_text_form_field.dart';
|
||
|
|
||
|
class AddProjectDetails extends StatefulWidget {
|
||
|
final Map? editData;
|
||
|
const AddProjectDetails({super.key, this.editData});
|
||
|
static route(RouteSettings settings) {
|
||
|
return BlurredRouter(builder: (context) {
|
||
|
return BlocProvider(
|
||
|
create: (context) => ManageProjectCubit(),
|
||
|
child: AddProjectDetails(
|
||
|
editData: settings.arguments as Map?,
|
||
|
));
|
||
|
});
|
||
|
}
|
||
|
|
||
|
@override
|
||
|
CloudState<AddProjectDetails> createState() => _AddProjectDetailsState();
|
||
|
}
|
||
|
|
||
|
class _AddProjectDetailsState extends CloudState<AddProjectDetails> {
|
||
|
late bool isEdit = widget.editData != null;
|
||
|
|
||
|
late ProjectModel? project = getEditProjectData(widget.editData?['project']);
|
||
|
|
||
|
late final TextEditingController _titleController =
|
||
|
TextEditingController(text: project?.title);
|
||
|
late final TextEditingController _descriptionController =
|
||
|
TextEditingController(text: project?.description);
|
||
|
late final TextEditingController _videoLinkController =
|
||
|
TextEditingController(text: project?.videoLink);
|
||
|
String selectedLocation = "";
|
||
|
GooglePlaceModel? suggestion;
|
||
|
final GlobalKey<FormState> _formKey = GlobalKey();
|
||
|
|
||
|
List<Document> documentFiles = [];
|
||
|
List<int> removedDocumentId = [];
|
||
|
List<int> removedGalleryImageId = [];
|
||
|
|
||
|
GooglePlaceRepository googlePlaceRepository = GooglePlaceRepository();
|
||
|
|
||
|
late final TextEditingController _cityNameController =
|
||
|
TextEditingController(text: project?.city);
|
||
|
|
||
|
late final TextEditingController _stateNameController =
|
||
|
TextEditingController(text: project?.state);
|
||
|
|
||
|
late final TextEditingController _countryNameController =
|
||
|
TextEditingController(text: project?.country);
|
||
|
|
||
|
late final TextEditingController _addressController =
|
||
|
TextEditingController(text: project?.location);
|
||
|
// final TextEditingController _main=TextEditingController();
|
||
|
double? latitude;
|
||
|
double? longitude;
|
||
|
Map? floorPlans = {};
|
||
|
List<Map> floorPlansRawData = [];
|
||
|
ImagePickerValue? titleImage;
|
||
|
ImagePickerValue? galleryImages;
|
||
|
String projectType = "upcoming";
|
||
|
List<int> removedPlansId = [];
|
||
|
ProjectModel? getEditProjectData(Map<String, dynamic>? data) {
|
||
|
if (data == null) {
|
||
|
return null;
|
||
|
}
|
||
|
return ProjectModel.fromMap(data);
|
||
|
}
|
||
|
|
||
|
@override
|
||
|
void initState() {
|
||
|
//add documents in edit mode
|
||
|
List<UrlDocument>? list = project?.documents?.map((document) {
|
||
|
return UrlDocument(document.name!, document.id!);
|
||
|
}).toList();
|
||
|
|
||
|
if (list != null) {
|
||
|
documentFiles = List<Document>.from(list as List<Document>);
|
||
|
}
|
||
|
projectType = project?.type ?? "upcoming";
|
||
|
if (project != null && project?.image != "") {
|
||
|
titleImage = UrlValue(project!.image!);
|
||
|
}
|
||
|
|
||
|
if (project != null && project!.gallaryImages!.isNotEmpty) {
|
||
|
galleryImages = MultiValue(
|
||
|
project!.gallaryImages!.map((e) => UrlValue(e.name!)).toList());
|
||
|
}
|
||
|
|
||
|
///add plans in edit mode
|
||
|
project?.plans?.forEach((plan) {
|
||
|
floorPlansRawData.add({
|
||
|
"title": plan.title,
|
||
|
"id": plan.id,
|
||
|
"image": plan.document,
|
||
|
});
|
||
|
});
|
||
|
|
||
|
setState(() {});
|
||
|
super.initState();
|
||
|
}
|
||
|
|
||
|
@override
|
||
|
Widget build(BuildContext context) {
|
||
|
return Scaffold(
|
||
|
backgroundColor: context.color.backgroundColor,
|
||
|
appBar: UiUtils.buildAppBar(
|
||
|
context,
|
||
|
title: "projectDetails".translate(context),
|
||
|
showBackButton: true,
|
||
|
),
|
||
|
bottomNavigationBar: BottomAppBar(
|
||
|
color: context.color.backgroundColor,
|
||
|
child: Padding(
|
||
|
padding: const EdgeInsets.symmetric(horizontal: 14.0, vertical: 5),
|
||
|
child: MaterialButton(
|
||
|
shape:
|
||
|
RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||
|
color: context.color.tertiaryColor,
|
||
|
onPressed: () {
|
||
|
if (_formKey.currentState!.validate()) {
|
||
|
Map documents = {};
|
||
|
try {
|
||
|
documents = documentFiles.fold({}, (pr, el) {
|
||
|
if (el is FileDocument) {
|
||
|
pr.addAll({
|
||
|
"documents[${pr.length}]":
|
||
|
MultipartFile.fromFileSync(el.value.path)
|
||
|
});
|
||
|
}
|
||
|
return pr;
|
||
|
});
|
||
|
} catch (e) {
|
||
|
log("issue is $e");
|
||
|
}
|
||
|
addCloudData(
|
||
|
'add_project_details',
|
||
|
{
|
||
|
"title": _titleController.text,
|
||
|
"description": _descriptionController.text,
|
||
|
"latitude": latitude,
|
||
|
"longitude": longitude,
|
||
|
"city": _cityNameController.text,
|
||
|
"state": _stateNameController.text,
|
||
|
"country": _countryNameController.text,
|
||
|
"location": _addressController.text,
|
||
|
"video_link": _videoLinkController.text,
|
||
|
if (titleImage != null &&
|
||
|
titleImage is! UrlValue &&
|
||
|
titleImage?.value != "")
|
||
|
"image": titleImage,
|
||
|
"gallery_images": galleryImages,
|
||
|
|
||
|
...documents,
|
||
|
"is_edit": isEdit,
|
||
|
"project": project,
|
||
|
"type": projectType,
|
||
|
"remove_gallery_images": removedGalleryImageId.join(","),
|
||
|
"remove_documents": removedDocumentId.join(","),
|
||
|
"remove_plans": removedPlansId.join(","),
|
||
|
|
||
|
////if there is data it will add into it
|
||
|
...widget.editData ?? {}
|
||
|
},
|
||
|
);
|
||
|
|
||
|
//this will create Map from List<Map>
|
||
|
|
||
|
floorPlansRawData
|
||
|
.removeWhere((element) => element['image'] is String);
|
||
|
Map fold = floorPlansRawData.fold({}, (previousValue, element) {
|
||
|
previousValue.addAll({
|
||
|
"plans[${previousValue.length ~/ 2}][id]":
|
||
|
(element['id'] is ValueKey)
|
||
|
? (element['id'] as ValueKey).value
|
||
|
: "",
|
||
|
"plans[${previousValue.length ~/ 2}][document]":
|
||
|
element['image'],
|
||
|
"plans[${previousValue.length ~/ 2}][title]":
|
||
|
element['title'],
|
||
|
});
|
||
|
return previousValue;
|
||
|
});
|
||
|
|
||
|
addCloudData("floor_plans", fold);
|
||
|
|
||
|
Navigator.pushNamed(context, Routes.projectMetaDataScreens);
|
||
|
}
|
||
|
},
|
||
|
height: 50,
|
||
|
child: Text("continue".translate(context))
|
||
|
.color(context.color.secondaryColor),
|
||
|
),
|
||
|
),
|
||
|
),
|
||
|
body: SingleChildScrollView(
|
||
|
physics: const BouncingScrollPhysics(),
|
||
|
child: Form(
|
||
|
key: _formKey,
|
||
|
child: Padding(
|
||
|
padding: const EdgeInsets.all(14.0),
|
||
|
child: Column(
|
||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||
|
children: [
|
||
|
Text("projectName".translate(context)),
|
||
|
height(),
|
||
|
CustomTextFormField(
|
||
|
controller: _titleController,
|
||
|
validator: CustomTextFieldValidator.nullCheck,
|
||
|
action: TextInputAction.next,
|
||
|
hintText: "projectName".translate(context),
|
||
|
),
|
||
|
height(),
|
||
|
Text("Description".translate(context)),
|
||
|
height(),
|
||
|
CustomTextFormField(
|
||
|
action: TextInputAction.next,
|
||
|
controller: _descriptionController,
|
||
|
validator: CustomTextFieldValidator.nullCheck,
|
||
|
hintText: UiUtils.translate(context, "writeSomething"),
|
||
|
maxLine: 100,
|
||
|
minLine: 6,
|
||
|
),
|
||
|
height(),
|
||
|
projectTypeField(context),
|
||
|
height(),
|
||
|
buildLocationChooseHeader(),
|
||
|
height(),
|
||
|
buildProjectLocationTextFields(),
|
||
|
height(),
|
||
|
Text("uploadMainPicture".translate(context)),
|
||
|
height(),
|
||
|
AdaptiveImagePickerWidget(
|
||
|
isRequired: true,
|
||
|
multiImage: false,
|
||
|
value: isEdit ? UrlValue(project!.image!) : null,
|
||
|
title: UiUtils.translate(context, "addMainPicture"),
|
||
|
onSelect: (ImagePickerValue? selected) {
|
||
|
titleImage = selected;
|
||
|
setState(() {});
|
||
|
},
|
||
|
),
|
||
|
height(),
|
||
|
Text("uploadOtherImages".translate(context)),
|
||
|
height(),
|
||
|
AdaptiveImagePickerWidget(
|
||
|
title: UiUtils.translate(context, "addOtherImage"),
|
||
|
onRemove: (value) {
|
||
|
if (value is UrlValue) {
|
||
|
removedGalleryImageId.add(value.metaData['id']);
|
||
|
}
|
||
|
},
|
||
|
multiImage: true,
|
||
|
value: MultiValue([
|
||
|
...project?.gallaryImages?.map((e) => UrlValue(e.name!, {
|
||
|
"id": e.id!,
|
||
|
})) ??
|
||
|
[]
|
||
|
]),
|
||
|
onSelect: (ImagePickerValue? selected) {
|
||
|
if (selected is MultiValue) {
|
||
|
galleryImages = selected;
|
||
|
setState(() {});
|
||
|
}
|
||
|
},
|
||
|
),
|
||
|
height(),
|
||
|
Text("videoLink".translate(context)),
|
||
|
height(),
|
||
|
CustomTextFormField(
|
||
|
action: TextInputAction.next,
|
||
|
controller: _videoLinkController,
|
||
|
validator: CustomTextFieldValidator.link,
|
||
|
hintText: "http://example.com/video.mp4",
|
||
|
),
|
||
|
height(),
|
||
|
Text("projectDocuments".translate(context)),
|
||
|
height(),
|
||
|
buildDocumentPicker(context),
|
||
|
...documentsList(),
|
||
|
height(),
|
||
|
Row(
|
||
|
children: [
|
||
|
Column(
|
||
|
children: [
|
||
|
Text(
|
||
|
"floorPlans".translate(context),
|
||
|
),
|
||
|
Text("${floorPlansRawData.length}").bold()
|
||
|
],
|
||
|
),
|
||
|
Spacer(),
|
||
|
MaterialButton(
|
||
|
elevation: 0,
|
||
|
color: context.color.tertiaryColor.withOpacity(0.1),
|
||
|
onPressed: () async {
|
||
|
Map? data = await Navigator.pushNamed(
|
||
|
context, Routes.manageFloorPlansScreen,
|
||
|
arguments: {"floorPlan": floorPlansRawData})
|
||
|
as Map?;
|
||
|
if (data != null) {
|
||
|
floorPlansRawData = data['floorPlans'] ?? [];
|
||
|
|
||
|
removedPlansId = data['removed'];
|
||
|
}
|
||
|
setState(() {});
|
||
|
},
|
||
|
child: const Text("Manage"),
|
||
|
)
|
||
|
],
|
||
|
),
|
||
|
height(30)
|
||
|
],
|
||
|
),
|
||
|
),
|
||
|
),
|
||
|
),
|
||
|
);
|
||
|
}
|
||
|
|
||
|
Widget projectTypeField(BuildContext context) {
|
||
|
return Column(
|
||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||
|
children: [
|
||
|
Text("projectStatus".translate(context)),
|
||
|
height(),
|
||
|
InputDecorator(
|
||
|
decoration: InputDecoration(
|
||
|
hintStyle: TextStyle(
|
||
|
color: context.color.textColorDark.withOpacity(0.7),
|
||
|
fontSize: context.font.large),
|
||
|
filled: true,
|
||
|
fillColor: context.color.secondaryColor,
|
||
|
focusedBorder: OutlineInputBorder(
|
||
|
borderSide: BorderSide(
|
||
|
width: 1.5, color: context.color.tertiaryColor),
|
||
|
borderRadius: BorderRadius.circular(10)),
|
||
|
enabledBorder: OutlineInputBorder(
|
||
|
borderSide:
|
||
|
BorderSide(width: 1.5, color: context.color.borderColor),
|
||
|
borderRadius: BorderRadius.circular(10)),
|
||
|
border: OutlineInputBorder(
|
||
|
borderSide:
|
||
|
BorderSide(width: 1.5, color: context.color.borderColor),
|
||
|
borderRadius: BorderRadius.circular(10))),
|
||
|
child: DropdownButton(
|
||
|
isExpanded: true,
|
||
|
value: projectType,
|
||
|
isDense: true,
|
||
|
borderRadius: BorderRadius.zero,
|
||
|
padding: EdgeInsets.zero,
|
||
|
underline: const SizedBox.shrink(),
|
||
|
items: [
|
||
|
DropdownMenuItem(
|
||
|
child: Text("Upcoming".translate(context)),
|
||
|
value: "upcoming",
|
||
|
),
|
||
|
DropdownMenuItem(
|
||
|
child: Text("Under Construction".translate(context)),
|
||
|
value: "under_construction",
|
||
|
),
|
||
|
],
|
||
|
onChanged: (value) {
|
||
|
projectType = value!;
|
||
|
setState(() {});
|
||
|
},
|
||
|
),
|
||
|
),
|
||
|
],
|
||
|
);
|
||
|
}
|
||
|
|
||
|
buildProjectLocationTextFields() {
|
||
|
return Column(
|
||
|
children: [
|
||
|
CustomTextFormField(
|
||
|
action: TextInputAction.next,
|
||
|
controller: _cityNameController,
|
||
|
isReadOnly: false,
|
||
|
validator: CustomTextFieldValidator.nullCheck,
|
||
|
hintText: UiUtils.translate(context, "city"),
|
||
|
),
|
||
|
SizedBox(
|
||
|
height: 10.rh(context),
|
||
|
),
|
||
|
CustomTextFormField(
|
||
|
action: TextInputAction.next,
|
||
|
controller: _stateNameController,
|
||
|
isReadOnly: false,
|
||
|
validator: CustomTextFieldValidator.nullCheck,
|
||
|
hintText: UiUtils.translate(context, "state"),
|
||
|
),
|
||
|
SizedBox(
|
||
|
height: 10.rh(context),
|
||
|
),
|
||
|
CustomTextFormField(
|
||
|
action: TextInputAction.next,
|
||
|
controller: _countryNameController,
|
||
|
isReadOnly: false,
|
||
|
validator: CustomTextFieldValidator.nullCheck,
|
||
|
hintText: UiUtils.translate(context, "country"),
|
||
|
),
|
||
|
SizedBox(
|
||
|
height: 10.rh(context),
|
||
|
),
|
||
|
CustomTextFormField(
|
||
|
action: TextInputAction.next,
|
||
|
controller: _addressController,
|
||
|
hintText: UiUtils.translate(context, "addressLbl"),
|
||
|
maxLine: 100,
|
||
|
validator: CustomTextFieldValidator.nullCheck,
|
||
|
minLine: 4,
|
||
|
)
|
||
|
],
|
||
|
);
|
||
|
}
|
||
|
|
||
|
buildLocationChooseHeader() {
|
||
|
return SizedBox(
|
||
|
height: 35.rh(context),
|
||
|
child: Row(
|
||
|
mainAxisSize: MainAxisSize.max,
|
||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||
|
children: [
|
||
|
Expanded(flex: 3, child: Text("projectLocation".translate(context))),
|
||
|
// const Spacer(),
|
||
|
Expanded(
|
||
|
flex: 3,
|
||
|
child: ChooseLocationFormField(
|
||
|
initialValue: false,
|
||
|
validator: (bool? value) {
|
||
|
if (project != null) {
|
||
|
}
|
||
|
|
||
|
if (value == true) {
|
||
|
return null;
|
||
|
} else {
|
||
|
return "Select location";
|
||
|
}
|
||
|
},
|
||
|
build: (state) {
|
||
|
return Container(
|
||
|
decoration: BoxDecoration(
|
||
|
// color: context.color.teritoryColor,
|
||
|
border: Border.all(
|
||
|
width: 1.5,
|
||
|
color:
|
||
|
state.hasError ? Colors.red : Colors.transparent),
|
||
|
borderRadius: BorderRadius.circular(9)),
|
||
|
child: MaterialButton(
|
||
|
height: 30,
|
||
|
onPressed: () {
|
||
|
_onTapChooseLocation.call(state);
|
||
|
},
|
||
|
child: FittedBox(
|
||
|
fit: BoxFit.fitWidth,
|
||
|
child: Row(
|
||
|
mainAxisSize: MainAxisSize.min,
|
||
|
children: [
|
||
|
UiUtils.getSvg(AppIcons.location,
|
||
|
color: context.color.textLightColor),
|
||
|
const SizedBox(
|
||
|
width: 3,
|
||
|
),
|
||
|
Text(
|
||
|
UiUtils.translate(context, "chooseLocation"),
|
||
|
)
|
||
|
.size(context.font.normal)
|
||
|
.color(context.color.tertiaryColor)
|
||
|
.underline(),
|
||
|
],
|
||
|
),
|
||
|
)),
|
||
|
);
|
||
|
},
|
||
|
),
|
||
|
)
|
||
|
],
|
||
|
),
|
||
|
);
|
||
|
}
|
||
|
|
||
|
void _onTapChooseLocation(FormFieldState state) async {
|
||
|
try {
|
||
|
FocusManager.instance.primaryFocus?.unfocus();
|
||
|
Map? placeMark = await Navigator.pushNamed(
|
||
|
context, Routes.chooseLocaitonMap,
|
||
|
arguments: {
|
||
|
"latitude": project != null
|
||
|
? double.parse(project!.latitude!)
|
||
|
: Hive.box(HiveKeys.userDetailsBox)
|
||
|
.get("latitude")
|
||
|
.toString()
|
||
|
.toDouble(),
|
||
|
"longitude": project != null
|
||
|
? double.parse(project!.longitude!)
|
||
|
: Hive.box(HiveKeys.userDetailsBox)
|
||
|
.get("longitude")
|
||
|
.toString()
|
||
|
.toDouble()
|
||
|
}) as Map?;
|
||
|
LatLng? latlng = placeMark?['latlng'] as LatLng?;
|
||
|
Placemark? place = placeMark?['place'] as Placemark?;
|
||
|
|
||
|
if (latlng != null && place != null) {
|
||
|
latitude = latlng.latitude;
|
||
|
longitude = latlng.longitude;
|
||
|
// _latitudeController.text = latlng.latitude.toString();
|
||
|
// _longitudeController.text = latlng.longitude.toString();
|
||
|
_cityNameController.text = place.locality ?? "";
|
||
|
_countryNameController.text = place.country ?? "";
|
||
|
_stateNameController.text = place.administrativeArea ?? "";
|
||
|
_addressController.text =
|
||
|
[place.locality, place.administrativeArea, place.country].join(",");
|
||
|
// _addressController.text = getAddress(place);
|
||
|
|
||
|
state.didChange(true);
|
||
|
} else {
|
||
|
print('no action');
|
||
|
// state.didChange(false);
|
||
|
}
|
||
|
} catch (e) {
|
||
|
log("THE ISSUE IS $e");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Widget height([double? h]) {
|
||
|
return SizedBox(
|
||
|
height: (h)?.rh(context) ?? 15.rh(context),
|
||
|
);
|
||
|
}
|
||
|
|
||
|
List<Widget> documentsList() {
|
||
|
return documentFiles.map((document) {
|
||
|
String fileName = "";
|
||
|
if (document is FileDocument) {
|
||
|
fileName = document.value.path.split("/").last;
|
||
|
} else {
|
||
|
fileName = document.value.toString().split("/").last;
|
||
|
}
|
||
|
|
||
|
return ListTile(
|
||
|
title: Text(fileName).setMaxLines(lines: 2),
|
||
|
dense: true,
|
||
|
trailing: IconButton(
|
||
|
icon: const Icon(Icons.close),
|
||
|
onPressed: () {
|
||
|
if (document is UrlDocument) {
|
||
|
removedDocumentId.add(document.id);
|
||
|
}
|
||
|
documentFiles.remove(document);
|
||
|
setState(() {});
|
||
|
},
|
||
|
),
|
||
|
);
|
||
|
}).toList();
|
||
|
}
|
||
|
|
||
|
Widget buildDocumentPicker(BuildContext context) {
|
||
|
return Container(
|
||
|
child: Row(
|
||
|
children: [
|
||
|
DottedBorder(
|
||
|
borderType: BorderType.RRect,
|
||
|
color: context.color.textLightColor,
|
||
|
radius: const Radius.circular(20),
|
||
|
child: Container(
|
||
|
width: 60,
|
||
|
height: 60,
|
||
|
child: Center(
|
||
|
child: IconButton(
|
||
|
onPressed: () async {
|
||
|
FilePickerResult? filePickerResult =
|
||
|
await FilePicker.platform.pickFiles(
|
||
|
allowMultiple: true,
|
||
|
);
|
||
|
if (filePickerResult != null) {
|
||
|
List<Document> list =
|
||
|
List.from(filePickerResult.files.map((e) {
|
||
|
return FileDocument(File(e.path!));
|
||
|
}).toList());
|
||
|
documentFiles.addAll(list);
|
||
|
}
|
||
|
|
||
|
setState(() {});
|
||
|
},
|
||
|
icon: const Icon(Icons.upload),
|
||
|
)),
|
||
|
),
|
||
|
),
|
||
|
const SizedBox(
|
||
|
width: 15,
|
||
|
),
|
||
|
Column(
|
||
|
mainAxisSize: MainAxisSize.max,
|
||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||
|
children: [
|
||
|
Text("UploadDocs".translate(context)),
|
||
|
const SizedBox(
|
||
|
height: 4,
|
||
|
),
|
||
|
Text(documentFiles.length.toString())
|
||
|
],
|
||
|
),
|
||
|
],
|
||
|
),
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
abstract class Document<T> {
|
||
|
abstract final T value;
|
||
|
}
|
||
|
|
||
|
class FileDocument extends Document {
|
||
|
final File value;
|
||
|
FileDocument(this.value);
|
||
|
}
|
||
|
|
||
|
class UrlDocument extends Document {
|
||
|
@override
|
||
|
final String value;
|
||
|
final int id;
|
||
|
UrlDocument(this.value, this.id);
|
||
|
}
|