diff --git a/assets/images/speech_to_text.svg b/assets/images/speech_to_text.svg new file mode 100644 index 00000000..4474a1ec --- /dev/null +++ b/assets/images/speech_to_text.svg @@ -0,0 +1,3 @@ + + + diff --git a/lib/controllers/api_routes/urls.dart b/lib/controllers/api_routes/urls.dart index 74b60575..e6659caa 100644 --- a/lib/controllers/api_routes/urls.dart +++ b/lib/controllers/api_routes/urls.dart @@ -5,6 +5,7 @@ class URLs { static const host1 = "https://atomsmdev.hmg.com"; // local UAT url static String _baseUrl = "$_host/mobile"; + // static String _baseUrl = "$_host/v2/mobile"; // new V2 apis static String _host = host1; diff --git a/lib/new_views/common_widgets/app_text_form_field.dart b/lib/new_views/common_widgets/app_text_form_field.dart index 476576b0..3fe19929 100644 --- a/lib/new_views/common_widgets/app_text_form_field.dart +++ b/lib/new_views/common_widgets/app_text_form_field.dart @@ -28,6 +28,7 @@ class AppTextFormField extends StatefulWidget { final Color backgroundColor; final bool alignLabelWithHint; final bool showShadow; + final bool showWithoutDecoration; final void Function() onTap; const AppTextFormField({ @@ -54,6 +55,7 @@ class AppTextFormField extends StatefulWidget { this.onAction, this.backgroundColor, this.showShadow = true, + this.showWithoutDecoration = false, this.alignLabelWithHint, this.onTap, }) : super(key: key); @@ -72,59 +74,63 @@ class _AppTextFormFieldState extends State { @override Widget build(BuildContext context) { final border = UnderlineInputBorder(borderSide: BorderSide.none, borderRadius: BorderRadius.circular(10)); + + Widget textField = TextFormField( + focusNode: widget.node, + enabled: widget.enable, + onSaved: widget.onSaved, + initialValue: widget.controller != null ? null : widget.initialValue, + validator: widget.validator, + onChanged: widget.onChange, + obscureText: widget.obscureText ?? false, + keyboardType: widget.textInputType, + maxLines: widget.textInputType == TextInputType.multiline ? 4 : 1, + obscuringCharacter: "*", + controller: widget.controller, + textInputAction: widget.textInputType == TextInputType.multiline ? null : widget.textInputAction ?? TextInputAction.next, + onEditingComplete: widget.onAction ?? () => FocusScope.of(context).nextFocus(), + style: AppTextStyle.body1?.copyWith(fontWeight: FontWeight.w500), + onTap: widget.onTap, + decoration: InputDecoration( + alignLabelWithHint: widget.alignLabelWithHint, + border: border, + disabledBorder: border, + focusedBorder: border, + enabledBorder: border, + errorBorder: border, + contentPadding: EdgeInsets.symmetric(vertical: 8.toScreenHeight, horizontal: 16.toScreenWidth), + constraints: const BoxConstraints(), + suffixIconConstraints: const BoxConstraints(minWidth: 0), + filled: true, + fillColor: widget.backgroundColor ?? + (context.isDark && (widget.enable == false) + ? AppColor.neutral50 + : (widget.enable == false) + ? AppColor.neutral40 + : AppColor.background(context)), + errorStyle: AppTextStyle.tiny.copyWith(color: context.isDark ? AppColor.red50 : AppColor.red60), + floatingLabelStyle: AppTextStyle.body1?.copyWith(fontWeight: FontWeight.w500, color: context.isDark ? null : AppColor.neutral20), + hintText: widget.hintText ?? "", + labelText: widget.labelText ?? "", + prefixIcon: widget.prefixIcon ?? + (widget.prefixIconData == null + ? null + : Icon( + widget.prefixIconData, + size: widget.prefixIconSize == null ? 20.toScreenWidth : (widget.prefixIconSize - 10).toScreenWidth, + color: AppColor.neutral70, + )), + suffixIcon: widget.suffixIcon, + ), + ); + + if (widget.showWithoutDecoration) return textField; return Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(10), boxShadow: widget.showShadow ? [BoxShadow(color: Colors.black.withOpacity(0.05), blurRadius: 10)] : null, ), - child: TextFormField( - focusNode: widget.node, - enabled: widget.enable, - onSaved: widget.onSaved, - initialValue: widget.controller != null ? null : widget.initialValue, - validator: widget.validator, - onChanged: widget.onChange, - obscureText: widget.obscureText ?? false, - keyboardType: widget.textInputType, - maxLines: widget.textInputType == TextInputType.multiline ? 4 : 1, - obscuringCharacter: "*", - controller: widget.controller, - textInputAction: widget.textInputType == TextInputType.multiline ? null : widget.textInputAction ?? TextInputAction.next, - onEditingComplete: widget.onAction ?? () => FocusScope.of(context).nextFocus(), - style: AppTextStyle.body1?.copyWith(fontWeight: FontWeight.w500), - onTap: widget.onTap, - decoration: InputDecoration( - alignLabelWithHint: widget.alignLabelWithHint, - border: border, - disabledBorder: border, - focusedBorder: border, - enabledBorder: border, - errorBorder: border, - contentPadding: EdgeInsets.symmetric(vertical: 8.toScreenHeight, horizontal: 16.toScreenWidth), - constraints: const BoxConstraints(), - suffixIconConstraints: const BoxConstraints(minWidth: 0), - filled: true, - fillColor: widget.backgroundColor ?? - (context.isDark && (widget.enable == false) - ? AppColor.neutral50 - : (widget.enable == false) - ? AppColor.neutral40 - : AppColor.background(context)), - errorStyle: AppTextStyle.tiny.copyWith(color: context.isDark ? AppColor.red50 : AppColor.red60), - floatingLabelStyle: AppTextStyle.body1?.copyWith(fontWeight: FontWeight.w500, color: context.isDark ? null : AppColor.neutral20), - hintText: widget.hintText ?? "", - labelText: widget.labelText ?? "", - prefixIcon: widget.prefixIcon ?? - (widget.prefixIconData == null - ? null - : Icon( - widget.prefixIconData, - size: widget.prefixIconSize == null ? 20.toScreenWidth : (widget.prefixIconSize - 10).toScreenWidth, - color: AppColor.neutral70, - )), - suffixIcon: widget.suffixIcon, - ), - ), + child: textField, ); } } diff --git a/lib/views/pages/device_transfer/search_asset_page.dart b/lib/views/pages/device_transfer/search_asset_page.dart index 15d89fb9..40958313 100644 --- a/lib/views/pages/device_transfer/search_asset_page.dart +++ b/lib/views/pages/device_transfer/search_asset_page.dart @@ -84,45 +84,41 @@ class _SearchAssetPageState extends State { ), body: Column( children: [ - Expanded( - flex: 2, - child: HorizontalListWidget( - list: searchBy, - callBackFunction: (index) { + HorizontalListWidget( + list: searchBy, + callBackFunction: (index) { + setState(() { + _selectedIndex = index; + }); + }, + ).paddingOnly(top: 16, bottom: 0), + Form( + key: _formKey, + child: AppTextFormField( + controller: _searchController, + textInputAction: TextInputAction.search, + labelText: "${context.translation.searchBy} ${searchBy[_selectedIndex]}", + onAction: _search, + onChange: (text) { + _searchController.text = text; + _searchController.selection = TextSelection.fromPosition(TextPosition(offset: _searchController.text.length)); + setState(() {}); + }, + onSaved: (value) { setState(() { - _selectedIndex = index; + search = AssetSearch(); }); + _setValue(value); }, - ).paddingOnly(top: 16), - ), - Expanded( - flex: 3, - child: Form( - key: _formKey, - child: AppTextFormField( - controller: _searchController, - labelText: "${context.translation.searchBy} ${searchBy[_selectedIndex]}", - onChange: (text) { - _searchController.text = text; - _searchController.selection = TextSelection.fromPosition(TextPosition(offset: _searchController.text.length)); - setState(() {}); - }, - onSaved: (value) { - setState(() { - search = AssetSearch(); - }); - _setValue(value); - }, - suffixIcon: IconButton( - icon: const Icon(Icons.search), - onPressed: _searchController.text.isNotEmpty ? _search : null, - color: AppColor.neutral20, - ).paddingOnly(end: 6), - ).paddingOnly(top: 18, start: 18, end: 18), - ), + suffixIcon: IconButton( + icon: const Icon(Icons.search), + splashColor: Colors.transparent, + onPressed: _searchController.text.isNotEmpty ? _search : null, + color: AppColor.neutral20, + ).paddingOnly(end: 0), + ).paddingOnly(top: 16, start: 16, end: 16, bottom: 8), ), Expanded( - flex: 25, child: _searchableList.isEmpty ? _isFirst ? const SizedBox() @@ -141,6 +137,7 @@ class _SearchAssetPageState extends State { child: ListView.separated( itemCount: _searchableList.length, separatorBuilder: (listContext, itemIndex) => 8.height, + padding: EdgeInsets.all(16), itemBuilder: (listContext, itemIndex) { return AssetItemListView( device: _searchableList[itemIndex], @@ -151,7 +148,7 @@ class _SearchAssetPageState extends State { selectButton: Text(context.translation.select, style: AppTextStyles.bodyText.copyWith(color: AppColor.blueStatus(context))), ); }, - ).paddingOnly(start: 16, end: 16), + ), ), ) ], @@ -189,7 +186,7 @@ class _SearchAssetPageState extends State { } } - // bool _showResetButton() { - // return (_searchController?.text?.isNotEmpty ?? false); - // } +// bool _showResetButton() { +// return (_searchController?.text?.isNotEmpty ?? false); +// } } diff --git a/lib/views/pages/user/requests/create_service_request_page.dart b/lib/views/pages/user/requests/create_service_request_page.dart index bed86ba9..fed31c89 100644 --- a/lib/views/pages/user/requests/create_service_request_page.dart +++ b/lib/views/pages/user/requests/create_service_request_page.dart @@ -8,7 +8,6 @@ import 'package:test_sa/controllers/providers/api/user_provider.dart'; import 'package:test_sa/controllers/providers/settings/setting_provider.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/pending_service_request_model.dart'; import 'package:test_sa/models/service_request/service_request.dart'; @@ -20,11 +19,9 @@ import 'package:test_sa/views/widgets/bottom_sheets/pending_request_bottom_sheet import 'package:test_sa/views/widgets/equipment/asset_picker.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/speech_to_text/speech_to_text.dart'; +import 'package:test_sa/views/widgets/sound/TextSpeechRecordWidget.dart'; import '../../../../new_views/app_style/app_color.dart'; -import '../../../../new_views/common_widgets/app_text_form_field.dart'; import '../../../../new_views/common_widgets/default_app_bar.dart'; import '../../../../providers/service_request_providers/priority_provider.dart'; import '../../../../providers/service_request_providers/type_of_request_provider.dart'; @@ -292,26 +289,37 @@ class CreateServiceRequestPageState extends State { // ); // }), - Align( - alignment: AlignmentDirectional.centerStart, - child: context.translation.callComments.heading5(context), - ), + // Align( + // alignment: AlignmentDirectional.centerStart, + // child: context.translation.callComments.heading5(context), + // ), 8.height, - SpeechToTextButton( - controller: _commentController, - ), + // SpeechToTextButton( + // controller: _commentController, + // ), + // 8.height, + // AppTextFormField( + // controller: _commentController, + // labelText: context.translation.problemDesc, + // suffixIcon: "warning".toSvgAsset(color: context.isDark ? AppColor.neutral10 : AppColor.neutral20, width: 24).paddingOnly(end: 16), + // initialValue: _serviceRequest.callComments, + // onSaved: (text) { + // _serviceRequest.callComments = text; + // }, + // ), + // 8.height, + // RecordSound( + // onRecord: (audio) { + // _serviceRequest.audio = audio; + // }, + // enabled: widget.serviceRequest == null ? true : false, + // ), 8.height, - AppTextFormField( - controller: _commentController, - labelText: context.translation.problemDesc, - suffixIcon: "warning".toSvgAsset(color: context.isDark ? AppColor.neutral10 : AppColor.neutral20, width: 24).paddingOnly(end: 16), - initialValue: _serviceRequest.callComments, - onSaved: (text) { - _serviceRequest.callComments = text; + TextSpeechRecordWidget( + initialMessage: _serviceRequest.callComments, + onMessageChange: (message) { + _serviceRequest.callComments = message; }, - ), - 8.height, - RecordSound( onRecord: (audio) { _serviceRequest.audio = audio; }, diff --git a/lib/views/widgets/bottom_sheets/asset_detail_bottom_sheet.dart b/lib/views/widgets/bottom_sheets/asset_detail_bottom_sheet.dart index f92aa52c..55a69da3 100644 --- a/lib/views/widgets/bottom_sheets/asset_detail_bottom_sheet.dart +++ b/lib/views/widgets/bottom_sheets/asset_detail_bottom_sheet.dart @@ -49,8 +49,8 @@ class AssetDetailBottomSheet extends StatelessWidget { children: [ "${context.translation.assetNo}: ${asset.assetNumber}".bodyText(context), "${context.translation.modelName}: ${asset.modelDefinition.modelName}".bodyText(context), - "${context.translation.supplier}: ${asset.supplier?.suppliername ?? "-"}".bodyText(context), - "${context.translation.manufacture}: ${asset.modelDefinition.manufacturerName}".bodyText(context), + "${context.translation.supplier}: ${asset.supplier?.suppliername?.cleanupWhitespace?.capitalizeFirstOfEach ?? "-"}".bodyText(context), + "${context.translation.manufacture}: ${asset.modelDefinition.manufacturerName?.cleanupWhitespace?.capitalizeFirstOfEach}".bodyText(context), //"${context.translation.location}: ${assetModel.site.custName?.cleanupWhitespace?.capitalizeFirstOfEach}".bodyText(context), ], ).expanded, diff --git a/lib/views/widgets/horizontal_list_widget.dart b/lib/views/widgets/horizontal_list_widget.dart index 4e814949..5a506a7d 100644 --- a/lib/views/widgets/horizontal_list_widget.dart +++ b/lib/views/widgets/horizontal_list_widget.dart @@ -26,7 +26,7 @@ class _HorizontalListWidgetState extends State { itemCount: widget.list.length, scrollDirection: Axis.horizontal, shrinkWrap: true, - padding: EdgeInsetsDirectional.only(start: 16.toScreenWidth), + padding: EdgeInsetsDirectional.only(start: 16.toScreenWidth,end: 16), itemBuilder: (context, index) => Container( margin: EdgeInsets.symmetric(horizontal: 4.toScreenWidth), padding: EdgeInsets.symmetric(horizontal: 20.toScreenWidth), diff --git a/lib/views/widgets/sound/TextSpeechRecordWidget.dart b/lib/views/widgets/sound/TextSpeechRecordWidget.dart new file mode 100644 index 00000000..27d553bd --- /dev/null +++ b/lib/views/widgets/sound/TextSpeechRecordWidget.dart @@ -0,0 +1,352 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:permission_handler/permission_handler.dart'; +import 'package:provider/provider.dart'; +import 'package:record_mp3/record_mp3.dart'; +import 'package:rive/rive.dart'; +import 'package:speech_to_text/speech_recognition_error.dart'; +import 'package:speech_to_text/speech_recognition_result.dart'; +import 'package:speech_to_text/speech_to_text.dart'; +import 'package:test_sa/controllers/providers/settings/setting_provider.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/views/widgets/sound/sound_player.dart'; + +import '../../../new_views/app_style/app_color.dart'; +import '../../../new_views/common_widgets/app_text_form_field.dart'; + +class TextSpeechRecordWidget extends StatefulWidget { + final Function(String) onRecord; + final Function(String) onStop; + final bool enabled; + final Function(String) onMessageChange; + final String initialMessage; + + const TextSpeechRecordWidget({Key key, @required this.onRecord, this.onStop, this.enabled = true, this.onMessageChange, this.initialMessage}) : super(key: key); + + @override + State createState() => _RecordSoundState(); +} + +class _RecordSoundState extends State { + // FlutterSoundRecorder _myRecorder = FlutterSoundRecorder(); + + bool _recorderIsOpened = false; + bool _recording = false; + bool _played = false; + String _record; + Artboard _rive; + Timer _timer; + TextEditingController _timeController; + TextEditingController _commentController; + bool _speechEnabled = false; + + FocusNode node = FocusNode(); + + @override + void setState(VoidCallback fn) { + if (mounted) super.setState(fn); + } + + final SpeechToText _speechToText = SpeechToText(); + + /// This has to happen only once per app + void _initSpeech() async { + _speechEnabled = await _speechToText.initialize( + onError: (SpeechRecognitionError error) async { + Fluttertoast.showToast(msg: "failed to convert text to speech"); + setState(() {}); + }, + ); + } + + SettingProvider _settingProvider; + + @override + void initState() { + super.initState(); + _initSpeech(); + _timeController = TextEditingController(); + _commentController = TextEditingController(); + node.unfocus(); + _recorderIsOpened = true; + // RecordMp3.instance.start(recordFilePath, (type) { + // // record fail callback + // }); + // _myRecorder.openRecorder().then((value) { + // _recorderIsOpened = true; + // setState(() {}); + // }); + + // Load the animation file from the bundle, note that you could also + // download this. The RiveFile just expects a list of bytes. + rootBundle.load('assets/rives/recording.riv').then( + (data) async { + // Load the RiveFile from the binary data. + final file = RiveFile.import(data); + // The artboard is the root of the animation and gets drawn in the + // Rive widget. + final artboard = file.mainArtboard; + // Add a controller to play back a known animation on the main/default + // artboard.We store a reference to it so we can toggle playback. + artboard.addController(SimpleAnimation('recording')); + _rive = artboard; + + setState(() {}); + }, + ); + } + + @override + void dispose() { + _timeController?.dispose(); + // Be careful : you must `close` the audio session when you have finished with it. + RecordMp3.instance.stop(); + //_myRecorder.closeRecorder(); + // _myRecorder = null; + super.dispose(); + } + + String recordingFileDirectory; + + _startRecording() async { + PermissionStatus status = await Permission.microphone.request(); + if (!status.isGranted) { + PermissionStatus status = await Permission.microphone.request(); + if (!status.isGranted) { + Fluttertoast.showToast(msg: "Permission Denied"); + return; + } + } + _timer = Timer.periodic(const Duration(seconds: 1), (timer) { + setState(() { + String duration = Duration(seconds: timer?.tick).toString(); + duration = duration.substring(duration.indexOf(":") + 1, duration.indexOf(".")); + + String recordTime = ((timer?.tick ?? 0) / 60)?.toStringAsFixed(2)?.replaceFirst(".", ":"); + // print("recordTime:$recordTime"); + if (recordTime.length == 4 || recordTime.length == 7) { + recordTime = "0$recordTime"; + } + _timeController.text = duration; + }); + }); + _rive.addController(SimpleAnimation('recording')); + if (!_recorderIsOpened) { + // await _myRecorder.openRecorder(); + _recorderIsOpened = true; + } + final Directory tempDir = await getTemporaryDirectory(); + recordingFileDirectory = "${tempDir.path}/record_${DateTime.now().millisecondsSinceEpoch}.mp3"; + RecordMp3.instance.start(recordingFileDirectory, (type) { + // record fail callback + }); + + // await _myRecorder.startRecorder(toFile: "record_${DateTime.now().millisecondsSinceEpoch}.mp3", codec: Codec.aacADTS, sampleRate: 360000, bitRate: 360000); + + _recording = true; + setState(() {}); + } + + _stopRecording() async { + if (!_recording) { + setState(() {}); + return; + } + if (_timer?.isActive ?? false) { + _timer.cancel(); + } + RecordMp3.instance.stop(); + + //String path = (await _myRecorder.stopRecorder()).toString(); + _record = recordingFileDirectory; + widget.onRecord(recordingFileDirectory); + _recording = false; + setState(() {}); + } + + void _startListening() async { + _speechEnabled = _speechToText.isAvailable; + if (_speechToText.isListening) { + Fluttertoast.showToast(msg: "Currently in use"); + return; + } + if (!_speechEnabled) return; + await _speechToText.listen( + onResult: (SpeechRecognitionResult result) { + _commentController.text = result.recognizedWords; + widget.onMessageChange(_commentController.text); + setState(() {}); + }, + localeId: _settingProvider.speechToText); + setState(() {}); + } + + void _stopListening() async { + await _speechToText.stop(); + setState(() {}); + } + + _cancelRecording() async { + if (!_recording) return; + RecordMp3.instance.stop(); + // String path = await _myRecorder.stopRecorder(); + // _myRecorder.deleteRecord(fileName: path); + _rive.addController(SimpleAnimation('delete')); + + // rebuild(); + _recording = false; + await Future.delayed(const Duration(seconds: 1)); + if (!_recording) setState(() {}); + // _message.memoryAudio.; + } + + @override + Widget build(BuildContext context) { + if (node.hasFocus && widget.enabled) node.unfocus(); + _settingProvider ??= Provider.of(context); + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (!_recording) ...[ + AppTextFormField( + controller: _commentController, + labelText: _speechToText.isListening ? "Listening..." : context.translation.callComments, + alignLabelWithHint: true, + showWithoutDecoration: true, + backgroundColor: Colors.transparent, + textInputType: TextInputType.multiline, + // suffixIcon: + // (_recording ? "record".toLottieAsset(height: 24) : (_record != null ? "trash" : "mic").toSvgAsset(color: context.isDark ? AppColor.neutral10 : AppColor.neutral20, height: 24)) + // .paddingOnly(end: 16), + initialValue: widget.initialMessage, + onChange: (text) { + widget.onMessageChange(text); + }, + onSaved: (text) { + widget.onMessageChange(text); + }, + ).expanded, + (_speechToText.isListening + ? SizedBox( + height: 24.toScreenHeight, + width: 24.toScreenWidth, + child: const Icon( + Icons.fiber_manual_record, + color: Colors.red, + )).onPress(() { + _stopListening(); + }) + : PopupMenuButton( + child: SizedBox( + height: 24.toScreenHeight, + width: 24.toScreenWidth, + child: "speech_to_text".toSvgAsset(color: context.isDark ? AppColor.neutral10 : AppColor.neutral20, height: 24, width: 24), + ), + onSelected: (String selectedLanguage) { + _settingProvider.setSpeechToText(selectedLanguage); + _startListening(); + }, + itemBuilder: (BuildContext context) { + return [ + const PopupMenuItem( + value: 'ar', + child: Text('Arabic'), + ), + const PopupMenuItem( + value: 'en', + child: Text('English'), + ), + ]; + }, + )) + .paddingOnly(end: 16,top: 16), + ], + + if (_recording) + AppTextFormField( + enable: widget.enabled, + node: node, + backgroundColor: Colors.transparent, + showWithoutDecoration: true, + controller: _timeController, + labelText: context.translation.recordVoice, + textInputType: TextInputType.multiline, + alignLabelWithHint: true, + 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), + ).expanded, + + // SizedBox( + // height: 24.toScreenHeight, + // width: 24.toScreenWidth, + // child: "speech_to_text".toSvgAsset(color: context.isDark ? AppColor.neutral10 : AppColor.neutral20, height: 24, width: 24), + // ).paddingOnly(end: 16).onPress(_speechEnabled + // ? () async { + // if (!_speechEnabled) { + // Fluttertoast.showToast(msg: "microphone not available"); + // return; + // } + // if (_speechToText.isListening) { + // _stopListening(); + // } else { + // PopupMenuButton( + // onSelected: (String selectedLanguage) { + // _settingProvider.setSpeechToText(selectedLanguage); + // _startListening(); + // }, + // itemBuilder: (BuildContext context) => >[ + // const PopupMenuItem( + // value: "ar", + // child: Text('Arabic'), + // ), + // const PopupMenuItem( + // value: "en", + // child: Text('English'), + // ), + // ], + // ); + // } + // } + // : null), + SizedBox( + height: _recording ? 40 : 24.toScreenHeight, + width: _recording ? 40 : 24.toScreenWidth, + child: (_recording + ? "record".toLottieAsset(height: 24) + : (_record != null ? "trash" : "mic").toSvgAsset(color: context.isDark ? AppColor.neutral10 : AppColor.neutral20, height: 24, width: 24)), + ).paddingOnly(end: 16,top: 16).onPress(() { + if (_recording) { + _stopRecording(); + } else if (_record != null) { + _timeController?.text = "00:00"; + widget.onRecord(null); + _record = null; + setState(() {}); + } else { + _startRecording(); + } + }), + ], + ), + if (_record != null) ...[ + //8.height, + const Divider().defaultStyle(context), + ASoundPlayer(audio: _record).paddingOnly(top: 8, bottom: 16, start: 16, end: 16), + ] + ], + ).toShadowContainer(context, padding: 0); + } +}