import 'dart:io'; import 'dart:math' as math; import 'dart:typed_data'; import 'dart:ui' as ui; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:image_gallery_saver/image_gallery_saver.dart'; import 'package:just_audio/just_audio.dart'; import 'package:path_provider/path_provider.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:share/share.dart'; import 'package:tangheem/classes/colors.dart'; import 'package:tangheem/classes/utils.dart'; import 'package:tangheem/models/aya_tangheem_type_mapped.dart'; import 'package:volume_controller/volume_controller.dart'; class AyaPlayerWidget extends StatefulWidget { final String surahName; final GlobalKey globalKey; final List voiceNoteList; AyaPlayerWidget({Key key, this.surahName, this.voiceNoteList, @required this.globalKey}) : super(key: key); @override _AyaPlayerWidgetState createState() { return _AyaPlayerWidgetState(); } } class _AyaPlayerWidgetState extends State { double sliderValue = 0.4; bool isPlaying = false; AudioPlayer _player; bool _isAudioHaveError = false; int _currentVoiceNote = -1; @override void initState() { super.initState(); _player = AudioPlayer(); setAudioSource(); } setAudioSource() { try { var voiceList = []; if (widget.voiceNoteList != null && widget.voiceNoteList.length > 0) { voiceList = widget.voiceNoteList.map((e) => AudioSource.uri(Uri.parse(e.exposeFilePath))).toList(); _currentVoiceNote = 0; } final _playlist = ConcatenatingAudioSource(children: voiceList); _player.setAudioSource(_playlist, initialIndex: 0, initialPosition: Duration.zero).then((value) => () {}).catchError(() { _isAudioHaveError = true; }); } catch (e) { _isAudioHaveError = true; } } @override void didUpdateWidget(covariant AyaPlayerWidget oldWidget) { if (widget.voiceNoteList != oldWidget.voiceNoteList) { setAudioSource(); } super.didUpdateWidget(oldWidget); } @override void dispose() { _player.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Container( margin: EdgeInsets.only(top: 8, bottom: 8), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(8), ), padding: EdgeInsets.all(8), child: Column( children: [ Row( children: [ Transform.rotate( angle: 180 * math.pi / 180, child: SvgPicture.asset( "assets/icons/drop_menu.svg", width: 16, ), ), Container( width: 50.0, margin: EdgeInsets.only(left: 8, right: 8), height: 50.0, decoration: BoxDecoration( image: DecorationImage( fit: BoxFit.cover, image: NetworkImage("https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSgswb98Ga7aJjIaNvqUWBsjMkdR18xzp3pyg&usqp=CAU"), ), borderRadius: BorderRadius.all( Radius.circular(30.0), ), ), ), Expanded( child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( widget.surahName, maxLines: 1, overflow: TextOverflow.ellipsis, style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14, color: ColorConsts.primaryBlack, height: 1), ), SizedBox(height: 4), Text( _currentVoiceNote < 1 ? "" : widget.voiceNoteList[_currentVoiceNote], style: TextStyle(fontSize: 10, color: ColorConsts.textGrey1, height: 1), ), ], ), ), commonIconButton("assets/icons/download_aya.svg", () async { if (await _requestPermission()) { if (await _saveAya()) { Utils.showToast("Aya saved successfully"); } else { Utils.showToast("Failed to save aya"); } } else { Utils.showToast("you must granted permission to download aya."); } }), commonIconButton("assets/icons/share_aya.svg", () { _shareAya(); }), commonIconButton("assets/icons/bookmark.svg", () {}), commonIconButton("assets/icons/audio_level.svg", () async { // var vol = await VolumeController.getVolume(); VolumeController.maxVolume(); }), ], ), SizedBox(height: 8), Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ commonIconButton("assets/icons/next_aya.svg", () { _player.seekToNext(); }), SizedBox(width: 4), StreamBuilder( stream: _player.playerStateStream, builder: (context, snapshot) { final state = snapshot.data?.playing ?? false; if (state) { if (_player?.duration?.inSeconds == _player?.position?.inSeconds) { _player.pause(); _player.seek(Duration.zero); } } return commonIconButton(state ? "assets/icons/pause.svg" : "assets/icons/play_aya.svg", () { state ? _player.pause() : _isAudioHaveError ? Utils.showToast("Audio file is not loaded") : _player.play(); }); }, ), SizedBox(width: 4), commonIconButton("assets/icons/previous_aya.svg", () { _player.seekToPrevious(); }), SizedBox(width: 16), Expanded( child: StreamBuilder( stream: _player.positionStream, builder: (context, snapshot) { final state = snapshot.data; return SliderTheme( data: SliderTheme.of(context).copyWith( inactiveTrackColor: ColorConsts.sliderBackground, activeTrackColor: ColorConsts.secondaryOrange, trackHeight: 8.0, thumbColor: ColorConsts.primaryBlack, thumbShape: RoundSliderThumbShape(enabledThumbRadius: 10.0), overlayColor: ColorConsts.primaryBlack.withAlpha(32), overlayShape: RoundSliderOverlayShape(overlayRadius: 12.0), ), child: Directionality( textDirection: TextDirection.ltr, child: Slider( value: (state?.inSeconds ?? 0) + 0.0, min: 0, max: (_player?.duration?.inSeconds ?? 0) + 0.0, onChanged: (value) { _player.seek(Duration(seconds: value.round())); }, ), ), ); }, ), ), SizedBox(width: 8), StreamBuilder( stream: _player.positionStream, builder: (context, snapshot) { final state = snapshot.data; return Text( _durationTime(state) ?? "", style: TextStyle(color: ColorConsts.textGrey1, height: 1.1, fontFamily: "Roboto"), ); }, ), ], ) ], ), ); } 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"; } Widget commonIconButton(String icon, VoidCallback onPressed) { return IconButton( highlightColor: Colors.transparent, splashColor: Colors.transparent, constraints: BoxConstraints(), padding: EdgeInsets.only(right: 2), icon: SvgPicture.asset(icon, height: 16, width: 16), onPressed: onPressed); } Future _requestPermission() async { Map statuses = await [ Permission.storage, ].request(); return statuses[Permission.storage].isGranted; } Future _saveAya() async { Utils.showLoading(context); try { RenderRepaintBoundary boundary = widget.globalKey.currentContext.findRenderObject(); ui.Image image = await boundary.toImage(pixelRatio: 3.0); ByteData byteData = await image.toByteData(format: ui.ImageByteFormat.png); final result = await ImageGallerySaver.saveImage(byteData.buffer.asUint8List(), quality: 100); Utils.hideLoading(context); return result["isSuccess"]; } catch (ex) { Future.delayed(Duration(seconds: 1), () { Utils.hideLoading(context); }); return false; } } void _shareAya() async { Utils.showLoading(context); try { RenderRepaintBoundary boundary = widget.globalKey.currentContext.findRenderObject(); ui.Image image = await boundary.toImage(pixelRatio: 3.0); ByteData byteData = await image.toByteData(format: ui.ImageByteFormat.png); Uint8List pngBytes = byteData.buffer.asUint8List(); final tempDir = await getTemporaryDirectory(); final file = await File('${tempDir.path}/${DateTime.now().toString()}.png').create(); await file.writeAsBytes(pngBytes); Utils.hideLoading(context); Share.shareFiles(['${file.path}']); } catch (ex) { Future.delayed(Duration(seconds: 1), () { Utils.hideLoading(context); }); } } }