From 3b6a0eea3c43a944f18025756de0a6910760e171 Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Thu, 6 Nov 2025 10:18:08 +0300 Subject: [PATCH] voice note ui added. --- lib/modules/cx_module/chat/chat_page.dart | 337 ++++++++++++++++------ 1 file changed, 242 insertions(+), 95 deletions(-) diff --git a/lib/modules/cx_module/chat/chat_page.dart b/lib/modules/cx_module/chat/chat_page.dart index 26b982fa..5c5e34a9 100644 --- a/lib/modules/cx_module/chat/chat_page.dart +++ b/lib/modules/cx_module/chat/chat_page.dart @@ -8,6 +8,8 @@ import 'package:test_sa/modules/cm_module/views/components/action_button/footer_ import 'package:test_sa/new_views/app_style/app_color.dart'; import 'package:test_sa/new_views/common_widgets/default_app_bar.dart'; +enum ChatState { idle, voiceRecordingStarted, voiceRecordingCompleted } + class ChatPage extends StatefulWidget { ChatPage({Key? key}) : super(key: key); @@ -25,13 +27,22 @@ class _ChatPageState extends State { final RecorderController recorderController = RecorderController(); PlayerController playerController = PlayerController(); + ChatState chatState = ChatState.idle; + @override void initState() { super.initState(); + playerController.addListener(() async { + // if (playerController.playerState == PlayerState.playing && playerController.maxDuration == await playerController.getDuration()) { + // await playerController.stopPlayer(); + // setState(() {}); + // } + }); } @override void dispose() { + playerController.dispose(); recorderController.dispose(); super.dispose(); } @@ -87,47 +98,37 @@ class _ChatPageState extends State { constraints: const BoxConstraints(minHeight: 56), child: Row( children: [ - if (recordedFilePath == null) ...[ - isAudioRecording - ? AudioWaveforms( - size: Size(MediaQuery.of(context).size.width, 56.0), - - // enableGesture: true, - - waveStyle: const WaveStyle(waveColor: AppColor.neutral50, extendWaveform: true, showMiddleLine: false), - padding: const EdgeInsets.only(left: 16), - recorderController: recorderController, // Customize how waveforms looks. - ).expanded - : TextFormField( - cursorColor: AppColor.neutral50, - style: AppTextStyles.bodyText.copyWith(color: AppColor.neutral50), - minLines: 1, - maxLines: 3, - textInputAction: TextInputAction.none, - keyboardType: TextInputType.multiline, - decoration: InputDecoration( - enabledBorder: InputBorder.none, - focusedBorder: InputBorder.none, - border: InputBorder.none, - errorBorder: InputBorder.none, - contentPadding: const EdgeInsets.only(left: 16, top: 8, bottom: 8), - alignLabelWithHint: true, - filled: true, - constraints: const BoxConstraints(), - suffixIconConstraints: const BoxConstraints(), - hintText: "Type your message here...", - hintStyle: AppTextStyles.bodyText.copyWith(color: const Color(0xffCCCCCC)), - // suffixIcon: Row( - // mainAxisSize: MainAxisSize.min, - // crossAxisAlignment: CrossAxisAlignment.end, - // mainAxisAlignment: MainAxisAlignment.end, - // children: [ - // - // 8.width, - // ], - // ) - ), - ).expanded, + if (chatState == ChatState.idle) ...[ + TextFormField( + cursorColor: AppColor.neutral50, + style: AppTextStyles.bodyText.copyWith(color: AppColor.neutral50), + minLines: 1, + maxLines: 3, + textInputAction: TextInputAction.none, + keyboardType: TextInputType.multiline, + decoration: InputDecoration( + enabledBorder: InputBorder.none, + focusedBorder: InputBorder.none, + border: InputBorder.none, + errorBorder: InputBorder.none, + contentPadding: const EdgeInsets.only(left: 16, top: 8, bottom: 8), + alignLabelWithHint: true, + filled: true, + constraints: const BoxConstraints(), + suffixIconConstraints: const BoxConstraints(), + hintText: "Type your message here...", + hintStyle: AppTextStyles.bodyText.copyWith(color: const Color(0xffCCCCCC)), + // suffixIcon: Row( + // mainAxisSize: MainAxisSize.min, + // crossAxisAlignment: CrossAxisAlignment.end, + // mainAxisAlignment: MainAxisAlignment.end, + // children: [ + // + // 8.width, + // ], + // ) + ), + ).expanded, IconButton( onPressed: () {}, style: const ButtonStyle( @@ -136,44 +137,191 @@ class _ChatPageState extends State { icon: "chat_attachment".toSvgAsset(width: 24, height: 24), constraints: const BoxConstraints(), ), - ], - if (recordedFilePath == null) - ...[] - else ...[ IconButton( onPressed: () async { - await playerController.preparePlayer(path: recordedFilePath!); - playerController.startPlayer(); + await recorderController.checkPermission(); + if (recorderController.hasPermission) { + chatState = ChatState.voiceRecordingStarted; + recorderController.record(); + setState(() {}); + } else { + "Audio permission denied. Please enable from setting".showToast; + } + // if (!isPermissionGranted) { + // "Audio permission denied. Please enable from setting".showToast; + // return; + // } }, style: const ButtonStyle( tapTargetSize: MaterialTapTargetSize.shrinkWrap, // or .padded ), - icon: const Icon(Icons.play_circle_fill_rounded, size: 20), + icon: "chat_mic".toSvgAsset(width: 24, height: 24), constraints: const BoxConstraints(), ), - AudioFileWaveforms( - playerController: playerController, - size: Size(300, 50), + ] else if (chatState == ChatState.voiceRecordingStarted) ...[ + AudioWaveforms( + size: Size(MediaQuery.of(context).size.width, 56.0), + waveStyle: const WaveStyle(waveColor: AppColor.neutral50, extendWaveform: true, showMiddleLine: false), + padding: const EdgeInsets.only(left: 16), + recorderController: recorderController, // Customize how waveforms looks. ).expanded, IconButton( onPressed: () async { - playerController.pausePlayer(); + isAudioRecording = false; + await recorderController.pause(); + recordedFilePath = await recorderController.stop(); + chatState = ChatState.voiceRecordingCompleted; + setState(() {}); }, style: const ButtonStyle( tapTargetSize: MaterialTapTargetSize.shrinkWrap, // or .padded ), - icon: const Icon(Icons.pause_circle_filled_outlined, size: 20), + icon: Icon(Icons.stop_circle_rounded), constraints: const BoxConstraints(), - ), + ) + ] else if (chatState == ChatState.voiceRecordingCompleted) ...[ + if (playerController.playerState == PlayerState.playing) + IconButton( + onPressed: () async { + await playerController.pausePlayer(); + await playerController.stopPlayer(); + setState(() {}); + }, + style: const ButtonStyle( + tapTargetSize: MaterialTapTargetSize.shrinkWrap, // or .padded + ), + icon: const Icon(Icons.stop_circle_outlined, size: 20), + constraints: const BoxConstraints(), + ) + else + IconButton( + onPressed: () async { + await playerController.preparePlayer(path: recordedFilePath!); + await playerController.startPlayer(); + setState(() {}); + }, + style: const ButtonStyle( + tapTargetSize: MaterialTapTargetSize.shrinkWrap, // or .padded + ), + icon: const Icon(Icons.play_circle_fill_rounded, size: 20), + constraints: const BoxConstraints(), + ), + AudioFileWaveforms( + playerController: playerController, + waveformData: recorderController.waveData, + enableSeekGesture: false, + continuousWaveform: false, + waveformType: WaveformType.long, + playerWaveStyle: const PlayerWaveStyle( + fixedWaveColor: AppColor.neutral50, + liveWaveColor: AppColor.primary10, + showSeekLine: true, + ), + size: Size(MediaQuery.of(context).size.width, 56.0), + ).expanded, IconButton( - onPressed: () {}, + onPressed: () async { + await playerController.stopPlayer(); + recorderController.reset(); + recordedFilePath = null; + chatState = ChatState.idle; + setState(() {}); + }, style: const ButtonStyle( tapTargetSize: MaterialTapTargetSize.shrinkWrap, // or .padded ), - icon: "delete".toSvgAsset(width: 24, height: 24), + icon: "delete_icon".toSvgAsset(width: 24, height: 24), constraints: const BoxConstraints(), ), ], + + // if (recordedFilePath == null) ...[ + // isAudioRecording + // ? AudioWaveforms( + // size: Size(MediaQuery.of(context).size.width, 56.0), + // + // // enableGesture: true, + // + // waveStyle: const WaveStyle(waveColor: AppColor.neutral50, extendWaveform: true, showMiddleLine: false), + // padding: const EdgeInsets.only(left: 16), + // recorderController: recorderController, // Customize how waveforms looks. + // ).expanded + // : TextFormField( + // cursorColor: AppColor.neutral50, + // style: AppTextStyles.bodyText.copyWith(color: AppColor.neutral50), + // minLines: 1, + // maxLines: 3, + // textInputAction: TextInputAction.none, + // keyboardType: TextInputType.multiline, + // decoration: InputDecoration( + // enabledBorder: InputBorder.none, + // focusedBorder: InputBorder.none, + // border: InputBorder.none, + // errorBorder: InputBorder.none, + // contentPadding: const EdgeInsets.only(left: 16, top: 8, bottom: 8), + // alignLabelWithHint: true, + // filled: true, + // constraints: const BoxConstraints(), + // suffixIconConstraints: const BoxConstraints(), + // hintText: "Type your message here...", + // hintStyle: AppTextStyles.bodyText.copyWith(color: const Color(0xffCCCCCC)), + // // suffixIcon: Row( + // // mainAxisSize: MainAxisSize.min, + // // crossAxisAlignment: CrossAxisAlignment.end, + // // mainAxisAlignment: MainAxisAlignment.end, + // // children: [ + // // + // // 8.width, + // // ], + // // ) + // ), + // ).expanded, + // IconButton( + // onPressed: () {}, + // style: const ButtonStyle( + // tapTargetSize: MaterialTapTargetSize.shrinkWrap, // or .padded + // ), + // icon: "chat_attachment".toSvgAsset(width: 24, height: 24), + // constraints: const BoxConstraints(), + // ), + // ], + // if (recordedFilePath == null) + // ...[] + // else ...[ + // IconButton( + // onPressed: () async { + // await playerController.preparePlayer(path: recordedFilePath!); + // playerController.startPlayer(); + // }, + // style: const ButtonStyle( + // tapTargetSize: MaterialTapTargetSize.shrinkWrap, // or .padded + // ), + // icon: const Icon(Icons.play_circle_fill_rounded, size: 20), + // constraints: const BoxConstraints(), + // ), + // AudioFileWaveforms( + // playerController: playerController, + // size: Size(300, 50), + // ).expanded, + // IconButton( + // onPressed: () async { + // playerController.pausePlayer(); + // }, + // style: const ButtonStyle( + // tapTargetSize: MaterialTapTargetSize.shrinkWrap, // or .padded + // ), + // icon: const Icon(Icons.pause_circle_filled_outlined, size: 20), + // constraints: const BoxConstraints(), + // ), + // IconButton( + // onPressed: () {}, + // style: const ButtonStyle( + // tapTargetSize: MaterialTapTargetSize.shrinkWrap, // or .padded + // ), + // icon: "delete".toSvgAsset(width: 24, height: 24), + // constraints: const BoxConstraints(), + // ), + // ], // if (isAudioRecording && recorderController.isRecording) // IconButton( // onPressed: () {}, @@ -183,45 +331,44 @@ class _ChatPageState extends State { // icon: "chat_msg_send".toSvgAsset(width: 24, height: 24), // constraints: const BoxConstraints(), // ), - if (isAudioRecording) - IconButton( - onPressed: () async { - isAudioRecording = false; - await recorderController.pause(); - recordedFilePath = await recorderController.stop(); - setState(() {}); - }, - style: const ButtonStyle( - tapTargetSize: MaterialTapTargetSize.shrinkWrap, // or .padded - ), - icon: Icon(Icons.stop_circle_rounded), - constraints: const BoxConstraints(), - ) - else - IconButton( - onPressed: () async { - await recorderController.checkPermission(); - if (recorderController.hasPermission) { - setState(() { - isAudioRecording = true; - }); - recorderController.record(); - } else { - "Audio permission denied. Please enable from setting".showToast; - } - // if (!isPermissionGranted) { - // "Audio permission denied. Please enable from setting".showToast; - // return; - // } - }, - style: const ButtonStyle( - tapTargetSize: MaterialTapTargetSize.shrinkWrap, // or .padded - ), - icon: "chat_mic".toSvgAsset(width: 24, height: 24), - constraints: const BoxConstraints(), - ), - - + // if (isAudioRecording) + // IconButton( + // onPressed: () async { + // isAudioRecording = false; + // await recorderController.pause(); + // recordedFilePath = await recorderController.stop(); + // chatState = ChatState.voiceRecordingCompleted; + // setState(() {}); + // }, + // style: const ButtonStyle( + // tapTargetSize: MaterialTapTargetSize.shrinkWrap, // or .padded + // ), + // icon: Icon(Icons.stop_circle_rounded), + // constraints: const BoxConstraints(), + // ) + // else + // IconButton( + // onPressed: () async { + // await recorderController.checkPermission(); + // if (recorderController.hasPermission) { + // setState(() { + // isAudioRecording = true; + // }); + // recorderController.record(); + // } else { + // "Audio permission denied. Please enable from setting".showToast; + // } + // // if (!isPermissionGranted) { + // // "Audio permission denied. Please enable from setting".showToast; + // // return; + // // } + // }, + // style: const ButtonStyle( + // tapTargetSize: MaterialTapTargetSize.shrinkWrap, // or .padded + // ), + // icon: "chat_mic".toSvgAsset(width: 24, height: 24), + // constraints: const BoxConstraints(), + // ), IconButton( onPressed: () {},