While i'm using custom code for using audio player to play audio in application background on that time audio play very well play in background for few seconds.after few second music stop automatically and for this custom widget i'm use just_audio: ^0.9.36 and just_audio_background: ^0.0.1-beta.13
Here i attached custom widget code please check for understand in more detail:
// Automatic FlutterFlow imports
import '/backend/backend.dart';
import '/backend/schema/structs/index.dart';
import '/flutter_flow/flutter_flow_theme.dart';
import '/flutter_flow/flutter_flow_util.dart';
import '/custom_code/widgets/index.dart'; // Imports other custom widgets
import '/custom_code/actions/index.dart'; // Imports custom actions
import '/flutter_flow/custom_functions.dart'; // Imports custom functions
import 'package:flutter/material.dart';
// Begin custom widget code
// DO NOT REMOVE OR MODIFY THE CODE ABOVE!
import 'package:flutter/services.dart';
import 'package:just_audio/just_audio.dart';
import 'package:just_audio_background/just_audio_background.dart';
import 'dart:math';
import 'dart:async';
import 'package:fluttertoast/fluttertoast.dart';
import '/flutter_flow/custom_functions.dart' as functions;
AudioPlayer audioPlayer = AudioPlayer();
class MusicPlayer extends StatefulWidget {
MusicPlayer(
{Key? key,
this.width,
this.height,
required this.initialUrl,
required this.musicUrls,
required this.sliderActiveColor,
required this.sliderInactiveColor,
required this.backwardIconPath,
required this.forwardIconPath,
required this.backwardIconColor,
required this.forwardIconColor,
required this.pauseIconPath,
required this.playIconPath,
required this.pauseIconColor,
required this.playIconColor,
required this.loopIconPath,
required this.loopIconColor,
required this.shuffleIconPath,
required this.shuffleIconColor,
required this.playbackDurationTextColor,
required this.previousIconPath,
required this.nextIconPath,
required this.previousIconColor,
required this.nextIconColor,
required this.loopIconPressedPath,
required this.shuffleIconPressedPath,
required this.speakerOnIconPath,
required this.speakerOffIconPath,
required this.speakerOnIconColor,
required this.speakerOffIconColor,
required this.dropdownTextColor,
required this.timerIcon,
required this.currentSongIndex,
required this.onStateListen,
this.isBottom = false,
required this.bookRef,
required this.snackbarBGColor,
required this.snackbarTextColor,
required this.onPlayNext})
: super(key: key);
final double? width;
final double? height;
final String initialUrl;
final List<String> musicUrls;
final Color sliderActiveColor;
final Color sliderInactiveColor;
final Widget backwardIconPath;
final Widget forwardIconPath;
final Color backwardIconColor;
final Color forwardIconColor;
final Widget pauseIconPath;
final Widget playIconPath;
final Color pauseIconColor;
final Color playIconColor;
final Widget loopIconPath;
final Color loopIconColor;
final Widget shuffleIconPath;
final Color shuffleIconColor;
final Color playbackDurationTextColor;
final Widget previousIconPath;
final Widget nextIconPath;
final Color previousIconColor;
final Color nextIconColor;
final Widget loopIconPressedPath;
final Widget shuffleIconPressedPath;
final Widget speakerOnIconPath;
final Widget speakerOffIconPath;
final Color speakerOnIconColor;
final Color speakerOffIconColor;
final Color dropdownTextColor;
final Widget timerIcon;
final Future Function() onPlayNext;
final bool? isBottom;
int currentSongIndex;
final Future Function() onStateListen;
final DocumentReference bookRef;
final Color snackbarBGColor;
final Color snackbarTextColor;
@override
_MusicPlayerState createState() => _MusicPlayerState();
}
class _MusicPlayerState extends State<MusicPlayer>
with SingleTickerProviderStateMixin, AutomaticKeepAliveClientMixin {
@override
bool get wantKeepAlive => true;
bool isPlaying = false;
Duration currentPosition = Duration.zero;
Duration totalDuration = Duration(seconds: 1);
bool isLooping = false;
bool isShuffling = false;
bool isSpeakerOn = true;
String playbackSpeed = 'Normal';
double? minSliderValue;
DateTime? pauseDate;
Map<String, double> speedValues = {
'0.25x': 0.25,
'0.5x': 0.5,
'0.75x': 0.75,
'Normal': 1.0,
'1.25x': 1.25,
'1.5x': 1.5,
'1.75': 1.75,
'2x': 2.0,
};
late AnimationController _animationController;
late Animation<double> _fadeAnimation;
Duration? selectedTimer;
final List<Duration> timerOptions = [
Duration(minutes: 1),
Duration(minutes: 2),
Duration(minutes: 3),
Duration(minutes: 4),
Duration(minutes: 5),
Duration(minutes: 10),
Duration(minutes: 15),
Duration(minutes: 20),
Duration(minutes: 25),
Duration(minutes: 30),
Duration(minutes: 35),
Duration(minutes: 40),
Duration(minutes: 45),
Duration(minutes: 50),
Duration(minutes: 55),
Duration(minutes: 60),
];
Timer? countdownTimer;
Timer? listeningTimer;
void _showSnackBar(BuildContext context, int minutes) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text("$minutes minute timer set.",
style: Theme.of(context).textTheme.bodyMedium),
duration: const Duration(seconds: 3),
),
);
}
@override
void initState() {
super.initState();
FFAppState().bookPlaying = true;
audioPlayer.dispose();
audioPlayer = AudioPlayer();
audioPlayer.setUrl(widget.initialUrl);
List<PlayerIndexStruct> playerIndex = FFAppState()
.bookrecord
.where((e) => e.bookRef == widget.bookRef)
.toList();
if (playerIndex.isNotEmpty) {
currentPosition =
Duration(milliseconds: playerIndex.first.currentPostion.floor());
audioPlayer.seek(currentPosition);
}
if (widget.isBottom ?? false) {
setInitialAudioDuration();
audioPlayer
.seek(Duration(milliseconds: (FFAppState().currentPosition).floor()));
} else {
// FFAppState().currentChapterIndex = 0;
}
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
statusBarColor: Colors.black,
));
audioPlayer.playerStateStream.listen((PlayerState state) {
setState(() {
isPlaying = state.playing;
totalDuration = audioPlayer.duration ?? Duration.zero;
currentPosition = audioPlayer.position ?? Duration.zero;
print("===== Call update widget =========");
});
setSliderMin();
if (state.processingState == ProcessingState.completed) {
playNext();
}
// Update currentURL
if (state.playing) {
FFAppState().currentURL =
widget.musicUrls[FFAppState().currentChapterIndex];
}
});
audioPlayer.positionStream.listen((position) {
setState(() {
currentPosition = position;
FFAppState().currentPosition =
currentPosition.inMilliseconds.toDouble() + 0.1;
FFAppState().totalDuration = totalDuration.inMilliseconds.toDouble();
FFAppState().runningTime =
'${currentPosition.inHours}:${currentPosition.inMinutes}:${(currentPosition.inSeconds % 60).toString().padLeft(2, '0')}';
FFAppState().audioTime =
'${currentPosition.inHours}:${totalDuration.inMinutes}:${(totalDuration.inSeconds % 60).toString().padLeft(2, '0')}';
FFAppState()
.bookrecord
.removeWhere((e) => e.bookRef == widget.bookRef!);
FFAppState().bookrecord.add(PlayerIndexStruct(
bookRef: widget.bookRef!,
currentPostion: currentPosition.inMilliseconds.toDouble() + 0.1,
chapterIndex: FFAppState().currentChapterIndex));
widget.onStateListen();
FFAppState().update;
});
// Check if the selected timer is complete
if (pauseDate != null && pauseDate! <= DateTime.now()) {
audioPlayer.pause();
setState(() {
pauseDate = null;
});
FFAppState().bookPlaying = false;
}
// if (selectedTimer != null && currentPosition >= selectedTimer!) {
// audioPlayer.pause();
// setState(() {
// selectedTimer = null;
// });
// }
});
_animationController = AnimationController(
vsync: this,
duration: Duration(seconds: 1),
);
_fadeAnimation = CurvedAnimation(
parent: _animationController,
curve: Curves.easeIn,
);
if (!isPlaying) {
audioPlayer.play();
}
listeningTimer = Timer.periodic(Duration(seconds: 1), (Timer timer) {
FFAppState().seconds++;
});
Timer.periodic(Duration(seconds: 2), (Timer timer) {
if (FFAppState().playSelected) {
FFAppState().playSelected = false;
FFAppState().update;
audioPlayer.setUrl(widget.musicUrls[FFAppState().currentChapterIndex]);
audioPlayer.play();
audioPlayer.playerStateStream.listen((PlayerState state) {
setState(() {
isPlaying = state.playing;
totalDuration = audioPlayer.duration ?? Duration.zero;
currentPosition = audioPlayer.position ?? Duration.zero;
});
setSliderMin();
if (state.processingState == ProcessingState.completed) {
playNext();
}
// Update currentURL
if (state.playing) {
FFAppState().currentURL =
widget.musicUrls[FFAppState().currentChapterIndex];
}
});
}
});
_animationController.forward();
}
void seekTo(Duration position) {
audioPlayer.seek(position);
}
void updatePosition(double value) {
final newPosition = Duration(milliseconds: value.toInt());
seekTo(newPosition); // Complete the method call by passing newPosition
}
void playPause() {
if (isPlaying) {
audioPlayer.pause();
FFAppState().bookPlaying = false;
} else {
audioPlayer.play();
}
}
void skip(Duration duration) {
final newPosition = currentPosition + duration;
if (newPosition < Duration.zero) {
seekTo(Duration.zero);
} else if (newPosition > totalDuration) {
seekTo(totalDuration);
} else {
seekTo(newPosition);
}
}
void playPrevious() {
if (FFAppState().currentChapterIndex > 0) {
setState(() {
FFAppState().currentChapterIndex--;
FFAppState()
.bookrecord
.removeWhere((e) => e.bookRef == widget.bookRef!);
FFAppState().bookrecord.add(PlayerIndexStruct(
bookRef: widget.bookRef!,
chapterIndex: FFAppState().currentChapterIndex));
});
audioPlayer.setUrl(widget.musicUrls[FFAppState().currentChapterIndex]);
audioPlayer.play();
widget.onPlayNext();
FFAppState().update;
} else {
// FFAppState().currentChapterIndex = widget.musicUrls.length - 1;
}
}
void playNext() {
if (isShuffling) {
FFAppState().currentChapterIndex = _getRandomIndex();
} else {
if (FFAppState().currentChapterIndex < widget.musicUrls.length - 1) {
setState(() {
FFAppState().currentChapterIndex++;
});
FFAppState()
.bookrecord
.removeWhere((e) => e.bookRef == widget.bookRef!);
FFAppState().bookrecord.add(PlayerIndexStruct(
bookRef: widget.bookRef!,
chapterIndex: FFAppState().currentChapterIndex));
audioPlayer.setUrl(widget.musicUrls[FFAppState().currentChapterIndex]);
audioPlayer.play();
widget.onPlayNext();
FFAppState().update;
} else {
// FFAppState().currentChapterIndex = 0;
}
}
}
void toggleLooping() {
setState(() {
isLooping = !isLooping;
audioPlayer.setLoopMode(isLooping ? LoopMode.one : LoopMode.off);
});
}
void toggleShuffle() {
setState(() {
isShuffling = !isShuffling;
});
}
void toggleSpeaker() {
setState(() {
isSpeakerOn = !isSpeakerOn;
if (isSpeakerOn) {
audioPlayer.setVolume(1.0);
} else {
audioPlayer.setVolume(0.0);
}
});
}
void setPlaybackSpeed(String speed) {
double playbackSpeed = speedValues[speed]!;
audioPlayer.setSpeed(playbackSpeed);
}
int _getRandomIndex() {
final random = Random();
int randomIndex = FFAppState().currentChapterIndex;
while (randomIndex == FFAppState().currentChapterIndex) {
randomIndex = random.nextInt(widget.musicUrls.length);
}
return randomIndex;
}
void setTimer(Duration duration) {
setState(() {
selectedTimer = duration;
if (duration == Duration.zero) {
audioPlayer.pause(); // Pause the player if the selected timer is 0
}
Navigator.pop(context);
});
}
void setSliderMin() {
setState(() {
minSliderValue = currentPosition.inMilliseconds.toDouble();
});
}
void setInitialAudioDuration() {
setState(() {
currentPosition =
Duration(milliseconds: (FFAppState().currentPosition).floor());
});
}
@override
Widget build(BuildContext context) {
super.build(context);
return FadeTransition(
opacity: _fadeAnimation,
child: Container(
width: widget.width,
height: widget.height,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// // IconButton(
// // onPressed: toggleSpeaker,
// // icon: isSpeakerOn
// // ? widget.speakerOnIconPath
// // : widget.speakerOffIconPath,
// // color: isSpeakerOn
// // ? widget.speakerOnIconColor
// // : widget.speakerOffIconColor,
// // ),
// // DropdownButton<String>(
// // value: playbackSpeed,
// // onChanged: (String? speed) {
// // setState(() {
// // playbackSpeed = speed!;
// // setPlaybackSpeed(speed);
// // });
// // },
// // items: speedValues.keys
// // .map<DropdownMenuItem<String>>((String value) {
// // return DropdownMenuItem<String>(
// // value: value,
// // child: Text(
// // value,
// // style: TextStyle(
// // color: widget.dropdownTextColor,
// // ),
// // ),
// // );
// // }).toList(),
// // ),
// ],
// ),
SizedBox(height: 8),
Slider(
value: min(currentPosition.inMilliseconds.toDouble() + 0.1,
totalDuration.inMilliseconds.toDouble()),
min: 0.0,
max: totalDuration.inMilliseconds.toDouble(),
onChanged: (double value) {
updatePosition(value);
},
activeColor: widget.sliderActiveColor,
inactiveColor: widget.sliderInactiveColor,
),
SizedBox(height: 8),
Padding(
padding: EdgeInsets.symmetric(horizontal: 12),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'${currentPosition.inMinutes > 59 ? currentPosition.inMinutes ~/ 60 : currentPosition.inHours}:${currentPosition.inMinutes > 59 ? currentPosition.inMinutes % 60 : currentPosition.inMinutes}:${(currentPosition.inSeconds % 60).toString().padLeft(2, '0')}',
style: TextStyle(color: widget.playbackDurationTextColor),
),
Text(
'${totalDuration.inMinutes > 59 ? totalDuration.inMinutes ~/ 60 : totalDuration.inHours}:${totalDuration.inMinutes > 59 ? totalDuration.inMinutes % 60 : totalDuration.inMinutes}:${(totalDuration.inSeconds % 60).toString().padLeft(2, '0')}',
style: TextStyle(color: widget.playbackDurationTextColor),
),
],
),
),
SizedBox(height: 8),
Padding(
padding: EdgeInsets.symmetric(horizontal: 6),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
IconButton(
onPressed: playPrevious,
icon: widget.previousIconPath,
iconSize: 35,
color: widget.previousIconColor,
),
IconButton(
onPressed: () {
skip(Duration(seconds: -30));
},
icon: widget.backwardIconPath,
iconSize: 45,
color: widget.backwardIconColor,
),
IconButton(
onPressed: playPause,
iconSize: 60,
icon:
isPlaying ? widget.pauseIconPath : widget.playIconPath,
color: isPlaying
? widget.pauseIconColor
: widget.playIconColor,
),
IconButton(
onPressed: () => skip(Duration(seconds: 30)),
icon: widget.forwardIconPath,
iconSize: 45,
color: widget.forwardIconColor,
),
IconButton(
onPressed: playNext,
iconSize: 35,
icon: widget.nextIconPath,
color: widget.nextIconColor,
),
],
),
),
SizedBox(height: 12),
Padding(
padding: EdgeInsets.symmetric(horizontal: 12),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// GestureDetector(
// onTap: toggleLooping,
// child: isLooping
// ? widget.loopIconPressedPath
// : widget.loopIconPath,
// ),
// GestureDetector(
// onTap: toggleShuffle,
// child: isShuffling
// ? widget.shuffleIconPressedPath
// : widget.shuffleIconPath,
// ),
Column(children: [
DropdownButton<String>(
value: playbackSpeed,
onChanged: (String? speed) {
setState(() {
playbackSpeed = speed!;
setPlaybackSpeed(speed);
});
},
items: speedValues.keys
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(
value,
style: TextStyle(
color: widget.dropdownTextColor, fontSize: 12),
),
);
}).toList(),
),
Text("Speed",
style: TextStyle(
color: widget.dropdownTextColor, fontSize: 12))
]),
GestureDetector(
onTap: () {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('Sleep Timer'),
content: Container(
height: 200,
width: 200,
child: ListView.builder(
itemCount: timerOptions.length,
itemBuilder: (BuildContext context, int index) {
final duration = timerOptions[index];
final minutes = duration.inMinutes;
bool isSelected = duration == selectedTimer;
return ListTile(
title: Text(
'$minutes minutes',
style: TextStyle(
color: isSelected
? widget
.dropdownTextColor // Customize the selected option's text color
: null,
),
),
onTap: () {
DateTime now = DateTime.now();
DateTime newTime =
now.add(Duration(minutes: minutes));
setState(() {
pauseDate = newTime;
});
// final snackBar = SnackBar(
// content: Text(
// ""));
// ScaffoldMessenger.of(context)
// .showSnackBar(snackBar);
Fluttertoast.showToast(
msg: "$minutes minutes timer set.",
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.BOTTOM,
timeInSecForIosWeb: 3,
backgroundColor:
widget.snackbarBGColor,
textColor: widget.snackbarTextColor,
fontSize: 16.0);
// _showSnackBar(context, minutes);
Navigator.pop(context);
// setTimer(
// duration); // Set the selected timer
},
);
},
),
),
actions: [
TextButton(
onPressed: () {
setState(() {
selectedTimer = null;
Navigator.pop(context);
});
},
child: Text('Cancel'),
),
],
);
},
);
},
child: Column(
children: [
widget.timerIcon,
Text("Timer",
style: TextStyle(
color: widget.dropdownTextColor, fontSize: 12))
],
),
),
],
),
),
],
),
),
);
}
}