I'm trying to make some kind of an mp3 player app and i'm using audioplayers package. And it's been working fine on Android, but on IOS the onDurationChanged doesn't seem to be getting called.
And since i'm also showing a slider, it gives an Error on IOS because the max value returns null
Here's my code
class AudioProvider extends ChangeNotifier {
AudioProvider() {
initAudio();
}
AudioPlayer _audioPlayer = AudioPlayer();
Duration totalDuration;
Duration position;
String audioState;
initAudio() {
_audioPlayer.onDurationChanged.listen((updatedDuration) {
totalDuration = updatedDuration; // This doesn't work on IOS, totalDuration == null
notifyListeners();
});
_audioPlayer.onAudioPositionChanged.listen((updatedPosition) {
position = updatedPosition;
notifyListeners();
});
_audioPlayer.onPlayerStateChanged.listen((playerState) {
if (playerState == AudioPlayerState.STOPPED) audioState = "Stopped";
if (playerState == AudioPlayerState.PLAYING) audioState = "Playing";
if (playerState == AudioPlayerState.PAUSED) audioState = "Paused";
notifyListeners();
});
}
playPauseAudio(String url, bool alreadyPlaying) async {
if (!alreadyPlaying) {
position = null;
totalDuration = null;
await _audioPlayer.play(url);
notifyListeners();
}
if (audioState == 'Playing') {
await _audioPlayer.pause();
} else {
await _audioPlayer.resume();
}
notifyListeners();
}
void stop() {
_audioPlayer.stop();
}
void seekToSec(Duration durationToSeek) {
_audioPlayer.seek(durationToSeek);
notifyListeners();
}
Related
I have a StateNotifierProvider that calls an async function which loads some images from the internal storage and adds them to the AsyncValue data:
//Provider declaration
final paginationImagesProvider = StateNotifierProvider.autoDispose<PaginationNotifier, AsyncValue<List<Uint8List?>>>((ref) {
return PaginationNotifier(folderId: ref.watch(localStorageSelectedFolderProvider), itemsPerBatch: 100, ref: ref);
});
//Actual class with AsyncValue as State
class PaginationNotifier extends StateNotifier<AsyncValue<List<Uint8List?>>> {
final int itemsPerBatch;
final String folderId;
final Ref ref;
int _numberOfItemsInFolder = 0;
bool _alreadyFetching = false;
bool _hasMoreItems = true;
PaginationNotifier({required this.itemsPerBatch, required this.folderId, required this.ref}) : super(const AsyncValue.loading()) {
log("PaginationNotifier created with folderId: $folderId, itemsPerBatch: $itemsPerBatch");
init();
}
final List<Uint8List?> _items = [];
void init() {
if (_items.isEmpty) {
log("fetchingFirstBatch");
_fetchFirstBatch();
}
}
Future<List<Uint8List?>> _fetchNextItems() async {
List<AssetEntity> images = (await (await PhotoManager.getAssetPathList())
.firstWhere((element) => element.id == folderId)
.getAssetListRange(start: _items.length, end: _items.length + itemsPerBatch));
List<Uint8List?> newItems = [];
for (AssetEntity image in images) {
newItems.add(await image.thumbnailData);
}
return newItems;
}
void _updateData(List<Uint8List?> result) {
if (result.isEmpty) {
state = AsyncValue.data(_items);
} else {
state = AsyncValue.data(_items..addAll(result));
}
_hasMoreItems = _numberOfItemsInFolder > _items.length;
}
Future<void> _fetchFirstBatch() async {
try {
_numberOfItemsInFolder = await (await PhotoManager.getAssetPathList()).firstWhere((element) => element.id == folderId).assetCountAsync;
state = const AsyncValue.loading();
final List<Uint8List?> result = await _fetchNextItems();
_updateData(result);
} catch (e, stk) {
state = AsyncValue.error(e, stk);
}
}
Future<void> fetchNextBatch() async {
if (_alreadyFetching || !_hasMoreItems) return;
_alreadyFetching = true;
log("data updated");
state = AsyncValue.data(_items);
try {
final result = await _fetchNextItems();
_updateData(result);
} catch (e, stk) {
state = AsyncValue.error(e, stk);
log("error catched");
}
_alreadyFetching = false;
}
}
Then I use a scroll controller attached to a CustomScrollView in order to call fetchNextBatch() when the scroll position changes:
#override
void initState() {
if (!controller.hasListeners && !controller.hasClients) {
log("listener added");
controller.addListener(() {
double maxScroll = controller.position.maxScrollExtent;
double position = controller.position.pixels;
if ((position > maxScroll * 0.2 || position == 0) && ref.read(paginationImagesProvider.notifier).mounted) {
ref.read(paginationImagesProvider.notifier).fetchNextBatch();
}
});
}
super.initState();
}
The problem is that when the StateNotifierProvider is fetching more data in the async function fetchNextBatch() and I go back on the navigator (like navigator.pop()), Flutter gives me an error:
[ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: Bad state: Tried to use PaginationNotifier after dispose was called.
Consider checking mounted.
I think that the async function responsible of loading data completes after I've popped the page from the Stack (which triggers a Provider dispose).
I'm probably missing something and I still haven't found a fix for this error, any help is appreciated.
I am building a videoplayer application in Flutter that plays multiple videos one by one by fetching from the database
After completion of one video we need to transfer videocontroller to another video so first I dispose video controller and initialize with another video
It works fine for 4,5 video sequence and then throw a error like this
throw FlutterError(
'A $runtimeType was used after being disposed.\n'
'Once you have called dispose() on a $runtimeType, it can no longer be used.',
);
Part of my code throwing this error is
void playVideo() async {
if (DateTime.now().isAfter(videoData[index].start_from) &&
DateTime.now().isBefore(videoData[index].end_on)) {
bool played_count = await CountQuery().whenComplete(() {
});
file = File(videoData[index].file_link);
if (file.existsSync() && !played_count) {
isEnd = true;
controller = VideoPlayerController.file(file)
..initialize().then((value) {
setState(() {
controller.play();
});
controller.addListener(() {
// checking the duration and position every time
// Video Completed//
if (controller.value.duration == controller.value.position &&
isEnd) {
setState(() {
isEnd = false;
});
CreateLog("video");
controller.dispose();
PlayNext();
}
});
})
..setLooping(false);
} else {
PlayNext();
}
} else {
PlayNext();
}
}
void PlayNext() {
setState(() {
index++;
if (videoData.length > index) {
// this.asset = videoData[index].file_link;
this.file = File(videoData[index].file_link);
playVideo();
} else {
index = 0;
// this.asset = videoData[index].file_link;
this.file = File(videoData[index].file_link);
playVideo();
}
});
}
Note: I am using video_player: ^2.4.2
I solve this problem by adding controller = null after each dispose
I'm creating a chat app with sending voice message. now I'm getting this error in my code. appreciate your help on this. I HAVE INSERTED FOLLOWING CODE ANS THEN ERROR APPEARS. Cant find a exact file error occurring.
Null check operator used on a null value
class SoundRecorder {
FlutterSoundRecorder? _audioRecorder;
bool _isRecorderInitialised = false;
bool get isRecording => _audioRecorder!.isRecording;
Future init() async {
_audioRecorder = FlutterSoundRecorder();
await _audioRecorder?.openAudioSession(); //start recording
//asking permisson
final status = await Permission.microphone.request();
if (status != PermissionStatus.granted) {
throw RecordingPermissionException("Microphone permission");
}
await _audioRecorder!.openAudioSession();
_isRecorderInitialised = true;
}
void dispose() {
if (!_isRecorderInitialised) return;
_audioRecorder!.closeAudioSession();
_audioRecorder = null;
_isRecorderInitialised = false;
}
Future _record() async {
if (!_isRecorderInitialised) return;
await _audioRecorder!.startRecorder(toFile: pathToSaveAudio);
}
Future _stop() async {
if (!_isRecorderInitialised) return;
await _audioRecorder!.stopRecorder();
}
Future toggleRecording() async {
if (_audioRecorder!.isStopped) {
await _record();
} else {
await _stop();
}
}
}
Looks like at some point you use the ! operator to assert that _audioRecorder isn't null but it actually is. From the stack, I think this would be from the isRecording getter.
A simple fix to this would be to make the getter bool get isRecording => _audioRecorder?.isRecording ?? false, since if _audioRecorder is null, then you can't be recording, right?
I am working on a basic audio streaming app. Everything is working fine Play/Pause , Skip the song... The only problem that i have is to go forward in a Song.
My goal is to have a button to skip 10seconds of the music.
Here is the class AudioManager that i am using :
class AudioManager {
late AudioPlayer _audioPlayer;
String lastBeatType = "";
Data data = Data(id: "",channelTitle: "",title: "",link:"");
AudioManager({required this.lastBeatType});
void dispose() {
_audioPlayer.dispose();
}
void initAudio() async {
_audioPlayer = AudioPlayer();
}
void play() {
_audioPlayer.play();
}
void pause() {
_audioPlayer.pause();
}
void goForward() async{
await _audioPlayer.seek(const Duration(seconds: _audioPlayer.position.inSeconds + 10) );
}
void goBackward() {
_audioPlayer.seek(Duration(seconds: _audioPlayer.position.inSeconds - 10));
}
//Function getting the buffers of the songs
getBeat(String type) async {
print("Entering Get Beat");
lastBeatType = type;
AudioManager _audioManager = AudioManager(lastBeatType: type);
final response =
await http.get(Uri.parse("http://localhost:5000/beat?type=$type"));
print("Response has been getted");
if (response.statusCode == 200) {
final songdata = jsonDecode(response.body);
_audioPlayer.setUrl("http://localhost:5000/audio?videoId=${songdata["id"]}");
}
return response.bodyBytes;
}
The function goForward/goBackward are the one that i am using.
But when i press the button here is the error i have
Client returned a buffer it does not own according to our record: 0
Any help would be very appreciate !
I have uploaded some mp3 files to my google drive. And now I want to play those mp3 files in my flutter app using audioplayers package.
https://drive.google.com/file/d/1v8RBvEOpsEDlD_o7OA2TKgxysF-O5jUW/view?usp=sharing. This is my public shareable link of my mp3 on google drive.
void getAudio() async {
var url =
"https://drive.google.com/file/d/1v8RBvEOpsEDlD_o7OA2TKgxysF-O5jUW/view?usp=sharing";
if (isPlaying) {
var res = await audioPlayer.pause();
if (res == 1) {
print("isPlaying $res");
setState(() {
isPlaying = false;
});
}
} else {
var res = await audioPlayer.play(url, isLocal: false);
print("!isPlaying $res");
if (res == 1) {
setState(() {
isPlaying = true;
});
}
}
audioPlayer.onDurationChanged.listen((Duration d) {
setState(() {
duration = d;
});
});
audioPlayer.onAudioPositionChanged.listen((Duration d) {
setState(() {
position = d;
});
});
}
you have to try direct url for your file like this
https://drive.google.com/uc?export=download&confirm=no_antivirus&id=1v8RBvEOpsEDlD_o7OA2TKgxysF-O5jUW