Service request design done

main_design2.0
zaid_daoud 2 years ago
parent 0170c6f5cb
commit e419b17bd1

@ -0,0 +1,4 @@
<svg width="16" height="24" viewBox="0 0 16 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.32812 12.6562H2.86313C3.20484 15.0384 5.25891 16.875 7.73438 16.875C10.2098 16.875 12.2639 15.0384 12.6056 12.6562H9.14062V11.25H12.6562V9.14062H9.14062V7.73438H12.6562V5.625H9.14062V4.21875H12.6056C12.2639 1.83656 10.2098 0 7.73438 0C5.25891 0 3.20484 1.83656 2.86313 4.21875H6.32812V5.625H2.8125V7.73438H6.32812V9.14062H2.8125V11.25H6.32812V12.6562Z" fill="#767676"/>
<path d="M8.4375 19.6561C12.3741 19.2994 15.4688 15.9806 15.4688 11.9531C15.2906 11.9531 14.4632 11.9531 14.0625 11.9531C14.0625 15.446 11.2205 18.2812 7.73438 18.2812C4.245 18.2812 1.40625 15.4425 1.40625 11.9531H0C0 15.9806 3.09469 19.2994 7.03125 19.6561V22.5938H2.10938V24H13.3594V22.5938H8.4375V19.6561Z" fill="#767676"/>
</svg>

After

Width:  |  Height:  |  Size: 812 B

@ -0,0 +1,3 @@
<svg width="24" height="20" viewBox="0 0 24 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M23.3186 17.8143L12.9972 0.69713C12.7363 0.264525 12.268 0 11.7628 0C11.2576 0 10.7892 0.264479 10.5284 0.69713L0.207042 17.8143C-0.0613895 18.2595 -0.0693386 18.8148 0.186319 19.2674C0.441976 19.7201 0.921541 20 1.4414 20H22.0842C22.604 20 23.0836 19.72 23.3393 19.2674C23.5949 18.8147 23.587 18.2595 23.3186 17.8143ZM11.7705 5.92101C12.3633 5.92101 12.8648 6.25543 12.8648 6.84816C12.8648 8.65683 12.6521 11.256 12.6521 13.0646C12.6521 13.5358 12.1353 13.7334 11.7705 13.7334C11.2842 13.7334 10.8738 13.5358 10.8738 13.0646C10.8738 11.256 10.661 8.65683 10.661 6.84816C10.661 6.25543 11.1473 5.92101 11.7705 5.92101ZM11.7857 17.1228C11.117 17.1228 10.6153 16.5757 10.6153 15.9525C10.6153 15.3142 11.1169 14.7822 11.7857 14.7822C12.4088 14.7822 12.9408 15.3142 12.9408 15.9525C12.9408 16.5757 12.4088 17.1228 11.7857 17.1228Z" fill="#767676"/>
</svg>

After

Width:  |  Height:  |  Size: 957 B

File diff suppressed because one or more lines are too long

@ -8,6 +8,7 @@ import 'package:test_sa/controllers/api_routes/urls.dart';
import 'package:test_sa/models/lookup.dart';
import 'package:test_sa/models/user.dart';
@Deprecated("Use the one inside lib/providers folder")
class ServiceRequestPriorityProvider extends ChangeNotifier {
//reset provider data
void reset() {

@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:lottie/lottie.dart';
import 'package:test_sa/extensions/context_extension.dart';
import 'package:test_sa/extensions/int_extensions.dart';
@ -144,4 +145,11 @@ extension FilesExtension on String {
BoxFit fit = BoxFit.contain,
}) =>
Image.asset("assets/images/$this.png", width: width?.toScreenWidth, height: height?.toScreenHeight, color: color, fit: fit);
LottieBuilder toLottieAsset({
int width,
int height,
BoxFit fit = BoxFit.contain,
bool repeat = true,
}) =>
Lottie.asset("assets/lottie/$this.json", width: width?.toScreenWidth, height: height?.toScreenHeight, fit: fit, repeat: repeat);
}

@ -1,6 +1,6 @@
{
"serverErrorMessage": "الخدمة غير متاحة حاليا",
"failedRequestMessage": "الفشل في إكمال الطلب",
"failedRequestMessage": "فشل إكمال الطلب",
"successfulRequestMessage": "تم إكمال الطلب بنجاح",
"requestLockMessage": "انتظر حتى إكمال الطلب",
"cancel": "إلغاء",
@ -252,5 +252,11 @@
"serialNumber" : "الرقم التسلسلي",
"device" : "الجهاز",
"pickAsset" : "إختر جهاز",
"firstAction" : "First Action"
"firstAction" : "First Action",
"priority" : "الأولوية",
"equipmentStatus" : "حالة المعدات",
"attachImage" : "إرفاق صورة",
"callComments" : "",
"comments" : "تعليقات",
"recordVoice" : "تسجيل صوت"
}

@ -254,5 +254,11 @@
"pickAsset" : "Pick Asset",
"firstAction" : "First Action",
"workOrder" : "Work Orders",
"viewWorkOrder" : "View All Work Order"
"viewWorkOrder" : "View All Work Order",
"priority" : "Priority",
"equipmentStatus" : "Equipment Status",
"attachImage" : "Attach Image",
"callComments" : "Call Comments",
"comments": "Comments",
"recordVoice" : "Record Voice"
}

@ -3,10 +3,9 @@ import 'dart:io';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:localization/localization.dart';
import 'package:provider/provider.dart';
import 'package:test_sa/controllers/localization/localization.dart';
import 'package:test_sa/controllers/providers/api/asset_transfer_provider.dart';
import 'package:test_sa/controllers/providers/api/departments_provider.dart';
import 'package:test_sa/controllers/providers/api/devices_provider.dart';
@ -55,6 +54,8 @@ import 'package:test_sa/providers/gas_request_providers/cylinder_type_provider.d
import 'package:test_sa/providers/gas_request_providers/gas_types_provider.dart';
import 'package:test_sa/providers/gas_request_providers/site_provider.dart';
import 'package:test_sa/providers/loading_list_notifier.dart';
import 'package:test_sa/providers/service_request_providers/equipment_status_provider.dart';
import 'package:test_sa/providers/service_request_providers/priority_provider.dart';
import 'package:test_sa/views/pages/device_transfer/request_device_transfer.dart';
import 'package:test_sa/views/pages/device_transfer/track_device_transfer.dart';
import 'package:test_sa/views/pages/sub_workorder/create_sub_workorder_page.dart';
@ -78,8 +79,6 @@ import 'controllers/providers/api/user_provider.dart';
import 'controllers/providers/settings/setting_provider.dart';
import 'new_views/pages/new_gas_refill_request_page.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
if (Platform.isIOS) {
@ -159,6 +158,8 @@ class MyApp extends StatelessWidget {
ChangeNotifierProvider(create: (_) => ServiceReportMaintenanceSituationProvider()),
ChangeNotifierProvider(create: (_) => ServiceReportUsersProvider()),
ChangeNotifierProvider(create: (_) => ServiceReportAssistantsEmployeeProvider()),
ChangeNotifierProvider(create: (_) => PriorityProvider()),
ChangeNotifierProvider(create: (_) => EquipmentStatusProvider()),
],
child: GestureDetector(
onTap: () {

@ -1,4 +1,7 @@
import 'package:flutter/src/widgets/framework.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:test_sa/controllers/api_routes/urls.dart';
import 'package:test_sa/extensions/context_extension.dart';
import 'package:test_sa/models/device/device.dart';
import 'package:test_sa/models/fault_description.dart';
import 'package:test_sa/models/lookup.dart';
@ -138,4 +141,18 @@ class ServiceRequest {
priority: Lookup.fromJson(parsedJson['priority']),
);
}
Future<bool> validateNewRequest(BuildContext context) async {
if (device == null) {
await Fluttertoast.showToast(msg: "${context.translation.youHaveToSelect} ${context.translation.device}");
return false;
} else if (priority == null) {
await Fluttertoast.showToast(msg: "${context.translation.youHaveToSelect} ${context.translation.priority}");
return false;
} else if (defectType == null) {
await Fluttertoast.showToast(msg: "${context.translation.youHaveToSelect} ${context.translation.equipmentStatus}");
return false;
}
return true;
}
}

@ -0,0 +1,33 @@
import 'package:dotted_border/dotted_border.dart';
import 'package:flutter/material.dart';
import 'package:test_sa/extensions/context_extension.dart';
import 'package:test_sa/extensions/int_extensions.dart';
import 'package:test_sa/extensions/text_extensions.dart';
import 'package:test_sa/extensions/widget_extensions.dart';
import '../app_style/app_color.dart';
class AppDashedButton extends StatelessWidget {
final String title;
final VoidCallback onPressed;
const AppDashedButton({@required this.title, @required this.onPressed, Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
padding: EdgeInsets.symmetric(horizontal: 2.toScreenWidth),
decoration: BoxDecoration(color: Theme.of(context).cardColor, borderRadius: BorderRadius.circular(10)),
child: DottedBorder(
strokeWidth: 2,
padding: EdgeInsets.symmetric(vertical: 16.toScreenHeight, horizontal: 16.toScreenWidth),
color: context.isDark ? AppColor.primary40 : AppColor.primary60,
dashPattern: const [4, 3],
radius: const Radius.circular(10),
borderType: BorderType.RRect,
child: title.heading6(context).custom(color: context.isDark ? AppColor.primary40 : AppColor.primary60).center,
),
).onPress(onPressed);
}
}

@ -0,0 +1,36 @@
import 'dart:convert';
import 'package:http/http.dart';
import 'package:test_sa/providers/loading_list_notifier.dart';
import '../../controllers/api_routes/api_manager.dart';
import '../../controllers/api_routes/urls.dart';
import '../../models/lookup.dart';
class EquipmentStatusProvider extends LoadingListNotifier<Lookup> {
@override
Future getDate() async {
if (loading == true) return -2;
loading = true;
notifyListeners();
loading = true;
notifyListeners();
try {
Response response = await ApiManager.instance.get(URLs.equipmentStatus);
stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {
// client's request was successfully received
List categoriesListJson = json.decode(response.body)["data"];
items = categoriesListJson.map((item) => Lookup.fromJson(item)).toList();
}
loading = false;
notifyListeners();
return response.statusCode;
} catch (error) {
loading = false;
stateCode = -1;
notifyListeners();
return -1;
}
}
}

@ -0,0 +1,36 @@
import 'dart:convert';
import 'package:http/http.dart';
import 'package:test_sa/providers/loading_list_notifier.dart';
import '../../controllers/api_routes/api_manager.dart';
import '../../controllers/api_routes/urls.dart';
import '../../models/lookup.dart';
class PriorityProvider extends LoadingListNotifier<Lookup> {
@override
Future getDate() async {
if (loading == true) return -2;
loading = true;
notifyListeners();
loading = true;
notifyListeners();
try {
Response response = await ApiManager.instance.get(URLs.getServiceReportPriority);
stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {
// client's request was successfully received
List categoriesListJson = json.decode(response.body)["data"];
items = categoriesListJson.map((item) => Lookup.fromJson(item)).toList();
}
loading = false;
notifyListeners();
return response.statusCode;
} catch (error) {
loading = false;
stateCode = -1;
notifyListeners();
return -1;
}
}
}

@ -1,43 +1,34 @@
import 'dart:convert';
import 'dart:io';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:image_picker/image_picker.dart';
import 'package:provider/provider.dart';
import 'package:test_sa/controllers/api_routes/http_status_manger.dart';
import 'package:test_sa/controllers/localization/localization.dart';
import 'package:test_sa/controllers/providers/api/service_requests_provider.dart';
import 'package:test_sa/controllers/providers/api/user_provider.dart';
import 'package:test_sa/controllers/providers/settings/setting_provider.dart';
import 'package:test_sa/controllers/validator/validator.dart';
import 'package:test_sa/extensions/context_extension.dart';
import 'package:test_sa/extensions/int_extensions.dart';
import 'package:test_sa/extensions/text_extensions.dart';
import 'package:test_sa/extensions/widget_extensions.dart';
import 'package:test_sa/models/service_request/service_request.dart';
import 'package:test_sa/models/subtitle.dart';
import 'package:test_sa/new_views/common_widgets/app_filled_button.dart';
import 'package:test_sa/views/widgets/app_text_form_field.dart';
import 'package:test_sa/views/widgets/date_and_time/time_picker.dart';
import 'package:test_sa/views/widgets/equipment/pick_asset.dart';
import 'package:test_sa/views/widgets/images/multi_image_picker.dart';
import 'package:test_sa/views/widgets/loaders/loading_manager.dart';
import 'package:test_sa/views/widgets/sound/record_sound.dart';
import 'package:test_sa/views/widgets/sound/sound_player.dart';
import 'package:test_sa/views/widgets/speech_to_text/speech_to_text.dart';
import 'package:test_sa/views/widgets/status/service_request/service_request_defect_types_mune.dart';
import 'package:test_sa/views/widgets/status/service_request/service_request_priority_mune.dart';
import 'package:test_sa/views/widgets/status/service_request/service_request_through_mune.dart';
import 'package:test_sa/views/widgets/status/service_request/service_request_types_mune.dart';
import 'package:test_sa/views/widgets/titles/app_sub_title.dart';
import '../../../../controllers/providers/api/status_drop_down/service_reqest/service_request_through_provider.dart';
import '../../../../controllers/providers/api/status_drop_down/service_reqest/service_request_type_provider.dart';
import '../../../../models/device/device.dart';
import '../../../../models/enums/translation_keys.dart';
import '../../../../models/lookup.dart';
import '../../../../new_views/app_style/app_color.dart';
import '../../../../new_views/common_widgets/app_dashed_button.dart';
import '../../../../new_views/common_widgets/app_lazy_loading.dart';
import '../../../../new_views/common_widgets/app_text_form_field.dart';
import '../../../../new_views/common_widgets/default_app_bar.dart';
import '../../../widgets/status/service_request/service_request_first_action.dart';
import '../../../widgets/status/service_request/service_request_loan_availability.dart';
import '../../../../new_views/common_widgets/single_item_drop_down_menu.dart';
import '../../../../providers/service_request_providers/equipment_status_provider.dart';
import '../../../../providers/service_request_providers/priority_provider.dart';
class CreateRequestPage extends StatefulWidget {
static const String id = "/create-request";
@ -50,6 +41,8 @@ class CreateRequestPage extends StatefulWidget {
}
class CreateRequestPageState extends State<CreateRequestPage> {
TextEditingController _commentController;
double _height;
UserProvider _userProvider;
SettingProvider _settingProvider;
@ -58,21 +51,17 @@ class CreateRequestPageState extends State<CreateRequestPage> {
final List<File> _deviceImages = [];
bool _isLoading = false;
bool _showDatePicker = false;
Device _asset;
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
TextEditingController _maintenanceController, _commentController, _reviewCommentController;
DateTime _dateTime;
bool isFirstActionSubmitted = false;
@override
void initState() {
_maintenanceController = TextEditingController();
_commentController = TextEditingController();
if (widget.serviceRequest != null) {
_serviceRequest = widget.serviceRequest;
_asset = _serviceRequest.device;
_deviceImages.addAll(_serviceRequest.devicePhotos.map((e) => File(e)).toList());
_showDatePicker = _serviceRequest.firstAction != null && _serviceRequest.firstAction.name == "Need a visit";
if (_showDatePicker && _serviceRequest.visitDate != null) {
@ -107,7 +96,6 @@ class CreateRequestPageState extends State<CreateRequestPage> {
@override
void dispose() {
_maintenanceController.dispose();
_commentController.dispose();
super.dispose();
}
@ -143,198 +131,120 @@ class CreateRequestPageState extends State<CreateRequestPage> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
PickAsset(
device: _asset,
device: _serviceRequest.device,
onPickAsset: (asset) {
_asset = asset;
_serviceRequest.device = asset;
setState(() {});
},
),
const SizedBox(
height: 8,
),
const ASubTitle("Priority"),
const SizedBox(
height: 4,
),
ServiceRequestPriorityMenu(
initialValue: widget.serviceRequest?.priority,
onSelect: (status) {
_serviceRequest.priority = status;
8.height,
SingleItemDropDownMenu<Lookup, PriorityProvider>(
context: context,
title: context.translation.priority,
initialValue: _serviceRequest?.priority,
onSelect: (value) {
_serviceRequest.priority = value;
},
enabled: widget.serviceRequest == null ? true : false,
),
const SizedBox(
height: 8,
),
const ASubTitle("Equipment Status"),
const SizedBox(
height: 4,
),
ServiceRequestDefectTypesMenu(
initialValue: _serviceRequest.defectType,
onSelect: (status) {
_serviceRequest.defectType = status;
8.height,
SingleItemDropDownMenu<Lookup, EquipmentStatusProvider>(
context: context,
title: context.translation.equipmentStatus,
initialValue: _serviceRequest?.defectType,
onSelect: (value) {
_serviceRequest.defectType = value;
},
enabled: widget.serviceRequest == null ? true : false,
),
12.height,
const SizedBox(
height: 8,
8.height,
AppDashedButton(title: _serviceRequest.devicePhotos?.first?.split("/")?.last ?? context.translation.attachImage, onPressed: _attachImage),
16.height,
Align(
alignment: AlignmentDirectional.centerStart,
child: context.translation.callComments.heading5(context),
),
const ASubTitle("Type of Request"),
const SizedBox(
height: 4,
8.height,
AppTextFormField(
controller: _commentController,
labelText: context.translation.comments,
suffixIcon: "warning".toSvgAsset(color: context.isDark ? AppColor.neutral10 : AppColor.neutral20, width: 24).paddingOnly(end: 16),
),
ServiceRequestTypesMenu(
initialValue: _serviceRequest.type,
onSelect: (status) {
_serviceRequest.type = status;
8.height,
RecordSound(
onRecord: (audio) {
_serviceRequest.audio = audio;
},
enabled: false,
withDefaultItem: widget.serviceRequest == null,
),
const SizedBox(
height: 8,
),
const ASubTitle("Through"),
const SizedBox(
height: 4,
),
ServiceRequestedThroughMenu(
initialValue: _serviceRequest.requestedThrough,
enabled: false,
),
if (widget.serviceRequest != null) 12.height,
if (widget.serviceRequest != null) const ASubTitle("First Action"),
const SizedBox(
height: 4,
enabled: widget.serviceRequest == null ? true : false,
),
if (widget.serviceRequest != null)
ServiceRequestedFirstAction(
initialValue: _serviceRequest.firstAction,
enabled: !isFirstActionSubmitted,
onSelect: (status) {
_dateTime = null;
_serviceRequest.firstAction = status;
_showDatePicker = _serviceRequest.firstAction != null && _serviceRequest.firstAction.name == "Need a visit";
_serviceRequestsProvider.notifyListeners();
},
16.height,
],
),
if (_showDatePicker) 12.height,
if (_showDatePicker)
ADateTimePicker(
date: _dateTime,
enable: !isFirstActionSubmitted,
from: DateTime.now(),
onDateTimePicker: (date) {
_dateTime = date;
setState(() {});
},
).expanded,
AppFilledButton(
onPressed: _submit,
label: context.translation.submitRequest,
),
if (widget.serviceRequest != null) 12.height,
if (widget.serviceRequest != null) const ASubTitle("Loan Availability"),
const SizedBox(
height: 4,
],
),
if (widget.serviceRequest != null)
ServiceRequestedLoanAvailability(
initialValue: _serviceRequest.loanAvailability,
onSelect: (status) {
_serviceRequest.loanAvailability = status;
},
).paddingOnly(start: 16, end: 16, bottom: 24, top: 16),
),
12.height,
if (widget.serviceRequest != null) const ASubTitle("Comments"),
const SizedBox(
height: 4,
),
if (widget.serviceRequest != null)
ATextFormField(
controller: _reviewCommentController,
initialValue: _serviceRequest.reviewComment,
hintText: context.translation.comment,
style: Theme.of(context).textTheme.titleMedium,
textInputType: TextInputType.multiline,
onSaved: (value) {
_serviceRequest.reviewComment = value;
);
}
_attachImage() async {
ImageSource source = await showDialog(
context: context,
builder: (dialogContext) => CupertinoAlertDialog(
actions: <Widget>[
TextButton(
child: Text(context.translation.pickFromCamera),
onPressed: () {
Navigator.of(dialogContext).pop(ImageSource.camera);
},
enable: widget.serviceRequest != null ? false : true,
),
12.height,
MultiFilesPicker(
label: context.translation.deviceFiles,
files: _deviceImages,
enabled: widget.serviceRequest == null ? true : false,
),
12.height,
SpeechToTextButton(
controller: _maintenanceController,
enabled: widget.serviceRequest == null ? true : false,
),
12.height,
ATextFormField(
controller: _maintenanceController,
initialValue: _serviceRequest.callComments,
hintText: context.translation.maintenanceIssue,
prefixIconData: FontAwesomeIcons.triangleExclamation,
style: Theme.of(context).textTheme.titleLarge,
textInputType: TextInputType.multiline,
validator: (value) => widget.serviceRequest != null || Validator.hasValue(value) ? null : context.translation.maintenanceIssueRequired,
onSaved: (value) {
_serviceRequest.callComments = value;
TextButton(
child: Text(context.translation.pickFromGallery),
onPressed: () {
Navigator.of(dialogContext).pop(ImageSource.gallery);
},
enable: widget.serviceRequest == null ? true : false,
),
12.height,
if (_serviceRequest.audio?.isNotEmpty == true)
ASoundPlayer(
audio: _serviceRequest.audio,
)
else
RecordSound(
onRecord: (audio) {
_serviceRequest.audio = audio;
},
enabled: widget.serviceRequest == null ? true : false,
),
12.height,
if (widget.serviceRequest != null)
ATextFormField(
controller: _commentController,
initialValue: _serviceRequest.comments,
hintText: context.translation.comment,
style: Theme.of(context).textTheme.titleMedium,
textInputType: TextInputType.multiline,
onSaved: (value) {
_serviceRequest.comments = value;
TextButton(
child: Text(context.translation.pickFromFiles),
onPressed: () async {
await _fromFilePicker();
Navigator.pop(context);
},
),
],
),
).expanded,
AppFilledButton(
onPressed: () async {
if (!_formKey.currentState.validate()) return;
if (_asset?.id == null) {
Fluttertoast.showToast(msg: context.translation.pickDevice);
return;
);
if (source == null) return;
final pickedFile = await ImagePicker().pickImage(source: source, imageQuality: 70, maxWidth: 800, maxHeight: 800);
if (pickedFile != null) {
_serviceRequest.devicePhotos ??= [];
_serviceRequest.devicePhotos?.clear();
_serviceRequest.devicePhotos?.add(pickedFile.path);
setState(() {});
}
if (_serviceRequest.firstAction?.name == "Need a visit" && _dateTime == null) {
Fluttertoast.showToast(msg: "first action is required");
return;
}
if (widget.serviceRequest != null && (_serviceRequest?.engineerId == null || (_serviceRequest?.engineerId?.isEmpty ?? false))) {
await Fluttertoast.showToast(msg: "No Assigned Employee");
return;
_fromFilePicker() async {
FilePickerResult result = await FilePicker.platform.pickFiles(
type: FileType.custom,
allowedExtensions: ['jpg', 'jpeg', 'png', 'pdf', 'doc', 'docx', 'xlsx', 'pptx'],
);
if (result?.files?.first != null) {
_serviceRequest.devicePhotos ??= [];
_serviceRequest.devicePhotos?.clear();
_serviceRequest.devicePhotos?.add(result?.files?.first?.path);
setState(() {});
}
_formKey.currentState.save();
_serviceRequest.deviceId = _asset?.id;
if (widget.serviceRequest == null) {
_serviceRequest.type = Provider.of<ServiceRequestTypeProvider>(context, listen: false).getDefaultItem();
}
_serviceRequest.requestedThrough = Provider.of<ServiceRequestedThroughProvider>(context, listen: false).getDefaultItem();
_isLoading = true;
setState(() {});
Future<void> _submit() async {
if (_formKey.currentState.validate() && await _serviceRequest.validateNewRequest(context)) {
_formKey.currentState.save();
_serviceRequest.devicePhotos = _deviceImages.map((e) => _isLocalUrl(e.path) ? "${e.path.split("/").last}|${base64Encode(e.readAsBytesSync())}" : e.path).toList();
if (_serviceRequest.audio != null) {
if (_isLocalUrl(_serviceRequest.audio)) {
@ -342,41 +252,19 @@ class CreateRequestPageState extends State<CreateRequestPage> {
_serviceRequest.audio = "${file.path.split("/").last}|${base64Encode(file.readAsBytesSync())}";
}
}
int status = 0;
if (widget.serviceRequest == null) {
status = await _serviceRequestsProvider.createRequest(
showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading());
int status = await _serviceRequestsProvider.createRequest(
user: _userProvider.user,
host: _settingProvider.host,
serviceRequest: _serviceRequest,
);
} else {
status = await _serviceRequestsProvider.updateDate(
user: _userProvider.user,
host: _settingProvider.host,
request: _serviceRequest,
date: _dateTime,
);
}
_isLoading = false;
setState(() {});
Navigator.of(context);
if (status >= 200 && status < 300) {
Fluttertoast.showToast(
msg: context.translation.successfulRequestMessage,
);
Navigator.of(context).pop();
Fluttertoast.showToast(msg: context.translation.successfulRequestMessage);
Navigator.pop(context);
} else {
String errorMessage = HttpStatusManger.getStatusMessage(status: status, subtitle: context.translation);
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(errorMessage),
));
Fluttertoast.showToast(msg: context.translation.failedRequestMessage);
}
}
},
label: context.translation.submitRequest),
],
),
).paddingOnly(start: 16, end: 16, bottom: 24, top: 16),
),
),
);
}
}

@ -3,11 +3,10 @@ import 'package:test_sa/extensions/context_extension.dart';
import 'package:test_sa/extensions/int_extensions.dart';
import 'package:test_sa/extensions/text_extensions.dart';
import 'package:test_sa/extensions/widget_extensions.dart';
import 'package:test_sa/models/enums/translation_keys.dart';
import 'package:test_sa/new_views/common_widgets/app_text_form_field.dart';
import 'package:test_sa/views/widgets/equipment/single_device_picker.dart';
import '../../../models/device/device.dart';
import '../../../new_views/app_style/app_color.dart';
class PickAsset extends StatelessWidget {
final Function(Device) onPickAsset;
@ -19,22 +18,36 @@ class PickAsset extends StatelessWidget {
Widget build(BuildContext context) {
return Column(
children: [
AppTextFormField(
enable: false,
labelText: context.translation.device,
initialValue: context.translation.site,
suffixIcon: "qr".toSvgAsset(height: 24, fit: BoxFit.fitHeight).paddingOnly(end: 16),
Container(
decoration: BoxDecoration(
color: Theme.of(context).cardColor,
borderRadius: BorderRadius.circular(10),
boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.03), blurRadius: 14)],
),
child: Row(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
context.translation.device.tinyFont(context).custom(color: context.isDark ? AppColor.neutral40 : AppColor.neutral50),
context.translation.pickAsset.bodyText(context).custom(color: context.isDark ? AppColor.neutral40 : AppColor.neutral50),
],
).onPress(() async {
Device device = await Navigator.of(context).pushNamed(SingleDevicePicker.id) as Device;
onPickAsset(device);
}).expanded,
"qr".toSvgAsset(height: 24, fit: BoxFit.fitHeight).onPress(() {
/// TODO [Zaid] : open qr reader
}),
if (device != null) 8.height,
],
).paddingOnly(start: 16, end: 16, top: 8, bottom: 8),
),
if (device != null)
Card(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
context.translation.myAssets.heading5(context),
device.modelDefinition?.assetName?.heading5(context),
8.height,
"${context.translation.assetNo}: ${device.assetNumber}".bodyText(context),
"${context.translation.manufacture}: ${device.modelDefinition?.manufacturerName}".bodyText(context),
@ -45,7 +58,7 @@ class PickAsset extends StatelessWidget {
"${context.translation.site}: ${device.site?.custName}".bodyText(context),
],
).paddingAll(16),
),
).paddingOnly(top: 8),
],
);
}

@ -1,17 +1,20 @@
import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_sound/flutter_sound.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:rive/rive.dart';
import 'package:test_sa/views/widgets/buttons/app_icon_button2.dart';
import 'package:test_sa/views/widgets/buttons/app_small_button.dart';
import 'package:test_sa/views/widgets/sound/sound_player.dart';
import 'package:record_mp3/record_mp3.dart';
import '../../app_style/sizing.dart';
import 'package:rive/rive.dart';
import 'package:test_sa/extensions/context_extension.dart';
import 'package:test_sa/extensions/int_extensions.dart';
import 'package:test_sa/extensions/text_extensions.dart';
import 'package:test_sa/extensions/widget_extensions.dart';
import '../../../new_views/app_style/app_color.dart';
import '../../../new_views/common_widgets/app_text_form_field.dart';
class RecordSound extends StatefulWidget {
final Function(String) onRecord;
@ -30,6 +33,8 @@ class _RecordSoundState extends State<RecordSound> {
bool _recording = false;
String _record;
Artboard _rive;
Timer _timer;
TextEditingController _timeController;
@override
void setState(VoidCallback fn) {
@ -39,7 +44,7 @@ class _RecordSoundState extends State<RecordSound> {
@override
void initState() {
super.initState();
_timeController = TextEditingController();
_recorderIsOpened = true;
// RecordMp3.instance.start(recordFilePath, (type) {
// // record fail callback
@ -70,6 +75,7 @@ class _RecordSoundState extends State<RecordSound> {
@override
void dispose() {
_timeController?.dispose();
// Be careful : you must `close` the audio session when you have finished with it.
RecordMp3.instance.stop();
//_myRecorder.closeRecorder();
@ -80,7 +86,6 @@ class _RecordSoundState extends State<RecordSound> {
String recordingFileDirectory;
_startRecording() async {
// await Permission.camera
PermissionStatus status = await Permission.microphone.request();
if (!status.isGranted) {
PermissionStatus status = await Permission.microphone.request();
@ -89,6 +94,11 @@ class _RecordSoundState extends State<RecordSound> {
return;
}
}
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
setState(() {
_timeController.text = ((timer?.tick ?? 0) / 60)?.toStringAsFixed(2);
});
});
_rive.addController(SimpleAnimation('recording'));
if (!_recorderIsOpened) {
// await _myRecorder.openRecorder();
@ -111,7 +121,9 @@ class _RecordSoundState extends State<RecordSound> {
setState(() {});
return;
}
if (_timer?.isActive ?? false) {
_timer.cancel();
}
RecordMp3.instance.stop();
//String path = (await _myRecorder.stopRecorder()).toString();
@ -137,94 +149,30 @@ class _RecordSoundState extends State<RecordSound> {
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.only(left: 12, right: 0),
decoration: BoxDecoration(
color: const Color(0xfff5f5f5),
border: Border.all(
color: const Color(0xffefefef),
),
borderRadius: BorderRadius.circular(AppStyle.borderRadius * AppStyle.getScaleFactor(context)),
),
child: Column(
children: [
Row(
children: [
Expanded(
child: _recording
? Row(
return Stack(
alignment: AlignmentDirectional.centerEnd,
children: [
ASmallButton(
text: "done",
onPressed: () {
_stopRecording();
},
),
Expanded(
child: Stack(
children: [
SizedBox(
height: 24 * AppStyle.getScaleFactor(context),
child: Rive(
artboard: _rive,
)),
InkWell(
child: SizedBox(
height: 32 * AppStyle.getScaleFactor(context),
width: MediaQuery.of(context).size.width,
),
onTap: () {
_cancelRecording();
},
AppTextFormField(
enable: false,
controller: _timeController,
labelText: context.translation.recordVoice,
initialValue: (_timeController?.text?.isEmpty ?? true) ? "00:00" : _timeController?.text,
suffixIcon: (_recording ? "record".toLottieAsset(height: 24) : (_record != null ? "trash" : "mic").toSvgAsset(color: context.isDark ? AppColor.neutral10 : AppColor.neutral20, height: 24))
.paddingOnly(end: 16),
),
],
),
),
],
)
: _record != null
? Row(
children: [
Expanded(child: ASoundPlayer(audio: _record)),
AIconButton2(
iconData: Icons.delete,
onPressed: () {
SizedBox(height: 50.toScreenHeight, width: 50.toScreenWidth).onPress(() {
if (_recording) {
_stopRecording();
} else if (_record != null) {
_timeController?.text = "00:00";
widget.onRecord(null);
_record = null;
setState(() {});
},
)
],
)
: const Text("Record Voice"),
),
Material(
color: Colors.transparent,
child: GestureDetector(
//key: ValueKey("voice"),
onTapDown: widget.enabled
? (TapDownDetails details) async {
} else {
_startRecording();
}
: null,
onTapUp: widget.enabled
? (TapUpDetails details) async {
_stopRecording();
}
: null,
onTapCancel: widget.enabled
? () async {
_cancelRecording();
}
: null,
//key: ValueKey("voice"),
child: const Padding(padding: EdgeInsets.all(12.0), child: Icon(Icons.mic)),
),
),
],
),
})
],
),
);
}
}

@ -209,6 +209,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.7.8"
dotted_border:
dependency: "direct main"
description:
name: dotted_border
sha256: "108837e11848ca776c53b30bc870086f84b62ed6e01c503ed976e8f8c7df9c04"
url: "https://pub.dev"
source: hosted
version: "2.1.0"
fake_async:
dependency: transitive
description:
@ -661,6 +669,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.4.0"
lottie:
dependency: "direct main"
description:
name: lottie
sha256: "23522951540d20a57a60202ed7022e6376bed206a4eee1c347a91f58bd57eb9f"
url: "https://pub.dev"
source: hosted
version: "2.3.2"
matcher:
dependency: transitive
description:

@ -68,6 +68,8 @@ dependencies:
path_provider: ^2.1.0
open_file: ^3.3.2
localization: ^2.1.0
dotted_border: ^2.1.0
lottie: ^2.3.0
dev_dependencies:
flutter_test:
@ -97,6 +99,7 @@ flutter:
assets:
- assets/
- assets/images/
- assets/lottie/
- assets/subtitles/
- assets/rives/
- assets/translations/

Loading…
Cancel
Save