text to speech, call comments & voice record added.

main_design2.1
Sikander Saleem 1 year ago
parent e65d2a44ed
commit a9042a834c

@ -0,0 +1,3 @@
<svg width="17" height="17" viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2.05 14.95C2.26 16.11 3.28 17 4.5 17H14.5C15.88 17 17 15.88 17 14.5V10.5C17 9.12 15.88 8 14.5 8H14V2.5C14 1.12 12.88 0 11.5 0H2.5C1.12 0 0 1.12 0 2.5V12.5C0 13.72 0.88 14.74 2.05 14.95ZM3.5 3H10.5C10.78 3 11 3.22 11 3.5C11 3.78 10.78 4 10.5 4H3.5C3.22 4 3 3.78 3 3.5C3 3.22 3.22 3 3.5 3ZM3.5 5.5H9.5C9.78 5.5 10 5.72 10 6C10 6.28 9.78 6.5 9.5 6.5H3.5C3.22 6.5 3 6.28 3 6C3 5.72 3.22 5.5 3.5 5.5ZM3 10.5C3 9.67 3.67 9 4.5 9H11.5C11.78 9 12 8.78 12 8.5V7.71L13.15 8.86C13.25 8.96 13.37 9.01 13.5 9.01H14.5C15.33 9.01 16 9.68 16 10.51V14.51C16 15.34 15.33 16.01 14.5 16.01H4.5C3.67 16.01 3 15.34 3 14.51V10.51V10.5Z" fill="#7D859A"/>
</svg>

After

Width:  |  Height:  |  Size: 744 B

@ -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;

@ -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<AppTextFormField> {
@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,
);
}
}

@ -84,45 +84,41 @@ class _SearchAssetPageState extends State<SearchAssetPage> {
),
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<SearchAssetPage> {
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<SearchAssetPage> {
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<SearchAssetPage> {
}
}
// bool _showResetButton() {
// return (_searchController?.text?.isNotEmpty ?? false);
// }
// bool _showResetButton() {
// return (_searchController?.text?.isNotEmpty ?? false);
// }
}

@ -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<CreateServiceRequestPage> {
// );
// }),
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;
},

@ -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,

@ -26,7 +26,7 @@ class _HorizontalListWidgetState extends State<HorizontalListWidget> {
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),

@ -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<TextSpeechRecordWidget> createState() => _RecordSoundState();
}
class _RecordSoundState extends State<TextSpeechRecordWidget> {
// 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<SettingProvider>(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<String>(
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<String>(
value: 'ar',
child: Text('Arabic'),
),
const PopupMenuItem<String>(
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<String>(
// onSelected: (String selectedLanguage) {
// _settingProvider.setSpeechToText(selectedLanguage);
// _startListening();
// },
// itemBuilder: (BuildContext context) => <PopupMenuEntry<String>>[
// const PopupMenuItem<String>(
// value: "ar",
// child: Text('Arabic'),
// ),
// const PopupMenuItem<String>(
// 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);
}
}
Loading…
Cancel
Save