From b93a5cc3dccf89ca9cffb28ecc47733cb6e6be53 Mon Sep 17 00:00:00 2001 From: "Pooja.Saini" Date: Thu, 4 Jul 2024 17:50:15 +0530 Subject: [PATCH 1/7] added the start ,stop and pause --- .../obs/obs_demo/android/app/build.gradle | 4 +- .../android/app/src/main/AndroidManifest.xml | 5 + obs_flutter/obs/obs_demo/lib/main.dart | 2 - .../obs_demo/lib/screen/audio_recorder.dart | 285 ++++++++++++++++++ .../obs/obs_demo/lib/screen/bottomNavi.dart | 10 +- obs_flutter/obs/obs_demo/pubspec.yaml | 6 + 6 files changed, 304 insertions(+), 8 deletions(-) create mode 100644 obs_flutter/obs/obs_demo/lib/screen/audio_recorder.dart diff --git a/obs_flutter/obs/obs_demo/android/app/build.gradle b/obs_flutter/obs/obs_demo/android/app/build.gradle index c230f7c..7d3e6d2 100644 --- a/obs_flutter/obs/obs_demo/android/app/build.gradle +++ b/obs_flutter/obs/obs_demo/android/app/build.gradle @@ -38,8 +38,8 @@ android { applicationId = "com.example.obs_demo" // You can update the following values to match your application needs. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. - minSdk = flutter.minSdkVersion - targetSdk = flutter.targetSdkVersion + minSdk =24 + targetSdk = 31 versionCode = flutterVersionCode.toInteger() versionName = flutterVersionName } diff --git a/obs_flutter/obs/obs_demo/android/app/src/main/AndroidManifest.xml b/obs_flutter/obs/obs_demo/android/app/src/main/AndroidManifest.xml index ceb8ef8..116442b 100644 --- a/obs_flutter/obs/obs_demo/android/app/src/main/AndroidManifest.xml +++ b/obs_flutter/obs/obs_demo/android/app/src/main/AndroidManifest.xml @@ -1,4 +1,9 @@ + + + + + _AudioRecorderState(); +} + +class _AudioRecorderState extends State { + late Record audioRecord; + late AudioPlayer audioPlayer; + String _audioFilePath = ''; + bool _isRecording = false; + bool _hasRecording = false; + Duration elapsedTime = const Duration(seconds: 0); + Duration maxDuration = const Duration(seconds: 150); + + @override + void initState() { + audioRecord = Record(); + audioPlayer = AudioPlayer(); + _requestPermissions(); + + super.initState(); + } + + void dispose() { + audioRecord.dispose(); + audioPlayer.dispose(); + super.dispose(); + } + + Future _requestPermissions() async { + await Permission.microphone.request(); + await Permission.storage.request(); + } + + Future startRecording() async { + try { + if (await audioRecord.hasPermission()) { + await audioRecord.start(samplingRate: 21, bitRate: 48000); + setState(() { + _isRecording = true; + _hasRecording = false; + }); + } + } catch (e) { + print(e); + } + } + + Future stopRecording() async { + try { + String? path = await audioRecord.stop(); + setState(() { + _isRecording = false; + _audioFilePath = path!; + _hasRecording = true; + }); + } catch (e) { + print(e); + } + } + + Future playRecording() async { + try { + Source urlSource = UrlSource(_audioFilePath); + await audioPlayer.play(urlSource); + } catch (e) { + print(e); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Modern Audio Recorder'), + elevation: 0, + ), + body: Padding( + padding: const EdgeInsets.all(20.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + _isRecording ? 'Recording...' : 'Press the button to record', + style: TextStyle(fontSize: 20), + ), + SizedBox(height: 10), + ElevatedButton( + onPressed: _isRecording ? stopRecording : startRecording, + child: Text(_isRecording ? 'Stop Recording' : 'Start Recording'), + ), + SizedBox(height: 20), + if (_hasRecording) + ElevatedButton( + onPressed: playRecording, + child: Text('Play Recording'), + ), + PlayerWidget(player: audioPlayer) + ], + ), + ), + ); + } +} + +class PlayerWidget extends StatefulWidget { + final AudioPlayer player; + + const PlayerWidget({ + required this.player, + super.key, + }); + + @override + State createState() { + return _PlayerWidgetState(); + } +} + +class _PlayerWidgetState extends State { + PlayerState? _playerState; + Duration? _duration; + Duration? _position; + + StreamSubscription? _durationSubscription; + StreamSubscription? _positionSubscription; + StreamSubscription? _playerCompleteSubscription; + StreamSubscription? _playerStateChangeSubscription; + + bool get _isPlaying => _playerState == PlayerState.playing; + + bool get _isPaused => _playerState == PlayerState.paused; + + String get _durationText => _duration?.toString().split('.').first ?? ''; + + String get _positionText => _position?.toString().split('.').first ?? ''; + + AudioPlayer get player => widget.player; + + @override + void initState() { + super.initState(); + // Use initial values from player + _playerState = player.state; + player.getDuration().then( + (value) => setState(() { + _duration = value; + }), + ); + player.getCurrentPosition().then( + (value) => setState(() { + _position = value; + }), + ); + _initStreams(); + } + + @override + void setState(VoidCallback fn) { + // Subscriptions only can be closed asynchronously, + // therefore events can occur after widget has been disposed. + if (mounted) { + super.setState(fn); + } + } + + @override + void dispose() { + _durationSubscription?.cancel(); + _positionSubscription?.cancel(); + _playerCompleteSubscription?.cancel(); + _playerStateChangeSubscription?.cancel(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final color = Theme.of(context).primaryColor; + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + key: const Key('play_button'), + onPressed: _isPlaying ? null : _play, + iconSize: 48.0, + icon: const Icon(Icons.play_arrow), + color: color, + ), + IconButton( + key: const Key('pause_button'), + onPressed: _isPlaying ? _pause : null, + iconSize: 48.0, + icon: const Icon(Icons.pause), + color: color, + ), + IconButton( + key: const Key('stop_button'), + onPressed: _isPlaying || _isPaused ? _stop : null, + iconSize: 48.0, + icon: const Icon(Icons.stop), + color: color, + ), + ], + ), + Slider( + onChanged: (value) { + final duration = _duration; + if (duration == null) { + return; + } + final position = value * duration.inMilliseconds; + player.seek(Duration(milliseconds: position.round())); + }, + value: (_position != null && + _duration != null && + _position!.inMilliseconds > 0 && + _position!.inMilliseconds < _duration!.inMilliseconds) + ? _position!.inMilliseconds / _duration!.inMilliseconds + : 0.0, + ), + Text( + _position != null + ? '$_positionText / $_durationText' + : _duration != null + ? _durationText + : '', + style: const TextStyle(fontSize: 16.0), + ), + ], + ); + } + + void _initStreams() { + _durationSubscription = player.onDurationChanged.listen((duration) { + setState(() => _duration = duration); + }); + + _positionSubscription = player.onPositionChanged.listen( + (p) => setState(() => _position = p), + ); + + _playerCompleteSubscription = player.onPlayerComplete.listen((event) { + setState(() { + _playerState = PlayerState.stopped; + _position = Duration.zero; + }); + }); + + _playerStateChangeSubscription = + player.onPlayerStateChanged.listen((state) { + setState(() { + _playerState = state; + }); + }); + } + + Future _play() async { + await player.resume(); + setState(() => _playerState = PlayerState.playing); + } + + Future _pause() async { + await player.pause(); + setState(() => _playerState = PlayerState.paused); + } + + Future _stop() async { + await player.stop(); + setState(() { + _playerState = PlayerState.stopped; + _position = Duration.zero; + }); + } +} diff --git a/obs_flutter/obs/obs_demo/lib/screen/bottomNavi.dart b/obs_flutter/obs/obs_demo/lib/screen/bottomNavi.dart index 68d569f..7af965b 100644 --- a/obs_flutter/obs/obs_demo/lib/screen/bottomNavi.dart +++ b/obs_flutter/obs/obs_demo/lib/screen/bottomNavi.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:obs_demo/editortext.dart'; +import 'package:obs_demo/screen/audio_recorder.dart'; import 'package:obs_demo/screen/dashboard.dart'; import 'package:obs_demo/user_profile.dart'; @@ -64,6 +65,7 @@ class _BottomNavigationBarExampleState // 'Audio', // style: optionStyle, // ), + AudioRecorder(), Column( mainAxisAlignment: MainAxisAlignment.center, children: [ @@ -130,10 +132,10 @@ class _BottomNavigationBarExampleState icon: Icon(Icons.create), label: '', ), - // BottomNavigationBarItem( - // icon: Icon(Icons.audiotrack_outlined), - // label: 'Audio', - // ), + BottomNavigationBarItem( + icon: Icon(Icons.volume_up), + label: '', + ), BottomNavigationBarItem( icon: Icon(Icons.menu), label: '', diff --git a/obs_flutter/obs/obs_demo/pubspec.yaml b/obs_flutter/obs/obs_demo/pubspec.yaml index 58a9112..7395fbf 100644 --- a/obs_flutter/obs/obs_demo/pubspec.yaml +++ b/obs_flutter/obs/obs_demo/pubspec.yaml @@ -37,6 +37,12 @@ dependencies: http: ^0.13.3 path_provider: ^2.0.2 + audioplayers: ^4.1.0 + record: ^4.4.4 + permission_handler: ^11.3.1 + general_audio_waveforms: ^0.0.10 + audio_waveforms: ^1.0.5 + file_picker: ^8.0.6 dev_dependencies: flutter_test: From dfa4ab892e27b6908c70b3b85d5fb5ac91116f36 Mon Sep 17 00:00:00 2001 From: "Pooja.Saini" Date: Fri, 5 Jul 2024 17:28:25 +0530 Subject: [PATCH 2/7] added wave for recording --- .../obs/obs_demo/android/app/build.gradle | 6 + obs_flutter/obs/obs_demo/android/build.gradle | 14 ++ .../lib/screen/audio_record_context.dart | 169 ++++++++++++++++ .../obs_demo/lib/screen/audio_recorder.dart | 1 - .../obs/obs_demo/lib/screen/bottomNavi.dart | 6 + .../obs/obs_demo/lib/screen/story_para.dart | 15 ++ .../obs/obs_demo/lib/utils/chat_bubble.dart | 184 ++++++++++++++++++ obs_flutter/obs/obs_demo/pubspec.yaml | 4 +- 8 files changed, 396 insertions(+), 3 deletions(-) create mode 100644 obs_flutter/obs/obs_demo/lib/screen/audio_record_context.dart create mode 100644 obs_flutter/obs/obs_demo/lib/screen/story_para.dart create mode 100644 obs_flutter/obs/obs_demo/lib/utils/chat_bubble.dart diff --git a/obs_flutter/obs/obs_demo/android/app/build.gradle b/obs_flutter/obs/obs_demo/android/app/build.gradle index 7d3e6d2..2117066 100644 --- a/obs_flutter/obs/obs_demo/android/app/build.gradle +++ b/obs_flutter/obs/obs_demo/android/app/build.gradle @@ -43,6 +43,12 @@ android { versionCode = flutterVersionCode.toInteger() versionName = flutterVersionName } + configurations.all { + resolutionStrategy { + force "org.jetbrains.kotlin:kotlin-stdlib:1.8.20" + force "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.20" + } + } buildTypes { release { diff --git a/obs_flutter/obs/obs_demo/android/build.gradle b/obs_flutter/obs/obs_demo/android/build.gradle index d2ffbff..31739ca 100644 --- a/obs_flutter/obs/obs_demo/android/build.gradle +++ b/obs_flutter/obs/obs_demo/android/build.gradle @@ -1,3 +1,17 @@ +buildscript { + ext { + kotlin_version = '1.8.20' // Define the Kotlin version here + } + repositories { + google() + mavenCentral() + } + dependencies { + classpath "com.android.tools.build:gradle:7.0.2" // Use the appropriate version for your project + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + allprojects { repositories { google() diff --git a/obs_flutter/obs/obs_demo/lib/screen/audio_record_context.dart b/obs_flutter/obs/obs_demo/lib/screen/audio_record_context.dart new file mode 100644 index 0000000..48ee08f --- /dev/null +++ b/obs_flutter/obs/obs_demo/lib/screen/audio_record_context.dart @@ -0,0 +1,169 @@ +import 'dart:io'; +import 'package:audio_waveforms/audio_waveforms.dart'; +import 'package:file_picker/file_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:obs_demo/screen/story_para.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:obs_demo/utils/chat_bubble.dart'; +// import 'package:flutter_ffmpeg/flutter_ffmpeg.dart'; + +class AudioRecordContext extends StatefulWidget { + @override + _AudioRecordContextState createState() => _AudioRecordContextState(); +} + +class _AudioRecordContextState extends State { + late final RecorderController recorderController; + // final FlutterFFmpeg _flutterFFmpeg = FlutterFFmpeg(); + String? path; + String? musicFile; + bool isRecording = false; + bool isRecordingCompleted = false; + bool isLoading = true; + late Directory appDirectory; + + @override + void initState() { + _getDir(); + _initialiseControllers(); + super.initState(); + } + + void _getDir() async { + appDirectory = await getApplicationDocumentsDirectory(); + path = "${appDirectory.path}/recording.m4a"; + isLoading = false; + setState(() {}); + } + + void _initialiseControllers() { + recorderController = RecorderController() + ..androidEncoder = AndroidEncoder.aac + ..androidOutputFormat = AndroidOutputFormat.mpeg4 + ..iosEncoder = IosEncoder.kAudioFormatMPEG4AAC + ..sampleRate = 24; + recorderController.bitRate = 48000; + } + + void _pickFile() async { + FilePickerResult? result = await FilePicker.platform.pickFiles(); + if (result != null) { + musicFile = result.files.single.path; + setState(() {}); + } else { + debugPrint("File not picked"); + } + } + + // Future _convertToWav(String inputPath) async { + // final outputPath = inputPath.replaceAll('.m4a', '.wav'); + // await _flutterFFmpeg.execute('-i $inputPath $outputPath'); + // path = outputPath; + // debugPrint('Converted to WAV: $outputPath'); + // setState(() {}); + // } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: isLoading + ? const Center( + child: CircularProgressIndicator(), + ) + : SafeArea( + child: Column( + children: [ + StoryPara(), + if (isRecordingCompleted) + WaveBubble( + path: path!, + isSender: true, + appDirectory: appDirectory, + ), + const SizedBox(height: 20), + SafeArea( + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + AnimatedSwitcher( + duration: const Duration(milliseconds: 200), + child: isRecording + ? AudioWaveforms( + enableGesture: true, + size: Size( + MediaQuery.of(context).size.width / 2, + 50, + ), + recorderController: recorderController, + waveStyle: const WaveStyle( + waveColor: Colors.white, + extendWaveform: true, + showMiddleLine: false, + ), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12.0), + color: const Color(0xFF1E1B26), + ), + padding: const EdgeInsets.only(left: 18), + margin: const EdgeInsets.symmetric( + horizontal: 15), + ) + : const Text(""), + ), + if (isRecording) + IconButton( + onPressed: _refreshWave, + icon: const Icon( + Icons.refresh, + color: Colors.black, + ), + ), + const SizedBox(width: 16), + Center( + child: IconButton( + onPressed: _startOrStopRecording, + icon: Icon(isRecording ? Icons.stop : Icons.mic), + color: Colors.black, + iconSize: 28, + ), + ) + ], + ), + ), + ], + ), + ), + ); + } + + void _startOrStopRecording() async { + try { + if (isRecording) { + recorderController.reset(); + + path = await recorderController.stop(false); + + if (path != null) { + isRecordingCompleted = true; + debugPrint(path); + debugPrint("Recorded file size: ${File(path!).lengthSync()}"); + } + } else { + await recorderController.record(path: path); // Path is optional + setState(() { + isRecordingCompleted = false; + }); + } + } catch (e) { + debugPrint(e.toString()); + } finally { + setState(() { + isRecording = !isRecording; + }); + } + } + + void _refreshWave() { + if (isRecording) recorderController.refresh(); + } +} diff --git a/obs_flutter/obs/obs_demo/lib/screen/audio_recorder.dart b/obs_flutter/obs/obs_demo/lib/screen/audio_recorder.dart index 7e30a60..5d9c155 100644 --- a/obs_flutter/obs/obs_demo/lib/screen/audio_recorder.dart +++ b/obs_flutter/obs/obs_demo/lib/screen/audio_recorder.dart @@ -4,7 +4,6 @@ import 'package:flutter/material.dart'; import 'package:audioplayers/audioplayers.dart'; import 'package:record/record.dart'; import 'package:permission_handler/permission_handler.dart'; -// import 'package:general_audio_waveforms/general_audio_waveforms.dart'; class AudioRecorder extends StatefulWidget { @override diff --git a/obs_flutter/obs/obs_demo/lib/screen/bottomNavi.dart b/obs_flutter/obs/obs_demo/lib/screen/bottomNavi.dart index 7af965b..39277f3 100644 --- a/obs_flutter/obs/obs_demo/lib/screen/bottomNavi.dart +++ b/obs_flutter/obs/obs_demo/lib/screen/bottomNavi.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:obs_demo/editortext.dart'; +import 'package:obs_demo/screen/audio_record_context.dart'; import 'package:obs_demo/screen/audio_recorder.dart'; import 'package:obs_demo/screen/dashboard.dart'; import 'package:obs_demo/user_profile.dart'; @@ -66,6 +67,7 @@ class _BottomNavigationBarExampleState // style: optionStyle, // ), AudioRecorder(), + AudioRecordContext(), Column( mainAxisAlignment: MainAxisAlignment.center, children: [ @@ -132,6 +134,10 @@ class _BottomNavigationBarExampleState icon: Icon(Icons.create), label: '', ), + BottomNavigationBarItem( + icon: Icon(Icons.music_note), + label: '', + ), BottomNavigationBarItem( icon: Icon(Icons.volume_up), label: '', diff --git a/obs_flutter/obs/obs_demo/lib/screen/story_para.dart b/obs_flutter/obs/obs_demo/lib/screen/story_para.dart new file mode 100644 index 0000000..025898b --- /dev/null +++ b/obs_flutter/obs/obs_demo/lib/screen/story_para.dart @@ -0,0 +1,15 @@ +import 'package:flutter/material.dart'; + +class StoryPara extends StatefulWidget { + @override + _StoryParaState createState() => _StoryParaState(); +} + +class _StoryParaState extends State { + @override + Widget build(BuildContext context) { + return (Container( + child: Text("para"), + )); + } +} diff --git a/obs_flutter/obs/obs_demo/lib/utils/chat_bubble.dart b/obs_flutter/obs/obs_demo/lib/utils/chat_bubble.dart new file mode 100644 index 0000000..caaca6e --- /dev/null +++ b/obs_flutter/obs/obs_demo/lib/utils/chat_bubble.dart @@ -0,0 +1,184 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:audio_waveforms/audio_waveforms.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +class ChatBubble extends StatelessWidget { + final String text; + final bool isSender; + final bool isLast; + + const ChatBubble({ + Key? key, + required this.text, + this.isSender = false, + this.isLast = false, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 20, bottom: 10, right: 20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + if (isSender) const Spacer(), + Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + color: isSender + ? const Color(0xFF276bfd) + : const Color(0xFF343145)), + padding: const EdgeInsets.only( + bottom: 9, top: 8, left: 14, right: 12), + child: Text( + text, + style: const TextStyle(color: Colors.white, fontSize: 20), + ), + ), + ], + ), + ], + ), + ); + } +} + +class WaveBubble extends StatefulWidget { + final bool isSender; + final int? index; + final String? path; + final double? width; + final Directory appDirectory; + + const WaveBubble({ + Key? key, + required this.appDirectory, + this.width, + this.index, + this.isSender = false, + this.path, + }) : super(key: key); + + @override + State createState() => _WaveBubbleState(); +} + +class _WaveBubbleState extends State { + File? file; + + late PlayerController controller; + late StreamSubscription playerStateSubscription; + + final playerWaveStyle = const PlayerWaveStyle( + fixedWaveColor: Colors.white54, + liveWaveColor: Colors.white, + spacing: 6, + ); + + @override + void initState() { + super.initState(); + controller = PlayerController(); + _preparePlayer(); + playerStateSubscription = controller.onPlayerStateChanged.listen((_) { + setState(() {}); + }); + } + + void _preparePlayer() async { + // Opening file from assets folder + if (widget.index != null) { + file = File('${widget.appDirectory.path}/audio${widget.index}.mp3'); + await file?.writeAsBytes( + (await rootBundle.load('assets/audios/audio${widget.index}.mp3')) + .buffer + .asUint8List()); + } + if (widget.index == null && widget.path == null && file?.path == null) { + return; + } + // Prepare player with extracting waveform if index is even. + controller.preparePlayer( + path: widget.path ?? file!.path, + shouldExtractWaveform: widget.index?.isEven ?? true, + ); + // Extracting waveform separately if index is odd. + if (widget.index?.isOdd ?? false) { + controller + .extractWaveformData( + path: widget.path ?? file!.path, + noOfSamples: + playerWaveStyle.getSamplesForWidth(widget.width ?? 200), + ) + .then((waveformData) => debugPrint(waveformData.toString())); + } + } + + @override + void dispose() { + playerStateSubscription.cancel(); + controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return widget.path != null || file?.path != null + ? Align( + alignment: + widget.isSender ? Alignment.center : Alignment.centerLeft, + child: Container( + padding: EdgeInsets.only( + bottom: 6, + right: widget.isSender ? 0 : 10, + top: 6, + ), + margin: const EdgeInsets.symmetric(vertical: 8, horizontal: 12), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + color: widget.isSender + ? const Color(0xFF276bfd) + : const Color(0xFF343145), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + if (!controller.playerState.isStopped) + IconButton( + onPressed: () async { + controller.playerState.isPlaying + ? await controller.pausePlayer() + : await controller.startPlayer( + finishMode: FinishMode.loop, + ); + }, + icon: Icon( + controller.playerState.isPlaying + ? Icons.stop + : Icons.play_arrow, + ), + color: Colors.white, + splashColor: Colors.transparent, + highlightColor: Colors.transparent, + ), + AudioFileWaveforms( + size: Size(MediaQuery.of(context).size.width / 2, 70), + playerController: controller, + waveformType: widget.index?.isOdd ?? false + ? WaveformType.fitWidth + : WaveformType.long, + playerWaveStyle: playerWaveStyle, + ), + if (widget.isSender) const SizedBox(width: 10), + ], + ), + ), + ) + : const SizedBox.shrink(); + } +} diff --git a/obs_flutter/obs/obs_demo/pubspec.yaml b/obs_flutter/obs/obs_demo/pubspec.yaml index 7395fbf..b5099e0 100644 --- a/obs_flutter/obs/obs_demo/pubspec.yaml +++ b/obs_flutter/obs/obs_demo/pubspec.yaml @@ -40,9 +40,9 @@ dependencies: audioplayers: ^4.1.0 record: ^4.4.4 permission_handler: ^11.3.1 - general_audio_waveforms: ^0.0.10 audio_waveforms: ^1.0.5 - file_picker: ^8.0.6 + file_picker: ^5.0.0 + flutter_ffmpeg: ^0.4.2 dev_dependencies: flutter_test: From 29e550e63b55c6ae4a7b4577039f00f6ac2343f2 Mon Sep 17 00:00:00 2001 From: "Pooja.Saini" Date: Tue, 9 Jul 2024 17:25:10 +0530 Subject: [PATCH 3/7] added audio into json --- obs_flutter/obs/obs_demo/lib/editortext.dart | 2 - .../lib/screen/audio_record_context.dart | 359 ++++++++++++++---- .../obs/obs_demo/lib/screen/bottomNavi.dart | 2 +- .../obs/obs_demo/lib/screen/story_para.dart | 140 ++++++- .../obs/obs_demo/lib/utils/chat_bubble.dart | 56 +-- obs_flutter/obs/obs_demo/pubspec.yaml | 1 + 6 files changed, 425 insertions(+), 135 deletions(-) diff --git a/obs_flutter/obs/obs_demo/lib/editortext.dart b/obs_flutter/obs/obs_demo/lib/editortext.dart index b26d46b..15d1c05 100644 --- a/obs_flutter/obs/obs_demo/lib/editortext.dart +++ b/obs_flutter/obs/obs_demo/lib/editortext.dart @@ -67,7 +67,6 @@ class _EditorTextLayoutState extends State { final jsonData = await file.readAsString(); final data = jsonDecode(jsonData) as Map; _controller.text = data['story'][0]['text']; - data['story'][0]['isEmpty'] = false; return data; } on FileSystemException { return {}; @@ -315,7 +314,6 @@ class _EditorTextLayoutState extends State { void saveData(String value) async { story['story'][paraIndex]['text'] = value; - story['story'][paraIndex]['isEmpty'] = value.isEmpty; writeJsonToFile(story); widget.onUpdateTextAvailability(value.isNotEmpty); print('Data saved: $value'); diff --git a/obs_flutter/obs/obs_demo/lib/screen/audio_record_context.dart b/obs_flutter/obs/obs_demo/lib/screen/audio_record_context.dart index 48ee08f..2de63c7 100644 --- a/obs_flutter/obs/obs_demo/lib/screen/audio_record_context.dart +++ b/obs_flutter/obs/obs_demo/lib/screen/audio_record_context.dart @@ -1,37 +1,54 @@ +import 'dart:convert'; import 'dart:io'; import 'package:audio_waveforms/audio_waveforms.dart'; -import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; -import 'package:obs_demo/screen/story_para.dart'; +import 'package:flutter/services.dart'; import 'package:path_provider/path_provider.dart'; import 'package:obs_demo/utils/chat_bubble.dart'; -// import 'package:flutter_ffmpeg/flutter_ffmpeg.dart'; class AudioRecordContext extends StatefulWidget { + const AudioRecordContext({super.key, required this.rowIndex}); + final int rowIndex; + @override _AudioRecordContextState createState() => _AudioRecordContextState(); } class _AudioRecordContextState extends State { late final RecorderController recorderController; - // final FlutterFFmpeg _flutterFFmpeg = FlutterFFmpeg(); + late final PlayerController playerController; String? path; - String? musicFile; bool isRecording = false; bool isRecordingCompleted = false; bool isLoading = true; late Directory appDirectory; + List> storyDatas = []; + Map story = {}; + late int storyIndex; + int paraIndex = 0; + + Future fetchStoryText() async { + final jsonString = await rootBundle.loadString('assets/OBSTextData.json'); + setState(() { + storyDatas = json.decode(jsonString).cast>(); + storyIndex = widget.rowIndex; + }); + } + @override void initState() { _getDir(); + fetchStoryText(); + fetchJson(); _initialiseControllers(); super.initState(); } void _getDir() async { appDirectory = await getApplicationDocumentsDirectory(); - path = "${appDirectory.path}/recording.m4a"; + String appDocPath = appDirectory.path; + path = '$appDocPath/${storyIndex}.${paraIndex}.wav'; isLoading = false; setState(() {}); } @@ -43,111 +60,285 @@ class _AudioRecordContextState extends State { ..iosEncoder = IosEncoder.kAudioFormatMPEG4AAC ..sampleRate = 24; recorderController.bitRate = 48000; + + playerController = PlayerController(); + } + + Future deleteRecording() async { + final file = File(path!); + try { + if (await file.exists()) { + await file.delete(); + story['story'][paraIndex].remove('audio'); + writeJsonToFile(story); + print('Recording deleted'); + print(path!); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Recording deleted successfully')), + ); + setState(() { + isRecordingCompleted = false; + }); + } else { + print('File does not exist'); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('File does not exist')), + ); + } + } catch (e) { + print('Error deleting file: $e'); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Error deleting file')), + ); + } } - void _pickFile() async { - FilePickerResult? result = await FilePicker.platform.pickFiles(); - if (result != null) { - musicFile = result.files.single.path; - setState(() {}); + Future fetchJson() async { + Map data = await readJsonToFile(); + if (data.isEmpty) { + final obsJson = await rootBundle.loadString('assets/OBSData.json'); + var obsData = json.decode(obsJson).cast>(); + writeJsonToFile(obsData[storyIndex]); + setState(() { + story = obsData[storyIndex]; + }); } else { - debugPrint("File not picked"); + setState(() { + story = data; + }); + } + } + + Future> readJsonToFile() async { + final directory = await getApplicationDocumentsDirectory(); + final file = File('${directory.path}/${storyIndex}.json'); + // Replace with your desired filename + try { + final jsonData = await file.readAsString(); + final data = jsonDecode(jsonData) as Map; + return data; + } on FileSystemException { + return {}; + } catch (e) { + print("Error reading JSON file: $e"); + rethrow; } } - // Future _convertToWav(String inputPath) async { - // final outputPath = inputPath.replaceAll('.m4a', '.wav'); - // await _flutterFFmpeg.execute('-i $inputPath $outputPath'); - // path = outputPath; - // debugPrint('Converted to WAV: $outputPath'); - // setState(() {}); - // } + Future writeJsonToFile(Map data) async { + final directory = await getApplicationDocumentsDirectory(); + final file = File('${directory.path}/${storyIndex}.json'); + final jsonData = jsonEncode(data); + await file.writeAsString(jsonData); + } + + @override + void dispose() { + recorderController.dispose(); + playerController.dispose(); + super.dispose(); + } @override Widget build(BuildContext context) { - return Scaffold( - body: isLoading - ? const Center( - child: CircularProgressIndicator(), - ) - : SafeArea( - child: Column( - children: [ - StoryPara(), - if (isRecordingCompleted) - WaveBubble( - path: path!, - isSender: true, - appDirectory: appDirectory, + String text = + storyDatas[storyIndex]['story'][paraIndex]['url'].split('/').last; + return Container( + child: Column( + children: [ + Container( + width: double.infinity, + height: 200, + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage('assets/images/$text'), + fit: BoxFit.cover, + ), + ), + child: Container( + width: double.infinity, + height: 200, + padding: const EdgeInsets.all(8), + color: const Color(0xF0FDFDFF).withOpacity(0.9), + child: Text( + storyDatas[storyIndex]['story'][paraIndex]['text'], + style: const TextStyle( + fontSize: 14.5, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + storyIndex != 0 + ? IconButton( + icon: const Icon(Icons.skip_previous), + iconSize: 35, + onPressed: () { + setState(() { + storyIndex = storyIndex > 0 ? storyIndex - 1 : 0; + paraIndex = 0; + }); + _getDir(); + fetchJson(); + }, + ) + : IconButton( + icon: Icon(Icons.skip_previous), + iconSize: 35, + color: Color.fromARGB(66, 168, 163, 163).withOpacity(0.5), + onPressed: () {}, + ), + paraIndex != 0 + ? IconButton( + icon: Icon(Icons.arrow_left_sharp), + iconSize: 35, + onPressed: () { + setState(() { + paraIndex = paraIndex > 0 ? paraIndex - 1 : 0; + }); + _getDir(); + }, + ) + : IconButton( + icon: Icon(Icons.arrow_left_sharp), + iconSize: 35, + color: Color.fromARGB(66, 168, 163, 163).withOpacity(0.5), + onPressed: () {}, ), - const SizedBox(height: 20), - SafeArea( - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - AnimatedSwitcher( - duration: const Duration(milliseconds: 200), - child: isRecording - ? AudioWaveforms( - enableGesture: true, - size: Size( - MediaQuery.of(context).size.width / 2, - 50, - ), - recorderController: recorderController, - waveStyle: const WaveStyle( - waveColor: Colors.white, - extendWaveform: true, - showMiddleLine: false, - ), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(12.0), - color: const Color(0xFF1E1B26), - ), - padding: const EdgeInsets.only(left: 18), - margin: const EdgeInsets.symmetric( - horizontal: 15), - ) - : const Text(""), - ), - if (isRecording) - IconButton( - onPressed: _refreshWave, - icon: const Icon( - Icons.refresh, - color: Colors.black, - ), + Text(storyDatas[storyIndex]['storyId'].toString()), + const Text(":"), + Text(storyDatas[storyIndex]['story'][paraIndex]['id'].toString()), + paraIndex != storyDatas[storyIndex]['story'].length - 1 + ? IconButton( + icon: Icon(Icons.arrow_right_sharp), + iconSize: 35, + onPressed: () { + setState(() { + paraIndex = paraIndex + 1; + }); + _getDir(); + }, + ) + : IconButton( + icon: Icon(Icons.arrow_right_sharp), + iconSize: 35, + color: Colors.black26.withOpacity(0.5), + onPressed: () {}, + ), + storyIndex != storyDatas.length - 1 + ? IconButton( + icon: Icon(Icons.skip_next), + iconSize: 35, + onPressed: () { + setState(() { + storyIndex = storyIndex + 1; + paraIndex = 0; + }); + _getDir(); + fetchJson(); + }, + ) + : IconButton( + icon: Icon(Icons.skip_next), + iconSize: 35, + color: Color.fromARGB(66, 168, 163, 163).withOpacity(0.5), + onPressed: () {}, + ), + ], + ), + if (story['story'][paraIndex]['audio'] != null) + WaveBubble( + path: story['story'][paraIndex]['audio'], + isSender: true, + appDirectory: appDirectory, + ), + const SizedBox(height: 20), + Padding( + padding: const EdgeInsets.all( + 8, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + AnimatedSwitcher( + duration: const Duration(milliseconds: 200), + child: isRecording + ? AudioWaveforms( + enableGesture: true, + size: Size( + MediaQuery.of(context).size.width / 2, + 50, + ), + recorderController: recorderController, + waveStyle: const WaveStyle( + waveColor: Colors.white, + extendWaveform: true, + showMiddleLine: false, ), - const SizedBox(width: 16), - Center( - child: IconButton( - onPressed: _startOrStopRecording, - icon: Icon(isRecording ? Icons.stop : Icons.mic), - color: Colors.black, - iconSize: 28, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12.0), + color: const Color(0xFF1E1B26), ), + padding: const EdgeInsets.only(left: 18), + margin: const EdgeInsets.symmetric(horizontal: 15), ) - ], + : const Text(""), + ), + if (isRecording) + IconButton( + onPressed: _refreshWave, + icon: const Icon( + Icons.refresh, + color: Colors.black, ), ), - ], - ), + const SizedBox(width: 16), + Center( + child: IconButton( + onPressed: () => + _startOrStopRecording(storyIndex, paraIndex), + icon: Icon(isRecording ? Icons.stop : Icons.mic), + color: Colors.black, + iconSize: 28, + ), + ), + if (story['story'][paraIndex]['audio'] != null) + IconButton( + onPressed: deleteRecording, + icon: const Icon( + Icons.delete, + color: Colors.black, + ), + ), + ], ), + ), + ], + ), ); } - void _startOrStopRecording() async { + void _startOrStopRecording(storyNumber, paraNumber) async { + // appDirectory = await getApplicationDocumentsDirectory(); + // String appDocPath = appDirectory.path; + // path = '$appDocPath/${storyNumber}.${paraNumber}.wav'; + isLoading = false; try { if (isRecording) { recorderController.reset(); + print(path); path = await recorderController.stop(false); - - if (path != null) { + if (path != "") { isRecordingCompleted = true; debugPrint(path); - debugPrint("Recorded file size: ${File(path!).lengthSync()}"); } + story['story'][paraNumber]['audio'] = path; + writeJsonToFile(story); } else { await recorderController.record(path: path); // Path is optional setState(() { diff --git a/obs_flutter/obs/obs_demo/lib/screen/bottomNavi.dart b/obs_flutter/obs/obs_demo/lib/screen/bottomNavi.dart index 39277f3..23fdb31 100644 --- a/obs_flutter/obs/obs_demo/lib/screen/bottomNavi.dart +++ b/obs_flutter/obs/obs_demo/lib/screen/bottomNavi.dart @@ -67,7 +67,7 @@ class _BottomNavigationBarExampleState // style: optionStyle, // ), AudioRecorder(), - AudioRecordContext(), + AudioRecordContext(rowIndex: 0), Column( mainAxisAlignment: MainAxisAlignment.center, children: [ diff --git a/obs_flutter/obs/obs_demo/lib/screen/story_para.dart b/obs_flutter/obs/obs_demo/lib/screen/story_para.dart index 025898b..265a16f 100644 --- a/obs_flutter/obs/obs_demo/lib/screen/story_para.dart +++ b/obs_flutter/obs/obs_demo/lib/screen/story_para.dart @@ -1,15 +1,149 @@ +import 'dart:convert'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; class StoryPara extends StatefulWidget { + const StoryPara({super.key, required this.rowIndex}); + final int rowIndex; + @override _StoryParaState createState() => _StoryParaState(); } class _StoryParaState extends State { + List> storyDatas = []; + Map story = {}; + late int storyIndex; + int paraIndex = 0; + final TextEditingController _controller = TextEditingController(); + + Future fetchStoryText() async { + final jsonString = await rootBundle.loadString('assets/OBSTextData.json'); + setState(() { + storyDatas = json.decode(jsonString).cast>(); + storyIndex = widget.rowIndex; + }); + } + + @override + void initState() { + fetchStoryText(); + super.initState(); + } + @override Widget build(BuildContext context) { - return (Container( - child: Text("para"), - )); + String text = + storyDatas[storyIndex]['story'][paraIndex]['url'].split('/').last; + return SingleChildScrollView( + child: Column( + children: [ + Container( + width: double.infinity, + height: 200, + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage('assets/images/$text'), + fit: BoxFit.cover, + ), + ), + child: Container( + width: double.infinity, + height: 200, + padding: const EdgeInsets.all(8), + color: const Color(0xF0FDFDFF).withOpacity(0.9), + child: Text( + storyDatas[storyIndex]['story'][paraIndex]['text'], + style: const TextStyle( + fontSize: 14.5, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + storyIndex != 0 + ? IconButton( + icon: const Icon(Icons.skip_previous), + iconSize: 35, + onPressed: () { + setState(() { + storyIndex = storyIndex > 0 ? storyIndex - 1 : 0; + paraIndex = 0; + }); + }, + ) + : IconButton( + icon: Icon(Icons.skip_previous), + iconSize: 35, + color: Color.fromARGB(66, 168, 163, 163).withOpacity(0.5), + onPressed: () {}, + ), + paraIndex != 0 + ? IconButton( + icon: Icon(Icons.arrow_left_sharp), + iconSize: 35, + onPressed: () { + setState(() { + paraIndex = paraIndex > 0 ? paraIndex - 1 : 0; + }); + _controller.text = story['story'][paraIndex]['text']; + }, + ) + : IconButton( + icon: Icon(Icons.arrow_left_sharp), + iconSize: 35, + color: Color.fromARGB(66, 168, 163, 163).withOpacity(0.5), + onPressed: () {}, + ), + Text(storyDatas[storyIndex]['storyId'].toString()), + const Text(":"), + Text(storyDatas[storyIndex]['story'][paraIndex]['id'].toString()), + paraIndex != storyDatas[storyIndex]['story'].length - 1 + ? IconButton( + icon: Icon(Icons.arrow_right_sharp), + iconSize: 35, + onPressed: () { + setState(() { + paraIndex = paraIndex + 1; + }); + _controller.text = story?['story']?[paraIndex]['text']; + }, + ) + : IconButton( + icon: Icon(Icons.arrow_right_sharp), + iconSize: 35, + color: Colors.black26.withOpacity(0.5), + onPressed: () {}, + ), + storyIndex != storyDatas.length - 1 + ? IconButton( + icon: Icon(Icons.skip_next), + iconSize: 35, + onPressed: () { + setState(() { + storyIndex = storyIndex + 1; + paraIndex = 0; + }); + }, + ) + : IconButton( + icon: Icon(Icons.skip_next), + iconSize: 35, + color: Color.fromARGB(66, 168, 163, 163).withOpacity(0.5), + onPressed: () {}, + ), + ], + ), + // AudioRecordContext( + // story_number: storyIndex, + // para_number: paraIndex, + // ) + ], + ), + ); } } diff --git a/obs_flutter/obs/obs_demo/lib/utils/chat_bubble.dart b/obs_flutter/obs/obs_demo/lib/utils/chat_bubble.dart index caaca6e..084fd1e 100644 --- a/obs_flutter/obs/obs_demo/lib/utils/chat_bubble.dart +++ b/obs_flutter/obs/obs_demo/lib/utils/chat_bubble.dart @@ -1,52 +1,8 @@ import 'dart:async'; import 'dart:io'; - -import 'package:audio_waveforms/audio_waveforms.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; - -class ChatBubble extends StatelessWidget { - final String text; - final bool isSender; - final bool isLast; - - const ChatBubble({ - Key? key, - required this.text, - this.isSender = false, - this.isLast = false, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.only(left: 20, bottom: 10, right: 20), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - if (isSender) const Spacer(), - Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(10), - color: isSender - ? const Color(0xFF276bfd) - : const Color(0xFF343145)), - padding: const EdgeInsets.only( - bottom: 9, top: 8, left: 14, right: 12), - child: Text( - text, - style: const TextStyle(color: Colors.white, fontSize: 20), - ), - ), - ], - ), - ], - ), - ); - } -} +import 'package:audio_waveforms/audio_waveforms.dart'; class WaveBubble extends StatefulWidget { final bool isSender; @@ -126,6 +82,15 @@ class _WaveBubbleState extends State { super.dispose(); } + @override + void didUpdateWidget(covariant WaveBubble oldWidget) { + if (oldWidget.path != widget.path) { + // Path has changed, update controller + _preparePlayer(); + } + super.didUpdateWidget(oldWidget); + } + @override Widget build(BuildContext context) { return widget.path != null || file?.path != null @@ -167,6 +132,7 @@ class _WaveBubbleState extends State { highlightColor: Colors.transparent, ), AudioFileWaveforms( + key: UniqueKey(), // Ensure a unique key is provided size: Size(MediaQuery.of(context).size.width / 2, 70), playerController: controller, waveformType: widget.index?.isOdd ?? false diff --git a/obs_flutter/obs/obs_demo/pubspec.yaml b/obs_flutter/obs/obs_demo/pubspec.yaml index b5099e0..53a1452 100644 --- a/obs_flutter/obs/obs_demo/pubspec.yaml +++ b/obs_flutter/obs/obs_demo/pubspec.yaml @@ -43,6 +43,7 @@ dependencies: audio_waveforms: ^1.0.5 file_picker: ^5.0.0 flutter_ffmpeg: ^0.4.2 + shared_preferences: ^2.2.3 dev_dependencies: flutter_test: From d7b8c8644f3e844a9d95412bbc99206fa3ba0ecb Mon Sep 17 00:00:00 2001 From: "Pooja.Saini" Date: Wed, 10 Jul 2024 12:45:13 +0530 Subject: [PATCH 4/7] ui changes and added text field --- .../lib/screen/audio_record_context.dart | 375 +++++++++++------- .../obs/obs_demo/lib/utils/chat_bubble.dart | 3 +- 2 files changed, 233 insertions(+), 145 deletions(-) diff --git a/obs_flutter/obs/obs_demo/lib/screen/audio_record_context.dart b/obs_flutter/obs/obs_demo/lib/screen/audio_record_context.dart index 2de63c7..6b68e37 100644 --- a/obs_flutter/obs/obs_demo/lib/screen/audio_record_context.dart +++ b/obs_flutter/obs/obs_demo/lib/screen/audio_record_context.dart @@ -22,11 +22,14 @@ class _AudioRecordContextState extends State { bool isRecordingCompleted = false; bool isLoading = true; late Directory appDirectory; + String _textFieldValue = ""; List> storyDatas = []; Map story = {}; + FocusNode _focusNode = FocusNode(); late int storyIndex; int paraIndex = 0; + final TextEditingController _controller = TextEditingController(); Future fetchStoryText() async { final jsonString = await rootBundle.loadString('assets/OBSTextData.json'); @@ -64,13 +67,14 @@ class _AudioRecordContextState extends State { playerController = PlayerController(); } - Future deleteRecording() async { - final file = File(path!); + Future deleteRecording(filepath) async { + final file = File(filepath!); try { if (await file.exists()) { await file.delete(); story['story'][paraIndex].remove('audio'); writeJsonToFile(story); + print('Recording deleted'); print(path!); ScaffoldMessenger.of(context).showSnackBar( @@ -116,6 +120,7 @@ class _AudioRecordContextState extends State { try { final jsonData = await file.readAsString(); final data = jsonDecode(jsonData) as Map; + _controller.text = data['story'][0]['text']; return data; } on FileSystemException { return {}; @@ -170,151 +175,236 @@ class _AudioRecordContextState extends State { ), ), ), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - storyIndex != 0 - ? IconButton( - icon: const Icon(Icons.skip_previous), - iconSize: 35, - onPressed: () { - setState(() { - storyIndex = storyIndex > 0 ? storyIndex - 1 : 0; - paraIndex = 0; - }); - _getDir(); - fetchJson(); - }, - ) - : IconButton( - icon: Icon(Icons.skip_previous), - iconSize: 35, - color: Color.fromARGB(66, 168, 163, 163).withOpacity(0.5), - onPressed: () {}, - ), - paraIndex != 0 - ? IconButton( - icon: Icon(Icons.arrow_left_sharp), - iconSize: 35, - onPressed: () { - setState(() { - paraIndex = paraIndex > 0 ? paraIndex - 1 : 0; - }); - _getDir(); - }, - ) - : IconButton( - icon: Icon(Icons.arrow_left_sharp), - iconSize: 35, - color: Color.fromARGB(66, 168, 163, 163).withOpacity(0.5), - onPressed: () {}, + Expanded( + child: SingleChildScrollView( + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + storyIndex != 0 + ? IconButton( + icon: const Icon(Icons.skip_previous), + iconSize: 35, + onPressed: () { + setState(() { + storyIndex = + storyIndex > 0 ? storyIndex - 1 : 0; + paraIndex = 0; + }); + _getDir(); + fetchJson(); + }, + ) + : IconButton( + icon: Icon(Icons.skip_previous), + iconSize: 35, + color: Color.fromARGB(66, 168, 163, 163) + .withOpacity(0.5), + onPressed: () {}, + ), + paraIndex != 0 + ? IconButton( + icon: Icon(Icons.arrow_left_sharp), + iconSize: 35, + onPressed: () { + setState(() { + paraIndex = paraIndex > 0 ? paraIndex - 1 : 0; + }); + _getDir(); + _controller.text = + story['story'][paraIndex]['text']; + }, + ) + : IconButton( + icon: Icon(Icons.arrow_left_sharp), + iconSize: 35, + color: Color.fromARGB(66, 168, 163, 163) + .withOpacity(0.5), + onPressed: () {}, + ), + Text(storyDatas[storyIndex]['storyId'].toString()), + const Text(":"), + Text(storyDatas[storyIndex]['story'][paraIndex]['id'] + .toString()), + paraIndex != storyDatas[storyIndex]['story'].length - 1 + ? IconButton( + icon: Icon(Icons.arrow_right_sharp), + iconSize: 35, + onPressed: () { + setState(() { + paraIndex = paraIndex + 1; + }); + _getDir(); + _controller.text = + story?['story']?[paraIndex]['text']; + }, + ) + : IconButton( + icon: Icon(Icons.arrow_right_sharp), + iconSize: 35, + color: Colors.black26.withOpacity(0.5), + onPressed: () {}, + ), + storyIndex != storyDatas.length - 1 + ? IconButton( + icon: Icon(Icons.skip_next), + iconSize: 35, + onPressed: () { + setState(() { + storyIndex = storyIndex + 1; + paraIndex = 0; + }); + _getDir(); + fetchJson(); + }, + ) + : IconButton( + icon: Icon(Icons.skip_next), + iconSize: 35, + color: Color.fromARGB(66, 168, 163, 163) + .withOpacity(0.5), + onPressed: () {}, + ), + ], + ), + Padding( + padding: const EdgeInsets.all(4.0), + child: SizedBox( + height: 200, + child: Container( + decoration: BoxDecoration( + boxShadow: [ + BoxShadow( + color: + Colors.grey.withOpacity(0.5), // Shadow color + spreadRadius: 1, + blurRadius: 7, + offset: + Offset(0, 3), // Changes position of shadow + ), + ], + color: Colors + .white, // Background color for the text field + borderRadius: + BorderRadius.circular(5), // Rounded corners + ), + child: Theme( + data: Theme.of(context).copyWith( + textSelectionTheme: TextSelectionThemeData( + selectionColor: + Colors.grey, // Color of the selected text + cursorColor: Colors + .grey, // Color of the caret (text cursor) + selectionHandleColor: + Colors.grey, // Color of the selection handles + ), + ), + child: TextField( + controller: _controller, + focusNode: _focusNode, + onChanged: (value) { + setState(() { + _textFieldValue = value; + }); + }, + enabled: false, + decoration: InputDecoration( + labelText: (_focusNode.hasFocus || + _textFieldValue.isNotEmpty) + ? null + : 'Start translating story', + labelStyle: TextStyle( + color: Colors + .grey), // Optional: changes label color to grey + + floatingLabelBehavior: + FloatingLabelBehavior.always, + border: InputBorder + .none, // Removes the default border + contentPadding: EdgeInsets.symmetric( + vertical: 10.0, + horizontal: 10.0), // Adjust padding as needed + ), + maxLines: + 30, // Increases the height to accommodate up to 30 lines + ), + ), + ), ), - Text(storyDatas[storyIndex]['storyId'].toString()), - const Text(":"), - Text(storyDatas[storyIndex]['story'][paraIndex]['id'].toString()), - paraIndex != storyDatas[storyIndex]['story'].length - 1 - ? IconButton( - icon: Icon(Icons.arrow_right_sharp), - iconSize: 35, - onPressed: () { - setState(() { - paraIndex = paraIndex + 1; - }); - _getDir(); - }, - ) - : IconButton( - icon: Icon(Icons.arrow_right_sharp), - iconSize: 35, - color: Colors.black26.withOpacity(0.5), - onPressed: () {}, + ), + if (story['story'][paraIndex]['audio'] != null) + WaveBubble( + path: story['story'][paraIndex]['audio'], + isSender: true, + appDirectory: appDirectory, ), - storyIndex != storyDatas.length - 1 - ? IconButton( - icon: Icon(Icons.skip_next), - iconSize: 35, - onPressed: () { - setState(() { - storyIndex = storyIndex + 1; - paraIndex = 0; - }); - _getDir(); - fetchJson(); - }, - ) - : IconButton( - icon: Icon(Icons.skip_next), - iconSize: 35, - color: Color.fromARGB(66, 168, 163, 163).withOpacity(0.5), - onPressed: () {}, + const SizedBox(height: 20), + Padding( + padding: const EdgeInsets.all( + 8, ), - ], - ), - if (story['story'][paraIndex]['audio'] != null) - WaveBubble( - path: story['story'][paraIndex]['audio'], - isSender: true, - appDirectory: appDirectory, - ), - const SizedBox(height: 20), - Padding( - padding: const EdgeInsets.all( - 8, - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - AnimatedSwitcher( - duration: const Duration(milliseconds: 200), - child: isRecording - ? AudioWaveforms( - enableGesture: true, - size: Size( - MediaQuery.of(context).size.width / 2, - 50, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + AnimatedSwitcher( + duration: const Duration(milliseconds: 200), + child: isRecording + ? AudioWaveforms( + enableGesture: true, + size: Size( + MediaQuery.of(context).size.width / 2, + 50, + ), + recorderController: recorderController, + waveStyle: const WaveStyle( + waveColor: Colors.white, + extendWaveform: true, + showMiddleLine: false, + ), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12.0), + color: const Color(0xFF1E1B26), + ), + padding: const EdgeInsets.only(left: 18), + margin: const EdgeInsets.symmetric( + horizontal: 15), + ) + : const Text(""), + ), + if (isRecording) + IconButton( + onPressed: _refreshWave, + icon: const Icon( + Icons.refresh, + color: Colors.black, + ), ), - recorderController: recorderController, - waveStyle: const WaveStyle( - waveColor: Colors.white, - extendWaveform: true, - showMiddleLine: false, + const SizedBox(width: 16), + if (story['story'][paraIndex]['audio'] == null) + Center( + child: IconButton( + onPressed: () => + _startOrStopRecording(storyIndex, paraIndex), + icon: Icon(isRecording ? Icons.stop : Icons.mic), + color: Colors.black, + iconSize: 28, + ), ), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(12.0), - color: const Color(0xFF1E1B26), + if (story['story'][paraIndex]['audio'] != null) + IconButton( + onPressed: () => deleteRecording( + story['story'][paraIndex]['audio']), + icon: const Icon( + Icons.delete, + color: Colors.black, + ), ), - padding: const EdgeInsets.only(left: 18), - margin: const EdgeInsets.symmetric(horizontal: 15), - ) - : const Text(""), - ), - if (isRecording) - IconButton( - onPressed: _refreshWave, - icon: const Icon( - Icons.refresh, - color: Colors.black, - ), - ), - const SizedBox(width: 16), - Center( - child: IconButton( - onPressed: () => - _startOrStopRecording(storyIndex, paraIndex), - icon: Icon(isRecording ? Icons.stop : Icons.mic), - color: Colors.black, - iconSize: 28, - ), - ), - if (story['story'][paraIndex]['audio'] != null) - IconButton( - onPressed: deleteRecording, - icon: const Icon( - Icons.delete, - color: Colors.black, + ], ), ), - ], + ], + ), ), ), ], @@ -323,16 +413,13 @@ class _AudioRecordContextState extends State { } void _startOrStopRecording(storyNumber, paraNumber) async { - // appDirectory = await getApplicationDocumentsDirectory(); - // String appDocPath = appDirectory.path; - // path = '$appDocPath/${storyNumber}.${paraNumber}.wav'; isLoading = false; try { if (isRecording) { - recorderController.reset(); - print(path); path = await recorderController.stop(false); + recorderController.reset(); + if (path != "") { isRecordingCompleted = true; debugPrint(path); diff --git a/obs_flutter/obs/obs_demo/lib/utils/chat_bubble.dart b/obs_flutter/obs/obs_demo/lib/utils/chat_bubble.dart index 084fd1e..b197a3c 100644 --- a/obs_flutter/obs/obs_demo/lib/utils/chat_bubble.dart +++ b/obs_flutter/obs/obs_demo/lib/utils/chat_bubble.dart @@ -98,6 +98,7 @@ class _WaveBubbleState extends State { alignment: widget.isSender ? Alignment.center : Alignment.centerLeft, child: Container( + width: double.infinity, padding: EdgeInsets.only( bottom: 6, right: widget.isSender ? 0 : 10, @@ -119,7 +120,7 @@ class _WaveBubbleState extends State { controller.playerState.isPlaying ? await controller.pausePlayer() : await controller.startPlayer( - finishMode: FinishMode.loop, + finishMode: FinishMode.pause, ); }, icon: Icon( From d7d5e90eca089f84d1377876a998a4681a7a3741 Mon Sep 17 00:00:00 2001 From: "Pooja.Saini" Date: Thu, 11 Jul 2024 11:59:03 +0530 Subject: [PATCH 5/7] added resume recording feature and also did ui fixes --- .../lib/screen/audio_record_context.dart | 153 ++++++++++-------- .../obs/obs_demo/lib/screen/bottomNavi.dart | 12 +- obs_flutter/obs/obs_demo/pubspec.yaml | 1 - 3 files changed, 89 insertions(+), 77 deletions(-) diff --git a/obs_flutter/obs/obs_demo/lib/screen/audio_record_context.dart b/obs_flutter/obs/obs_demo/lib/screen/audio_record_context.dart index 6b68e37..87b6df3 100644 --- a/obs_flutter/obs/obs_demo/lib/screen/audio_record_context.dart +++ b/obs_flutter/obs/obs_demo/lib/screen/audio_record_context.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:audio_waveforms/audio_waveforms.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:http/http.dart'; import 'package:path_provider/path_provider.dart'; import 'package:obs_demo/utils/chat_bubble.dart'; @@ -19,6 +20,7 @@ class _AudioRecordContextState extends State { late final PlayerController playerController; String? path; bool isRecording = false; + bool isPaused = false; bool isRecordingCompleted = false; bool isLoading = true; late Directory appDirectory; @@ -335,73 +337,69 @@ class _AudioRecordContextState extends State { ), if (story['story'][paraIndex]['audio'] != null) WaveBubble( - path: story['story'][paraIndex]['audio'], - isSender: true, - appDirectory: appDirectory, - ), - const SizedBox(height: 20), - Padding( - padding: const EdgeInsets.all( - 8, - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - AnimatedSwitcher( - duration: const Duration(milliseconds: 200), - child: isRecording - ? AudioWaveforms( - enableGesture: true, - size: Size( - MediaQuery.of(context).size.width / 2, - 50, - ), - recorderController: recorderController, - waveStyle: const WaveStyle( - waveColor: Colors.white, - extendWaveform: true, - showMiddleLine: false, - ), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(12.0), - color: const Color(0xFF1E1B26), - ), - padding: const EdgeInsets.only(left: 18), - margin: const EdgeInsets.symmetric( - horizontal: 15), - ) - : const Text(""), - ), - if (isRecording) - IconButton( - onPressed: _refreshWave, - icon: const Icon( - Icons.refresh, - color: Colors.black, - ), + path: story['story'][paraIndex]['audio'], + isSender: true, + appDirectory: appDirectory, + deleteRecording: deleteRecording), + if (!isRecording && + story['story'][paraIndex]['audio'] == null) + WaveBubble( + path: '', + isSender: false, + appDirectory: appDirectory, + deleteRecording: deleteRecording), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + AnimatedSwitcher( + duration: const Duration(milliseconds: 200), + child: isRecording + ? AudioWaveforms( + enableGesture: true, + size: Size( + MediaQuery.of(context).size.width / 2, + 50, + ), + recorderController: recorderController, + waveStyle: const WaveStyle( + waveColor: Colors.white, + extendWaveform: true, + showMiddleLine: false, + ), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12.0), + color: const Color(0xFF1E1B26), + ), + ) + : const Text(""), + ), + if (story['story'][paraIndex]['audio'] == null) + Center( + child: IconButton( + onPressed: () => + _startOrStopRecording(storyIndex, paraIndex), + icon: Icon(isRecording ? Icons.stop : Icons.mic), + color: Colors.black, + iconSize: 28, ), - const SizedBox(width: 16), - if (story['story'][paraIndex]['audio'] == null) - Center( - child: IconButton( - onPressed: () => - _startOrStopRecording(storyIndex, paraIndex), - icon: Icon(isRecording ? Icons.stop : Icons.mic), - color: Colors.black, - iconSize: 28, - ), + ), + if (isRecording && !isPaused) + IconButton( + onPressed: _pauseRecording, + icon: const Icon( + Icons.pause, + color: Colors.black, ), - if (story['story'][paraIndex]['audio'] != null) - IconButton( - onPressed: () => deleteRecording( - story['story'][paraIndex]['audio']), - icon: const Icon( - Icons.delete, - color: Colors.black, - ), + ), + if (isPaused) + IconButton( + onPressed: _resumeRecording, + icon: const Icon( + Icons.play_arrow, + color: Colors.black, ), - ], - ), + ), + ], ), ], ), @@ -417,7 +415,10 @@ class _AudioRecordContextState extends State { try { if (isRecording) { print(path); - path = await recorderController.stop(false); + path = await recorderController.stop(); + setState(() { + isPaused = false; + }); recorderController.reset(); if (path != "") { @@ -441,6 +442,28 @@ class _AudioRecordContextState extends State { } } + void _pauseRecording() async { + try { + await recorderController.pause(); + setState(() { + isPaused = true; + }); + } catch (e) { + debugPrint(e.toString()); + } + } + + void _resumeRecording() async { + try { + await recorderController.record(); + setState(() { + isPaused = false; + }); + } catch (e) { + debugPrint(e.toString()); + } + } + void _refreshWave() { if (isRecording) recorderController.refresh(); } diff --git a/obs_flutter/obs/obs_demo/lib/screen/bottomNavi.dart b/obs_flutter/obs/obs_demo/lib/screen/bottomNavi.dart index 23fdb31..a29251f 100644 --- a/obs_flutter/obs/obs_demo/lib/screen/bottomNavi.dart +++ b/obs_flutter/obs/obs_demo/lib/screen/bottomNavi.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:obs_demo/editortext.dart'; import 'package:obs_demo/screen/audio_record_context.dart'; -import 'package:obs_demo/screen/audio_recorder.dart'; import 'package:obs_demo/screen/dashboard.dart'; import 'package:obs_demo/user_profile.dart'; @@ -62,11 +61,6 @@ class _BottomNavigationBarExampleState rowIndex: 0, onUpdateTextAvailability: onUpdateTextAvailability, ), - // Text( - // 'Audio', - // style: optionStyle, - // ), - AudioRecorder(), AudioRecordContext(rowIndex: 0), Column( mainAxisAlignment: MainAxisAlignment.center, @@ -135,11 +129,7 @@ class _BottomNavigationBarExampleState label: '', ), BottomNavigationBarItem( - icon: Icon(Icons.music_note), - label: '', - ), - BottomNavigationBarItem( - icon: Icon(Icons.volume_up), + icon: Icon(Icons.mic), label: '', ), BottomNavigationBarItem( diff --git a/obs_flutter/obs/obs_demo/pubspec.yaml b/obs_flutter/obs/obs_demo/pubspec.yaml index 53a1452..b5099e0 100644 --- a/obs_flutter/obs/obs_demo/pubspec.yaml +++ b/obs_flutter/obs/obs_demo/pubspec.yaml @@ -43,7 +43,6 @@ dependencies: audio_waveforms: ^1.0.5 file_picker: ^5.0.0 flutter_ffmpeg: ^0.4.2 - shared_preferences: ^2.2.3 dev_dependencies: flutter_test: From e9ccabd1c454ab80ee6b92b1a51e1591e584072d Mon Sep 17 00:00:00 2001 From: "Pooja.Saini" Date: Tue, 16 Jul 2024 09:50:36 +0530 Subject: [PATCH 6/7] ui changes --- .../lib/screen/audio_record_context.dart | 61 +++++++-- .../obs/obs_demo/lib/screen/bottomNavi.dart | 6 +- .../obs/obs_demo/lib/utils/chat_bubble.dart | 126 ++++++++++-------- 3 files changed, 127 insertions(+), 66 deletions(-) diff --git a/obs_flutter/obs/obs_demo/lib/screen/audio_record_context.dart b/obs_flutter/obs/obs_demo/lib/screen/audio_record_context.dart index 87b6df3..3d69e98 100644 --- a/obs_flutter/obs/obs_demo/lib/screen/audio_record_context.dart +++ b/obs_flutter/obs/obs_demo/lib/screen/audio_record_context.dart @@ -3,7 +3,6 @@ import 'dart:io'; import 'package:audio_waveforms/audio_waveforms.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:http/http.dart'; import 'package:path_provider/path_provider.dart'; import 'package:obs_demo/utils/chat_bubble.dart'; @@ -23,6 +22,7 @@ class _AudioRecordContextState extends State { bool isPaused = false; bool isRecordingCompleted = false; bool isLoading = true; + bool isPlaying = false; late Directory appDirectory; String _textFieldValue = ""; @@ -146,6 +146,21 @@ class _AudioRecordContextState extends State { super.dispose(); } + void _playAudio(audioPath) { + // Ensure to provide the correct path to your audio file + playerController.preparePlayer(path: audioPath); + + setState(() { + isPlaying = true; + }); + playerController.startPlayer( + finishMode: FinishMode.pause, + ); + setState(() { + isPlaying = false; + }); + } + @override Widget build(BuildContext context) { String text = @@ -164,18 +179,38 @@ class _AudioRecordContextState extends State { ), ), child: Container( - width: double.infinity, - height: 200, - padding: const EdgeInsets.all(8), - color: const Color(0xF0FDFDFF).withOpacity(0.9), - child: Text( - storyDatas[storyIndex]['story'][paraIndex]['text'], - style: const TextStyle( - fontSize: 14.5, - fontWeight: FontWeight.bold, - ), - ), - ), + width: double.infinity, + height: 200, + padding: const EdgeInsets.all(8), + color: const Color(0xF0FDFDFF).withOpacity(0.9), + child: Column( + children: [ + Text( + storyDatas[storyIndex]['story'][paraIndex]['text'], + style: const TextStyle( + fontSize: 14.5, + fontWeight: FontWeight.bold, + ), + ), + isPlaying + ? IconButton( + onPressed: () => {}, + // _playAudio(story['story'][paraIndex]['audio']), + icon: const Icon( + Icons.stop, + color: Colors.black, + ), + ) + : IconButton( + onPressed: () => + _playAudio(story['story'][paraIndex]['audio']), + icon: const Icon( + Icons.play_arrow, + color: Colors.black, + ), + ), + ], + )), ), Expanded( child: SingleChildScrollView( diff --git a/obs_flutter/obs/obs_demo/lib/screen/bottomNavi.dart b/obs_flutter/obs/obs_demo/lib/screen/bottomNavi.dart index a29251f..26b84b9 100644 --- a/obs_flutter/obs/obs_demo/lib/screen/bottomNavi.dart +++ b/obs_flutter/obs/obs_demo/lib/screen/bottomNavi.dart @@ -61,6 +61,10 @@ class _BottomNavigationBarExampleState rowIndex: 0, onUpdateTextAvailability: onUpdateTextAvailability, ), + // Text( + // 'Audio', + // style: optionStyle, + // ), AudioRecordContext(rowIndex: 0), Column( mainAxisAlignment: MainAxisAlignment.center, @@ -129,7 +133,7 @@ class _BottomNavigationBarExampleState label: '', ), BottomNavigationBarItem( - icon: Icon(Icons.mic), + icon: Icon(Icons.volume_up), label: '', ), BottomNavigationBarItem( diff --git a/obs_flutter/obs/obs_demo/lib/utils/chat_bubble.dart b/obs_flutter/obs/obs_demo/lib/utils/chat_bubble.dart index b197a3c..f99ee5c 100644 --- a/obs_flutter/obs/obs_demo/lib/utils/chat_bubble.dart +++ b/obs_flutter/obs/obs_demo/lib/utils/chat_bubble.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:audio_waveforms/audio_waveforms.dart'; +import 'package:flutter/widgets.dart'; class WaveBubble extends StatefulWidget { final bool isSender; @@ -10,15 +11,17 @@ class WaveBubble extends StatefulWidget { final String? path; final double? width; final Directory appDirectory; + final Function deleteRecording; - const WaveBubble({ - Key? key, - required this.appDirectory, - this.width, - this.index, - this.isSender = false, - this.path, - }) : super(key: key); + const WaveBubble( + {Key? key, + required this.appDirectory, + this.width, + this.index, + this.isSender = false, + this.path, + required this.deleteRecording}) + : super(key: key); @override State createState() => _WaveBubbleState(); @@ -94,57 +97,76 @@ class _WaveBubbleState extends State { @override Widget build(BuildContext context) { return widget.path != null || file?.path != null - ? Align( - alignment: - widget.isSender ? Alignment.center : Alignment.centerLeft, - child: Container( - width: double.infinity, - padding: EdgeInsets.only( - bottom: 6, - right: widget.isSender ? 0 : 10, - top: 6, - ), - margin: const EdgeInsets.symmetric(vertical: 8, horizontal: 12), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(10), - color: widget.isSender - ? const Color(0xFF276bfd) - : const Color(0xFF343145), + ? Column( + children: [ + Align( + alignment: + widget.isSender ? Alignment.center : Alignment.centerLeft, + child: Container( + width: double.infinity, + padding: EdgeInsets.only( + bottom: 6, + right: widget.isSender ? 0 : 10, + top: 6, + ), + margin: + const EdgeInsets.symmetric(vertical: 8, horizontal: 12), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + color: widget.isSender + ? const Color(0xFF276bfd) + : const Color(0xFF343145), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + AudioFileWaveforms( + key: UniqueKey(), // Ensure a unique key is provided + size: Size(MediaQuery.of(context).size.width / 2, 70), + playerController: controller, + waveformType: widget.index?.isOdd ?? false + ? WaveformType.fitWidth + : WaveformType.long, + playerWaveStyle: playerWaveStyle, + ), + if (widget.isSender) const SizedBox(width: 10), + ], + ), + ), ), - child: Row( - mainAxisSize: MainAxisSize.min, + Row( + mainAxisAlignment: MainAxisAlignment.center, children: [ - if (!controller.playerState.isStopped) + if (widget.isSender) + if (!controller.playerState.isStopped) + IconButton( + onPressed: () async { + controller.playerState.isPlaying + ? await controller.pausePlayer() + : await controller.startPlayer( + finishMode: FinishMode.pause, + ); + }, + icon: Icon( + controller.playerState.isPlaying + ? Icons.stop + : Icons.play_arrow, + ), + color: Colors.black, + splashColor: Colors.transparent, + highlightColor: Colors.transparent, + ), + if (widget.path != null && widget.path != "") IconButton( - onPressed: () async { - controller.playerState.isPlaying - ? await controller.pausePlayer() - : await controller.startPlayer( - finishMode: FinishMode.pause, - ); - }, - icon: Icon( - controller.playerState.isPlaying - ? Icons.stop - : Icons.play_arrow, + onPressed: () => widget.deleteRecording(widget.path), + icon: const Icon( + Icons.delete, + color: Colors.black, ), - color: Colors.white, - splashColor: Colors.transparent, - highlightColor: Colors.transparent, ), - AudioFileWaveforms( - key: UniqueKey(), // Ensure a unique key is provided - size: Size(MediaQuery.of(context).size.width / 2, 70), - playerController: controller, - waveformType: widget.index?.isOdd ?? false - ? WaveformType.fitWidth - : WaveformType.long, - playerWaveStyle: playerWaveStyle, - ), - if (widget.isSender) const SizedBox(width: 10), ], ), - ), + ], ) : const SizedBox.shrink(); } From c8b47ba44752bbf76f7e8df905165d12cf6a9ce9 Mon Sep 17 00:00:00 2001 From: "Pooja.Saini" Date: Tue, 16 Jul 2024 10:15:44 +0530 Subject: [PATCH 7/7] added the comments --- obs_flutter/obs/obs_demo/README.md | 14 ++++++++++++++ .../lib/screen/audio_record_context.dart | 18 +++++++++++++++++- .../obs/obs_demo/lib/screen/bottomNavi.dart | 2 ++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/obs_flutter/obs/obs_demo/README.md b/obs_flutter/obs/obs_demo/README.md index 8cfa551..8041cdc 100644 --- a/obs_flutter/obs/obs_demo/README.md +++ b/obs_flutter/obs/obs_demo/README.md @@ -14,3 +14,17 @@ A few resources to get you started if this is your first Flutter project: For help getting started with Flutter development, view the [online documentation](https://docs.flutter.dev/), which offers tutorials, samples, guidance on mobile development, and a full API reference. + +- Clone the repo from the github into your system +- Packages used + - http: ^0.13.3 + - path_provider: ^2.0.2 + - audioplayers: ^4.1.0 + - record: ^4.4.4 + - permission_handler: ^11.3.1 + - audio_waveforms: ^1.0.5 +- how to add the package + - flutter pub add package_name +- If already have android studio than through vs code you can ctr+hipt+p +- Launch emulater than select the emmulater +- Than run the cammond `flutter run` diff --git a/obs_flutter/obs/obs_demo/lib/screen/audio_record_context.dart b/obs_flutter/obs/obs_demo/lib/screen/audio_record_context.dart index 3d69e98..72af963 100644 --- a/obs_flutter/obs/obs_demo/lib/screen/audio_record_context.dart +++ b/obs_flutter/obs/obs_demo/lib/screen/audio_record_context.dart @@ -15,6 +15,7 @@ class AudioRecordContext extends StatefulWidget { } class _AudioRecordContextState extends State { + //global variables late final RecorderController recorderController; late final PlayerController playerController; String? path; @@ -32,7 +33,7 @@ class _AudioRecordContextState extends State { late int storyIndex; int paraIndex = 0; final TextEditingController _controller = TextEditingController(); - +// this function for fetching the story data from the json Future fetchStoryText() async { final jsonString = await rootBundle.loadString('assets/OBSTextData.json'); setState(() { @@ -50,6 +51,7 @@ class _AudioRecordContextState extends State { super.initState(); } +//initiaize the directory void _getDir() async { appDirectory = await getApplicationDocumentsDirectory(); String appDocPath = appDirectory.path; @@ -58,6 +60,7 @@ class _AudioRecordContextState extends State { setState(() {}); } +//initiaize controllerss value void _initialiseControllers() { recorderController = RecorderController() ..androidEncoder = AndroidEncoder.aac @@ -69,6 +72,7 @@ class _AudioRecordContextState extends State { playerController = PlayerController(); } +// for deleting the recording from the device path and also from the json Future deleteRecording(filepath) async { final file = File(filepath!); try { @@ -99,6 +103,7 @@ class _AudioRecordContextState extends State { } } +//fetching the json data also writing the data Future fetchJson() async { Map data = await readJsonToFile(); if (data.isEmpty) { @@ -115,6 +120,7 @@ class _AudioRecordContextState extends State { } } +//reading the json file Future> readJsonToFile() async { final directory = await getApplicationDocumentsDirectory(); final file = File('${directory.path}/${storyIndex}.json'); @@ -132,6 +138,7 @@ class _AudioRecordContextState extends State { } } +//writting into the json file Future writeJsonToFile(Map data) async { final directory = await getApplicationDocumentsDirectory(); final file = File('${directory.path}/${storyIndex}.json'); @@ -146,6 +153,7 @@ class _AudioRecordContextState extends State { super.dispose(); } +//playing the audio through the path void _playAudio(audioPath) { // Ensure to provide the correct path to your audio file playerController.preparePlayer(path: audioPath); @@ -370,6 +378,7 @@ class _AudioRecordContextState extends State { ), ), ), + //for audio waves if (story['story'][paraIndex]['audio'] != null) WaveBubble( path: story['story'][paraIndex]['audio'], @@ -408,6 +417,7 @@ class _AudioRecordContextState extends State { ) : const Text(""), ), + //start and stop recording if (story['story'][paraIndex]['audio'] == null) Center( child: IconButton( @@ -418,6 +428,7 @@ class _AudioRecordContextState extends State { iconSize: 28, ), ), + //pause reording if (isRecording && !isPaused) IconButton( onPressed: _pauseRecording, @@ -426,6 +437,7 @@ class _AudioRecordContextState extends State { color: Colors.black, ), ), + //resume recording if (isPaused) IconButton( onPressed: _resumeRecording, @@ -445,6 +457,7 @@ class _AudioRecordContextState extends State { ); } +// this function work for start and stop recording void _startOrStopRecording(storyNumber, paraNumber) async { isLoading = false; try { @@ -477,6 +490,7 @@ class _AudioRecordContextState extends State { } } +// this function work for paue recording void _pauseRecording() async { try { await recorderController.pause(); @@ -487,6 +501,7 @@ class _AudioRecordContextState extends State { debugPrint(e.toString()); } } +// this function work for resume recording void _resumeRecording() async { try { @@ -499,6 +514,7 @@ class _AudioRecordContextState extends State { } } +//refresh the waves void _refreshWave() { if (isRecording) recorderController.refresh(); } diff --git a/obs_flutter/obs/obs_demo/lib/screen/bottomNavi.dart b/obs_flutter/obs/obs_demo/lib/screen/bottomNavi.dart index 26b84b9..44c07a9 100644 --- a/obs_flutter/obs/obs_demo/lib/screen/bottomNavi.dart +++ b/obs_flutter/obs/obs_demo/lib/screen/bottomNavi.dart @@ -65,6 +65,7 @@ class _BottomNavigationBarExampleState // 'Audio', // style: optionStyle, // ), + //call the audio recorder here AudioRecordContext(rowIndex: 0), Column( mainAxisAlignment: MainAxisAlignment.center, @@ -132,6 +133,7 @@ class _BottomNavigationBarExampleState icon: Icon(Icons.create), label: '', ), + //added the icon for audio recorder BottomNavigationBarItem( icon: Icon(Icons.volume_up), label: '',