import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; import 'package:just_audio/just_audio.dart'; import 'package:path_provider/path_provider.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:record_mp3/record_mp3.dart'; import 'package:tangheem/classes/colors.dart'; import 'package:tangheem/classes/utils.dart'; class AyaRecordWidget extends StatefulWidget { AyaRecordWidget({Key key}) : super(key: key); @override _AyaRecordWidgetState createState() => _AyaRecordWidgetState(); } class _AyaRecordWidgetState extends State { bool _isRecording = false; AudioPlayer _audioPlayer; bool _isAudioHaveError = false; @override void initState() { super.initState(); _audioPlayer = AudioPlayer(); } @override void dispose() { _audioPlayer.dispose(); if (recordFilePath != null && File(recordFilePath).existsSync()) { File(recordFilePath).deleteSync(); } super.dispose(); } Future checkPermission() async { if (!await Permission.microphone.isGranted) { PermissionStatus status = await Permission.microphone.request(); if (status != PermissionStatus.granted) { return false; } } return true; } void startRecord() async { bool hasPermission = await checkPermission(); if (hasPermission) { recordFilePath = await getFilePath(); _isRecording = true; RecordMp3.instance.start(recordFilePath, (type) { _isRecording = false; setState(() {}); }); } else { Utils.showToast("يجب أن تمنح الإذن للتسجيل"); } setState(() {}); } void stopRecord() async { bool _isStop = RecordMp3.instance.stop(); if (_isStop) { _isRecording = false; setState(() {}); } } String recordFilePath; void play() async { if (recordFilePath != null && File(recordFilePath).existsSync()) { try { await _audioPlayer.setFilePath(recordFilePath); _audioPlayer.play(); } catch (ex) { _isAudioHaveError = true; } } else { Utils.showToast("يجب عليك التسجيل أولا"); } } Future getFilePath() async { Directory storageDirectory = await getApplicationDocumentsDirectory(); if (Platform.isIOS) { storageDirectory = await getTemporaryDirectory(); } String sdPath = storageDirectory.path + "/record"; var d = Directory(sdPath); if (!d.existsSync()) { d.createSync(recursive: true); } return sdPath + "/temp${DateTime.now().millisecondsSinceEpoch}.mp3"; } @override Widget build(BuildContext context) { return Stack( alignment: Alignment.bottomCenter, children: [ Container( margin: EdgeInsets.only(top: 4, bottom: 20), padding: EdgeInsets.only(bottom: 20), width: double.infinity, decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(8), ), child: Column( children: [ Container( height: 60, width: double.infinity, margin: EdgeInsets.only(bottom: 8), alignment: Alignment.center, decoration: BoxDecoration( color: ColorConsts.primaryBlue, borderRadius: BorderRadius.only( topLeft: Radius.circular(8), topRight: Radius.circular(8), ), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ SvgPicture.asset( "assets/icons/mic_icon.svg", width: 25, height: 25, ), SizedBox(width: 12), Text( "سجل الآية بصوتك", style: TextStyle(fontSize: 18, color: Colors.white), ), ], ), ), InkWell( onTap: () { if (_isRecording) { stopRecord(); } else { startRecord(); } }, child: SvgPicture.asset( _isRecording ? "assets/icons/recording_on.svg" : "assets/icons/recording_off.svg", width: 60, height: 60, ), ), if (!(recordFilePath != null && File(recordFilePath).existsSync())) SizedBox(height: 16), if (recordFilePath != null && File(recordFilePath).existsSync()) Container( height: 50, margin: EdgeInsets.all(16), padding: EdgeInsets.only(left: 12, right: 12), decoration: BoxDecoration( color: ColorConsts.secondaryWhite, borderRadius: BorderRadius.circular(30), ), child: Row( children: [ Expanded( child: StreamBuilder( stream: _audioPlayer.positionStream, builder: (context, snapshot) { final state = snapshot.data; return SliderTheme( data: SliderTheme.of(context).copyWith( inactiveTrackColor: ColorConsts.sliderBackground, activeTrackColor: ColorConsts.secondaryOrange, trackHeight: 3.0, thumbColor: ColorConsts.primaryBlack, thumbShape: RoundSliderThumbShape(enabledThumbRadius: 6.0), overlayColor: ColorConsts.primaryBlack.withAlpha(32), overlayShape: RoundSliderOverlayShape(overlayRadius: 6.0), ), child: Directionality( textDirection: TextDirection.ltr, child: Slider( value: (state?.inSeconds ?? 0) + 0.0, min: 0, max: (_audioPlayer?.duration?.inSeconds ?? 0) + 0.0, onChanged: (value) {}, ), ), ); }, ), ), SizedBox(width: 8), StreamBuilder( stream: _audioPlayer.positionStream, builder: (context, snapshot) { final state = snapshot.data; return Text( _durationTime(state) ?? "", style: TextStyle(color: ColorConsts.textGrey1, height: 1.1, fontFamily: "Roboto", fontSize: 16), ); }, ), SizedBox(width: 8), StreamBuilder( stream: _audioPlayer.playerStateStream, builder: (context, snapshot) { final state = snapshot.data?.playing ?? false; if (state) { if (_audioPlayer?.duration?.inSeconds == _audioPlayer?.position?.inSeconds) { _audioPlayer.pause(); _audioPlayer.seek(Duration.zero); } } return IconButton( highlightColor: Colors.transparent, splashColor: Colors.transparent, constraints: BoxConstraints(), padding: EdgeInsets.only(right: 0), icon: SvgPicture.asset(state ? "assets/icons/pause.svg" : "assets/icons/play_aya.svg", height: 16, width: 16), onPressed: () { state ? _audioPlayer.pause() : _isAudioHaveError ? Utils.showToast("خطأ في تحميل ملف الصوت") : play(); }); }, ), ], ), ) ], ), ), Positioned( bottom: 0, child: InkWell( borderRadius: BorderRadius.circular(30), onTap: () async { if (await _requestStoragePermission()) { if (recordFilePath != null && File(recordFilePath).existsSync()) { saveToPhoneStorage(recordFilePath); } else { Utils.showToast("يجب عليك تسجيل صوتك أولا"); } } else { Utils.showToast("يجب أن تمنح الإذن لتنزيل التسجيل"); } }, child: Container( height: 40, padding: EdgeInsets.only(left: 24, right: 24), alignment: Alignment.centerRight, decoration: BoxDecoration( borderRadius: BorderRadius.circular(30), color: ColorConsts.gradientPink, gradient: LinearGradient( stops: [0.0, 0.5], begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ColorConsts.gradientPink, ColorConsts.gradientOrange], ), ), child: Text( "تنزيل التسجيل", style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14, color: Colors.white, height: 1.5), ), ), ), ), ], ); } Future _requestStoragePermission() async { Map statuses = await [Permission.storage].request(); return statuses[Permission.storage].isGranted; } String _durationTime(Duration duration) { if (duration == null) { return "00:00"; } String twoDigits(int n) => n.toString().padLeft(2, "0"); String twoDigitMinutes = twoDigits(duration.inMinutes.remainder(60)); String twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60)); return "$twoDigitMinutes:$twoDigitSeconds"; } void saveToPhoneStorage(String filePath) async { File file = File(filePath); Directory storageDirectory; if (Platform.isAndroid) { storageDirectory = await getExternalStorageDirectory(); } else if (Platform.isIOS) { storageDirectory = await getApplicationDocumentsDirectory(); } else { return; } String storagePath = storageDirectory.path; if (storagePath.contains("/Android/data")) { storagePath = storagePath.substring(0, storagePath.indexOf("/Android/data")); } storagePath += "/Download/Tangheem/"; var d = Directory(storagePath); if (!d.existsSync()) { d.createSync(recursive: true); } storagePath += "Tangheem${DateTime.now().millisecondsSinceEpoch}.mp3"; print("storagePath:$storagePath"); await file.copy(storagePath); Utils.showToast("تم التنزيل"); } }