Seek method not working Flutter Just Audio - flutter

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 !

Related

Unhandled Exception: Bad state: Tried to use PaginationNotifier after `dispose` was called

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.

Flutter recording feature working on emulator but not on actual device

I am developing an app that can record and play sound. Everything works fine on my emulator. But when I try to run the app on my device, it always gives me this error:
java.lang.NullPointerException: Attempt to invoke virtual method 'long android.os.storage.StorageVolume.getMaxFileSize()' on a null object reference
The way I implement the recording feature is to record the audio to a temporary file, and playback from it. This is the corresponding code, I am using flutter sound by the way:
String pathToSaveAudio = '';
class SoundRecorder {
FlutterSoundRecorder? _audioRecorder;
bool _isRecordingInitialised = false;
bool get isRecording => _audioRecorder!.isRecording;
// getters
bool getInitState() {
return _isRecordingInitialised;
}
FlutterSoundRecorder? getRecorder() {
return _audioRecorder;
}
/// init recorder
Future init() async {
_audioRecorder = FlutterSoundRecorder();
final status = await Permission.microphone.request();
if (status != PermissionStatus.granted) {
throw RecordingPermissionException('Microphone permission denied');
}
await _audioRecorder!.openRecorder();
_isRecordingInitialised = true;
var tempDir = await getTemporaryDirectory();
pathToSaveAudio = '${tempDir.path}/audio.mp4';
}
/// dipose recorder
void dispose() {
_audioRecorder!.closeRecorder();
_audioRecorder = null;
_isRecordingInitialised = false;
}
/// start record
Future _record() async {
assert(_isRecordingInitialised);
await _audioRecorder!
.startRecorder(toFile: pathToSaveAudio, codec: Codec.aacMP4);
}
/// stop record
Future _stop() async {
if (!_isRecordingInitialised) return;
await _audioRecorder!.stopRecorder();
}
I think what I'm doing is record the sound and put it in the file in that temp directory. But appearently the app want to acces the file before I even start recording. At this point, I don't know what to do, please help me.

How to update the data constantly after using API in Getx Flutter?

I have a getx controller and a method to update data in the database, I just wonder that how can I refresh of update the UI constantly after fetching the API? Here is my controller
class AdditionalContactController extends GetxController {
var additionalContactList = <AdditionalContact>[].obs;
var isLoading = true.obs;
UserController _userController = Get.find();
#override
void onInit() {
super.onInit();
_userController.getMail().then((value) async {
await _userController.getUser(value);
getAdditionalContacts(_userController.user.value.id);
});
}
//Update contact
Future<bool> updateContact({...}) async {
var response = await http.post(
Uri.parse(
"https://..."),
body: {
...
});
var jsonData = jsonDecode(response.body);
if (jsonData == "failed") {
return false;
}
return true;
}
}
you can use the ever worker to call a method that executes every time an Rx have assigned with a new value:
class AdditionalContactController extends GetxController {
var additionalContactList = <AdditionalContact>[].obs;
#override
void onInit() {
super.onInit();
ever(additionalContactList, methodToExecute)
});
}
methodToExecute(list) {
/* Code that will execute every time the additionalContactList changes */
}
now everytime additionalContactList is changed like as example if we assign a new List to it:
additionalContactList.value = [];
Then the methodToExecute() will be executed automatically, and will do every single time.

Flutter | audioplayers onDurationChanged Method isn't working on IOS

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();
}

Flutter Metronome App lagging and updating values as they change

As a first app in flutter, I want to build a metronome app. The UI is already built, but I still encounter the following problems with the actual metronome functionality:
sometimes, the metronome lags a bit, just enough, so you notice it. Is there a way in flutter to achieve a 100% precision of the metronome?
not changing subdivision while playing (you have to stop and start the metronome). How can the values "tempo" and "subdivision" be automatically applied to the metronome subscription, if they change? I know that Flutter provides tools like Listenable, Stream, InheritedWidget, etc. but I haven’t figured out a way how you can implement these in the existing code.
Acreenshot of the UI:
Here is the code (it's not entirely written by me -> credits):
import 'dart:io' show File;
import 'dart:async';
import 'package:flutter/cupertino.dart';
import 'package:quiver/async.dart';
import 'package:audioplayers/audioplayers.dart' show AudioPlayer;
import 'package:flutter/services.dart' show ByteData, rootBundle;
import 'package:path_provider/path_provider.dart' show getTemporaryDirectory;
//credits: "Andi Qu", https://stackoverflow.com/questions/51048402/flutter-audioplayers-or-metronome-lagging
ValueNotifier<int> tempo = ValueNotifier(100);
int subdivision = 1;
bool isPlaying = false;
int soundIndex = 1;
File _soundFile;
StreamSubscription<DateTime> _subscription;
Future<ByteData> _loadSound() async {
return await rootBundle.load('assets/sounds/sound_$soundIndex.wav');
}
void _writeSound() async {
_soundFile = File(
'${(await getTemporaryDirectory()).path}/sounds/sound_$soundIndex.wav');
await _soundFile.writeAsBytes((await _loadSound()).buffer.asUint8List());
print("_writeSound executed");
}
void _playLocal() async {
final AudioPlayer _audioPlayer = AudioPlayer();
AudioPlayer.logEnabled = false;
await _audioPlayer.play(_soundFile.path, isLocal: true);
}
/// The actual method that plays the metronome
void playpause() {
print("playpause triggered");
if (_soundFile == null) {
print("_soundFile = null ---> Soundfile written");
_writeSound();
}
if (isPlaying) {
_subscription.cancel();
isPlaying = false;
print("metronome stopped");
} else {
_subscription = Metronome.periodic(new Duration(
milliseconds: (60000 / (tempo.value * subdivision)).floor()))
.listen((d) => _playLocal());
isPlaying = true;
print("metronome started");
}
}
void increasetempo(int tempochange) {
tempo.value = tempo.value + tempochange;
if (isPlaying) {
_subscription.cancel();
print("_subscription canceled");
_subscription = Metronome.periodic(new Duration(
milliseconds: (60000 / (tempo.value * subdivision)).floor()))
.listen((d) => _playLocal());
}
print("tempo changed to ${tempo.value}");
}
void decreasetempo(int tempochange) {
tempo.value = tempo.value - tempochange;
if (isPlaying) {
_subscription.cancel();
print("_subscription canceled");
_subscription = Metronome.periodic(new Duration(
milliseconds: (60000 / (tempo.value * subdivision)).floor()))
.listen((d) => _playLocal());
}
print("tempo changed to ${tempo.value}");
}
Try to use a library called flutter_sequencer, it helped me to create Metronome without lagging, while any other solution and library didn't work.
https://github.com/mikeperri/flutter_sequencer
https://pub.dev/packages/flutter_sequencer