I have a list containing music on each item. If I close the screen im changing the index
of the currently located BottomNavigationPage
and calling the stop function for stopping the audio, but exactly this doesnt work sometimes. If the song is in loading process or user is really fast, song will continue beeing played on different pages.
Im using https://pub.dev/packages/audioplayers plugin.
This is my minified code example full working demo: EDIT::
So i followed up the hint of @Ringil providing a full working example with the actual issue im facing in here. This is my code:
import 'package:audioplayers/audioplayers.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int currentIndex =0;
@override
Widget build(BuildContext context) {
// Page selector for tab list
void _selectPage(int index) {
print('page index: $index');
setState(() {
currentIndex = index;
});
}
// Routes list for tab navigation Android
final List<Widget> _pages = [
ScreenA(),
ScreenB(func: _selectPage),
];
return Scaffold(
appBar: AppBar(),
body: _pages[currentIndex],
bottomNavigationBar: SafeArea(
child: BottomNavigationBar(
onTap: _selectPage,
iconSize: 22,
currentIndex: currentIndex,
type: BottomNavigationBarType.fixed,
items: [
BottomNavigationBarItem(
backgroundColor: Theme.of(context).primaryColor,
icon: Icon(Icons.description),
label: 'ScreenA',
),
BottomNavigationBarItem(
backgroundColor: Theme.of(context).primaryColor,
icon: Icon(Icons.ac_unit_outlined),
label: 'ScreenB'),
],
),
),
);
}
}
class ScreenA extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
child: Text('HOME'),
);
}
}
class ScreenB extends StatefulWidget {
Function func;
ScreenB({Key key, @required this.func}) : super(key: key);
@override
_ScreenBState createState() => _ScreenBState();
}
class _ScreenBState extends State<ScreenB> {
var audioPlayer = AudioPlayer(playerId: 'player');
var audioIndex = 0;
var audioFiles = [
"https://docs.google.com/uc?export=open&id=1SaJWqfQuHnFtL7uqrzfYG31hzOnqDM3r",
"https://docs.google.com/uc?export=open&id=1FZkFMjQyWguAl0RMAsYDEZ07c_Qf7gjz",
"https://docs.google.com/uc?export=open&id=1GqrwQ3eRuiil0p-Na_R1tMAvggp9YrbH",
];
var _controller = PageController();
@override
void initState() {
super.initState();
// starting player
startPlayer();
}
startPlayer() {
play();
}
// Player play class set globalindex
Future<void> play() async {
int result = await audioPlayer.play(audioFiles[audioIndex]);
if (result == 1) {
// success
}
}
// Stop song instance of player
Future<void> stop() async {
int result = await audioPlayer.stop();
if (result == 1) {
// success
}
}
// disposing listener if not needed or users navigates away from
@override
void dispose() {
super.dispose();
audioPlayer.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: [
IconButton(
icon: Icon(Icons.access_alarm_sharp),
onPressed: () async {
await stop();
widget.func(0);
},
),
],
),
body: PageView.custom(
dragStartBehavior: DragStartBehavior.start,
controller: _controller,
physics: NeverScrollableScrollPhysics(),
scrollDirection: Axis.horizontal,
childrenDelegate: SliverChildBuilderDelegate((ctx, pageIndex) =>
GestureDetector(
onPanUpdate: (details) async {
if (details.delta.dx < 0) {
_controller.nextPage(
duration: Duration(milliseconds: 200),
curve: Curves.easeInOut);
await stop();
setState(() {
audioIndex = pageIndex;
play();
});
}
},
child: Center(
child: Container(
width: 200,
height: 200,
color: Colors.red,
child: Text(audioFiles[audioIndex])))))),
);
}
}
And a short video showing the issue. If we go to the carousel screen, navigate threw songs and closing the screen before they have been loaded, they will be played on a different screen which I dont want. I also dont want to somehow "block" the user sliding threw the carousel until the song is loaded. Video: https://streamable.com/e/ycxwob
Basically I shrinked my code up to the minimum possible. I took the code of Ringil because in his demo everything works perfectly fine. Then I added more and more code from me, until I noticed the error again. Now here we are. Basically there is a difference in the example audio files from hin and mine. My files arent big from filesize aspect but they seem to be longer, which gives us the delay Im expecting all the time. It seems like if the song isnt loaded before closing or changing the index, the error occurs.
To Stop: audioPlayer. stop(); You will have to get the instance of AudioPlayer to stop the file, simply use await on play() to get the instance and using this, you can call stop() .
Playify is a Flutter plugin for play/pause/seek songs, fetching music metadata, and browsing music library. Playify was built using iOS's Media Player Framework to fetch and play music from iOS's Music Library. Currently supports only iOS, but Android support is being developed. PR's are welcomed. Checkout the documentation.
AudioManager provides us start () method to play the music. It takes a URL, title, description, cover, and auto. To play the music file using assets file you just need to change the song URL to assets file path. To fetch the music files form the external storage we will use a FutureBuilder as FlutterAudioQuery returns a future .
Fetching music files from our external storage. We will use flutter_audio_query to fetch the music form our external storage (eg. mobile phone, memory card, etc). A Flutter plugin, Android only at this moment, that allows you query for audio metadata info about artists, albums…
Sign in to your account When playing a video on Android, 3rd party music players will continue to play their content along with the audio output from the video. This results in garbled audio for the end-user.
add: "with WidgetsBindingObserver"
class Screen extends StatefulWidget with WidgetsBindingObserver
and you'll get access to the App Lifecycle State:
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.paused) {
player.pause();
}
}
It sounds like you're trying to build a carousel slider. I would suggest instead of trying to build it you use something like the carousel_slider package. You should be able to just call play
in the callback for onPageChanged
.
However, getting back to your specific issue, I think the issue is that you're likely getting the page index and the globalSongIndex
out of sync.
It seems like you have something like this globally:
var audioIndex = 0;
var audioFiles = [
"1.mp3",
"2.mp3",
"3.mp3"
];
with a play function like this:
Future<void> play() async {
int result = await audioPlayer.play(audioFiles[audioIndex]);
}
Then to ensure your gesture and your pageView on your pageView controller are in sync you need to make sure when you call the nextPage
function on the PageController
you also ensure your state variable is also updated to the nextPage
value. I'm not exactly sure on how the specifics of Provider.of<Songs>
works, but you likely need to force it to a specific value.
SliverChildBuilderDelegate((ctx, pageIndex) => GestureDetector(
onPanUpdate: (details) async {
if (details.delta.dx < 0) {
_controller.nextPage(
duration: Duration(milliseconds: 200),
curve: Curves.easeInOut);
await stop();
setState(() {
audioIndex = pageIndex;
play();
});
}
},
child: Center(child: Text(audioFiles[audioIndex])))
Full working example:
import 'package:audioplayers/audioplayers.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
var audioPlayer = AudioPlayer(playerId: 'player');
var audioIndex = 0;
var audioFiles = [
"https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3",
"https://www.soundhelix.com/examples/mp3/SoundHelix-Song-2.mp3",
"https://www.soundhelix.com/examples/mp3/SoundHelix-Song-3.mp3"
];
var _controller = PageController();
@override
void initState() {
super.initState();
// starting player
startPlayer();
}
startPlayer() {
play();
}
// Player play class set globalindex
Future<void> play() async {
int result = await audioPlayer.play(audioFiles[audioIndex]);
if (result == 1) {
// success
}
}
// Stop song instance of player
Future<void> stop() async {
int result = await audioPlayer.stop();
if (result == 1) {
// success
}
}
// disposing listener if not needed or users navigates away from
@override
void dispose() {
super.dispose();
audioPlayer.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: PageView.custom(
dragStartBehavior: DragStartBehavior.start,
controller: _controller,
physics: NeverScrollableScrollPhysics(),
scrollDirection: Axis.horizontal,
childrenDelegate:
SliverChildBuilderDelegate((ctx, pageIndex) => GestureDetector(
onPanUpdate: (details) async {
if (details.delta.dx < 0) {
_controller.nextPage(
duration: Duration(milliseconds: 200),
curve: Curves.easeInOut);
await stop();
setState(() {
audioIndex = pageIndex;
play();
});
}
},
child: Center(child: Text(audioFiles[audioIndex]))))));
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With