import 'dart:io'; import 'package:diplomaticquarterapp/config/config.dart'; import 'package:diplomaticquarterapp/core/viewModels/feedback/feedback_view_model.dart'; import 'package:diplomaticquarterapp/core/viewModels/project_view_model.dart'; import 'package:diplomaticquarterapp/models/Appointments/AppoimentAllHistoryResultList.dart'; import 'package:diplomaticquarterapp/pages/base/base_view.dart'; import 'package:diplomaticquarterapp/services/appointment_services/GetDoctorsList.dart'; import 'package:diplomaticquarterapp/services/robo_search/event_provider.dart'; import 'package:diplomaticquarterapp/theme/colors.dart'; import 'package:diplomaticquarterapp/uitl/app_toast.dart'; import 'package:diplomaticquarterapp/uitl/date_uitl.dart'; import 'package:diplomaticquarterapp/uitl/gif_loader_dialog_utils.dart'; import 'package:diplomaticquarterapp/uitl/translations_delegate_base.dart'; import 'package:diplomaticquarterapp/widgets/avatar/large_avatar.dart'; import 'package:diplomaticquarterapp/widgets/bottom_options/BottomSheet.dart'; import 'package:diplomaticquarterapp/widgets/buttons/defaultButton.dart'; import 'package:diplomaticquarterapp/widgets/data_display/medical/doctor_card.dart'; import 'package:diplomaticquarterapp/widgets/data_display/text.dart'; import 'package:diplomaticquarterapp/widgets/others/StarRating.dart'; import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart'; import 'package:diplomaticquarterapp/widgets/others/floating_button_search.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:provider/provider.dart'; import 'package:speech_to_text/speech_recognition_error.dart'; import 'package:speech_to_text/speech_to_text.dart' as stt; class SendFeedbackPage extends StatefulWidget { final AppoitmentAllHistoryResultList appointment; final MessageType messageType; const SendFeedbackPage({Key key, this.appointment, this.messageType = MessageType.NON}) : super(key: key); @override _SendFeedbackPageState createState() => _SendFeedbackPageState(); } class _SendFeedbackPageState extends State { TextEditingController titleController = TextEditingController(); TextEditingController messageController = TextEditingController(); List images = []; String title; AppoitmentAllHistoryResultList appointHistory; bool isShowListAppointHistory = true; String message; final formKey = GlobalKey(); MessageType messageType = MessageType.NON; var _currentLocaleId; stt.SpeechToText speech = stt.SpeechToText(); var reconizedWord; var event = RobotProvider(); List appoList = []; String getSelected(BuildContext context) { switch (messageType) { case MessageType.ComplaintOnAnAppointment: return TranslationBase.of(context).complainAppo; break; case MessageType.ComplaintWithoutAppointment: return TranslationBase.of(context).complainWithoutAppo; break; case MessageType.Question: return TranslationBase.of(context).question; break; case MessageType.Compliment: return TranslationBase.of(context).compliment; break; case MessageType.Suggestion: return TranslationBase.of(context).suggestion; break; case MessageType.NON: return TranslationBase.of(context).notClassified; break; } return TranslationBase.of(context).notClassified; } setMessageType(MessageType messageType) { setState(() { this.messageType = messageType; this.appointHistory = widget.appointment; }); } @override void initState() { setState(() { this.messageType = widget.messageType; this.appointHistory = widget.appointment; }); requestPermissions(); super.initState(); } @override Widget build(BuildContext context) { ProjectViewModel projectViewModel = Provider.of(context); return BaseView( allowAny: true, builder: (_, model, widget) => AppScaffold( baseViewModel: model, isShowDecPage: false, body: Column( children: [ Expanded( child: SingleChildScrollView( physics: BouncingScrollPhysics(), padding: EdgeInsets.only(top: 16, bottom: 16, right: 21, left: 21), child: Form( key: formKey, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( TranslationBase.of(context).likeToHear, // textAlign: TextAlign.center, style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: Color(0xff2B353E), letterSpacing: -0.64, height: 23 / 16), ), SizedBox(height: 21), InkWell( onTap: () { confirmBox(model); }, child: Container( padding: EdgeInsets.only(left: 16, right: 16, bottom: 15, top: 15), alignment: Alignment.center, decoration: BoxDecoration( borderRadius: BorderRadius.circular(15), color: Colors.white, border: Border.all( color: Color(0xffefefef), width: 1, ), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( getSelected(context), style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, letterSpacing: -0.48, color: Color(0xff2B353E), ), ), Icon( Icons.keyboard_arrow_down, size: 22, color: Colors.grey, ) ], ), ), ), SizedBox(height: 12), if (appointHistory != null && messageType == MessageType.ComplaintOnAnAppointment) InkWell( onTap: () { setState(() { isShowListAppointHistory = true; }); }, child: DoctorCard( onTap: null, isInOutPatient: appointHistory.isInOutPatient, name: appointHistory.doctorTitle + " " + appointHistory.doctorNameObj, // billNo: _appointmentResult.invoiceNo, profileUrl: appointHistory.doctorImageURL, subName: appointHistory.projectName, isLiveCareAppointment: appointHistory.isLiveCareAppointment, date: DateUtil.convertStringToDate(appointHistory.appointmentDate), rating: appointHistory.actualDoctorRate + 0.0, appointmentTime: appointHistory.startTime.substring(0, 5), ), ), SizedBox(height: 12), if (messageType == MessageType.ComplaintOnAnAppointment && appoList.length != 0 && isShowListAppointHistory) Container( margin: EdgeInsets.only(bottom: 10.0), height: appoList.length > 2 ? MediaQuery.of(context).size.height * 0.35 : MediaQuery.of(context).size.height * 0.17, child: ListView.builder( itemCount: appoList.length, itemBuilder: (context, index) => InkWell( onTap: () { setState(() { appointHistory = appoList[index]; isShowListAppointHistory = false; }); }, child: DoctorCard( onTap: null, isInOutPatient: appoList[index].isInOutPatient, name: appoList[index].doctorTitle + " " + appoList[index].doctorNameObj, profileUrl: appoList[index].doctorImageURL, subName: appoList[index].projectName, isLiveCareAppointment: appoList[index].isLiveCareAppointment, date: DateUtil.convertStringToDate(appoList[index].appointmentDate), rating: appoList[index].actualDoctorRate + 0.0, appointmentTime: appoList[index].startTime.substring(0, 5), ), ), ), ), inputWidget(TranslationBase.of(context).subject, "", titleController), SizedBox(height: 12), inputWidget(TranslationBase.of(context).message, "", messageController, lines: 11, suffixTap: () { openSpeechReco(); }), SizedBox(height: 12), InkWell( onTap: () { ImageOptions.showImageOptions(context, (String image, File file) { setState(() { images.add(image); }); }); }, child: Container( padding: EdgeInsets.only(left: 16, right: 16, bottom: 15, top: 15), alignment: Alignment.center, decoration: BoxDecoration( borderRadius: BorderRadius.circular(15), color: Colors.white, border: Border.all( color: Color(0xffefefef), width: 1, ), ), child: Row( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.attach_file, color: Color(0xff2B353E), ), Text( TranslationBase.of(context).selectAttachment, style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: Color(0xff2B353E), letterSpacing: -0.64, height: 23 / 16), ), ], ), ), ), ...List.generate( images.length, (index) => Container( margin: EdgeInsets.all(10), padding: EdgeInsets.all(8.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Icon(FontAwesomeIcons.paperclip), SizedBox( width: 8, ), Texts( 'image ${index + 1}.png', ), ], ), InkWell( onTap: () { setState(() { images.remove(images[index]); }); }, child: Icon( FontAwesomeIcons.trashAlt, color: Colors.red[300], )) ], ), )), ], ), ), ), ), Container( color: Colors.white, padding: EdgeInsets.only(top: 16, bottom: 16, right: 21, left: 21), child: DefaultButton( TranslationBase.of(context).send, (titleController.text.toString().isEmpty || messageController.text.toString().isEmpty) ? null : () { final form = formKey.currentState; if (form.validate()) { GifLoaderDialogUtils.showMyDialog(context); model .sendCOCItem( title: titleController.text, attachment: images.length > 0 ? images[0] : "", details: messageController.text, cOCTypeName: getCOCName(), appointHistory: messageType == MessageType.ComplaintOnAnAppointment ? appointHistory : null) .then((value) { if (value) { setState(() { titleController.text = ""; messageController.text = ""; images = []; }); setMessageType(MessageType.NON); GifLoaderDialogUtils.hideDialog(context); AppToast.showSuccessToast(message: TranslationBase.of(context).yourFeedback); } else { AppToast.showErrorToast(message: model.error); GifLoaderDialogUtils.hideDialog(context); } }); } }, color: Color(0xffD02127), textColor: (titleController.text.toString().isEmpty || messageController.text.toString().isEmpty) ? Color(0xff000000) : Colors.white, disabledColor: Color(0xffEAEAEA), ), ), ], ), ), ); } Widget inputWidget(String _labelText, String _hintText, TextEditingController _controller, {VoidCallback suffixTap, bool isEnable = true, bool hasSelection = false, int lines}) { return Container( padding: EdgeInsets.only(left: 16, right: 16, bottom: 15, top: 15), alignment: Alignment.center, decoration: BoxDecoration( borderRadius: BorderRadius.circular(15), color: Colors.white, border: Border.all( color: Color(0xffefefef), width: 1, ), ), child: InkWell( onTap: hasSelection ? () {} : null, child: Row( children: [ Expanded( child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( _labelText, style: TextStyle( fontSize: 11, fontWeight: FontWeight.w600, color: Color(0xff2B353E), letterSpacing: -0.44, ), ), TextField( enabled: isEnable, scrollPadding: EdgeInsets.zero, keyboardType: TextInputType.text, controller: _controller, maxLines: lines, onChanged: (value) => {setState(() {})}, style: TextStyle( fontSize: 14, height: 21 / 14, fontWeight: FontWeight.w400, color: Color(0xff2B353E), letterSpacing: -0.44, ), decoration: InputDecoration( isDense: true, hintText: _hintText, hintStyle: TextStyle( fontSize: 14, height: 21 / 14, fontWeight: FontWeight.w400, color: Color(0xff575757), letterSpacing: -0.56, ), suffixIconConstraints: BoxConstraints(minWidth: 50), suffixIcon: suffixTap == null ? null : IconButton(icon: Icon(Icons.mic, color: Color(0xff2E303A)), onPressed: suffixTap), contentPadding: EdgeInsets.zero, border: InputBorder.none, focusedBorder: InputBorder.none, enabledBorder: InputBorder.none, ), ), ], ), ), if (hasSelection) Icon(Icons.keyboard_arrow_down_outlined), ], ), ), ); } String getCOCName() { switch (messageType) { case MessageType.ComplaintOnAnAppointment: return "1"; break; case MessageType.ComplaintWithoutAppointment: return "2"; break; case MessageType.Question: return "3"; break; case MessageType.Compliment: return "4"; break; case MessageType.Suggestion: return "6"; break; case MessageType.NON: return "5"; break; } return ""; } // Show Dialog function void confirmBox(FeedbackViewModel model) { DoctorsListService service = new DoctorsListService(); showDialog( context: context, child: FeedbackTypeDialog( messageTypeDialog: messageType, onValueSelected: (MessageType value) { if (value == MessageType.ComplaintOnAnAppointment) { appoList.clear(); GifLoaderDialogUtils.showMyDialog(context); service.getPatientAppointmentHistory(false, context, isForCOC: true).then((res) { GifLoaderDialogUtils.hideDialog(context); setState(() { if (res['MessageStatus'] == 1) { if (res['AppoimentAllHistoryResultList'].length != 0) { res['AppoimentAllHistoryResultList'].forEach((v) { appoList.add(new AppoitmentAllHistoryResultList.fromJson(v)); }); setState(() { appointHistory = null; isShowListAppointHistory = true; }); } else {} } else { } }); }).catchError((err) { GifLoaderDialogUtils.hideDialog(context); // print(err); // AppToast.showErrorToast(message: err); // Navigator.of(context).pop(); }); } else { isShowListAppointHistory = false; } setMessageType(value); }, )); } openSpeechReco() async { new RoboSearch(context: context).showAlertDialog(context); _currentLocaleId = TranslationBase.of(AppGlobal.context).locale.languageCode; bool available = await speech.initialize(onStatus: statusListener, onError: errorListener); if (available) { speech.listen( onResult: resultListener, listenMode: stt.ListenMode.confirmation, localeId: _currentLocaleId == 'en' ? 'en-US' : 'ar-SA', ); } else { print("The user has denied the use of speech recognition."); } } void errorListener(SpeechRecognitionError error) { event.setValue({"searchText": 'null'}); //SpeechToText.closeAlertDialog(context); print(error); } void statusListener(String status) { reconizedWord = status == 'listening' ? 'Lisening...' : 'Sorry....'; } void requestPermissions() async { Map statuses = await [ Permission.microphone, ].request(); print(statuses); } void resultListener(result) { reconizedWord = result.recognizedWords; event.setValue({"searchText": reconizedWord}); if (result.finalResult == true) { setState(() { RoboSearch.closeAlertDialog(context); speech.stop(); messageController.text = reconizedWord + '\n'; }); } } Future initSpeechState() async { bool hasSpeech = await speech.initialize(onError: errorListener, onStatus: statusListener); print(hasSpeech); if (!mounted) return; } } class FeedbackTypeDialog extends StatefulWidget { final Function(MessageType) onValueSelected; final MessageType messageTypeDialog; const FeedbackTypeDialog({Key key, this.onValueSelected, this.messageTypeDialog = MessageType.NON}) : super(key: key); @override State createState() => new FeedbackTypeDialogState(); } class FeedbackTypeDialogState extends State { MessageType messageTypeDialog = MessageType.NON; setMessageDialogType(MessageType messageType) { setState(() { messageTypeDialog = messageType; }); } @override void initState() { messageTypeDialog = widget.messageTypeDialog; super.initState(); } Widget build(BuildContext context) { return BaseView( builder: (_, model, widge) => SimpleDialog( title: Text( TranslationBase.of(context).messageType, textAlign: TextAlign.center, ), children: [ Container( // padding: const EdgeInsets.all(10.0), child: Column( children: [ Divider( height: 2.5, color: Colors.grey[500], ), Row( children: [ Expanded( flex: 1, child: InkWell( onTap: () => setMessageDialogType(MessageType.NON), child: ListTile( title: Texts(TranslationBase.of(context).notClassified), leading: Radio( value: MessageType.NON, groupValue: messageTypeDialog, activeColor: CustomColors.accentColor, onChanged: (MessageType value) => setMessageDialogType(value), ), ), ), ) ], ), SizedBox( height: 5.0, ), Row( children: [ Expanded( flex: 1, child: InkWell( onTap: () => setMessageDialogType(MessageType.ComplaintOnAnAppointment), child: ListTile( title: Texts(TranslationBase.of(context).complainAppo), leading: Radio( value: MessageType.ComplaintOnAnAppointment, groupValue: messageTypeDialog, activeColor: CustomColors.accentColor, onChanged: (MessageType value) => setMessageDialogType(value), ), ), ), ) ], ), SizedBox( height: 5.0, ), Row( children: [ Expanded( flex: 1, child: InkWell( onTap: () => setMessageDialogType(MessageType.ComplaintWithoutAppointment), child: ListTile( title: Texts(TranslationBase.of(context).complainWithoutAppo), leading: Radio( value: MessageType.ComplaintWithoutAppointment, groupValue: messageTypeDialog, activeColor: CustomColors.accentColor, onChanged: (MessageType value) => setMessageDialogType(value), ), ), ), ) ], ), SizedBox( height: 5.0, ), Row( children: [ Expanded( flex: 1, child: InkWell( onTap: () => setMessageDialogType(MessageType.Question), child: ListTile( title: Texts(TranslationBase.of(context).question), leading: Radio( value: MessageType.Question, groupValue: messageTypeDialog, activeColor: CustomColors.accentColor, onChanged: (MessageType value) => setMessageDialogType(value), ), ), ), ) ], ), SizedBox( height: 5.0, ), Row( children: [ Expanded( flex: 1, child: InkWell( onTap: () => setMessageDialogType(MessageType.Compliment), child: ListTile( title: Texts(TranslationBase.of(context).compliment), leading: Radio( value: MessageType.Compliment, groupValue: messageTypeDialog, activeColor: CustomColors.accentColor, onChanged: (MessageType value) => setMessageDialogType(value), ), ), ), ) ], ), SizedBox( height: 5.0, ), Row( children: [ Expanded( flex: 1, child: InkWell( onTap: () => setMessageDialogType(MessageType.Suggestion), child: ListTile( title: Texts(TranslationBase.of(context).suggestion), leading: Radio( value: MessageType.Suggestion, groupValue: messageTypeDialog, activeColor: CustomColors.accentColor, onChanged: (MessageType value) => setMessageDialogType(value), ), ), ), ) ], ), SizedBox( height: 5.0, ), Divider( height: 2.5, color: Colors.grey[500], ), SizedBox( height: 5, ), Row( // mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( flex: 1, child: InkWell( onTap: () { Navigator.pop(context); }, child: Padding( padding: EdgeInsets.all(8.0), child: Container( child: Center( child: Texts( TranslationBase.of(context).cancel, color: CustomColors.accentColor, ), ), ), ), ), ), Container( width: 1, height: 30, color: Colors.grey[500], ), Expanded( flex: 1, child: InkWell( onTap: () { widget.onValueSelected(messageTypeDialog); Navigator.pop(context); }, child: Padding( padding: const EdgeInsets.all(8.0), child: Center( child: Texts( TranslationBase.of(context).ok, fontWeight: FontWeight.w400, )), ), )), ], ) ], ), ), ], ), ); } }