Please I am very new to flutter. I am working on a project and i got stuck in the implementation. I was wondering if you can help out. I am creating an audiobook. It converts pdf to audiofiles. I created a list of the books on a screen and if you tap on a book, it should list out the audios associated with it. Now the problem is that I split the audios into different parts as seen below:
import 'package:audioplayers/audioplayers.dart';
import 'package:flutter/material.dart';
import 'audio.dart';
class Audios with ChangeNotifier {
final List<Audio> _audios = [
Audio(
audioId: 1,
bookId: 1,
userId: 1,
audioFile: [
"https://myaudiobookapp.s3.eu-central-1.amazonaws.com/the_frog_prince_part1.mp3",
"https://myaudiobookapp.s3.eu-central-1.amazonaws.com/the_frog_prince_part2.mp3",
"https://myaudiobookapp.s3.eu-central-1.amazonaws.com/the_frog_prince_part3.mp3"
],
audioName: "the_frog_prince",
bookImage:
"https://myaudiobookapp.s3.amazonaws.com/1_The-Frog-Prince-Landscape-Book-CKF-FKB.pdf.png"),
Audio(
audioId: 2,
bookId: 2,
userId: 1,
audioFile: [
"https://myaudiobookapp.s3.amazonaws.com/sara_menker_part1.mp3"
],
audioName: "sara_menker",
bookImage:
"https://myaudiobookapp.s3.amazonaws.com/Data_Analysis_Plan_of_Sara_Menker.pdf.png",
),
];
// current song playing index
int? _currentAudioIndex;
/*
A U D I O P L A Y E R
*/
// audio player
final AudioPlayer _audioPlayer = AudioPlayer();
// durations
Duration _currentDuration = Duration.zero;
Duration _totalDuration = Duration.zero;
// constructor
Audios() {
listenToDuration();
}
// initially not playing
bool _isPlaying = false;
// play the audio
void playAudio(int index) async {
// final audIndex = _audios.indexWhere((aud) => aud.audioId == index + 1);
int i = 0;
while (i < _audios[index].audioFile.length) {
final audioUrl = _audios[index].audioFile[i];
await _audioPlayer.stop();
await _audioPlayer.play(audioUrl);
_isPlaying = true;
_currentAudioIndex = index;
notifyListeners();
}
}
// pause current song
void pauseAudio() async {
await _audioPlayer.pause();
_isPlaying = false;
notifyListeners();
}
// resume playing
void resumeAudio() async {
await _audioPlayer.resume();
_isPlaying = true;
notifyListeners();
}
// pause or resume
void pauseOrResumeAudio() async {
if (_isPlaying) {
pauseAudio();
} else {
resumeAudio();
}
notifyListeners();
}
// seek to a specific position in the current audio
void seek(Duration position) async {
await _audioPlayer.seek(position);
}
// play next song
void playNextAudio() {
if (_currentAudioIndex != null) {
if (_currentAudioIndex! < _audios.length - 1) {
// go to the next audio if it's not the last audio
currentAudioIndex = _currentAudioIndex! + 1;
} else {
// if it's the last song, loop back to the first song
currentAudioIndex = 0;
}
}
}
// play previous song
void playPreviousAudio() {
// if more than 2 seconds have passed, restart the current audio
if (_currentDuration.inSeconds > 2) {
// if it's within first 2 second of the audio, go to previous song
seek(Duration.zero);
} else {
if (_currentAudioIndex! > 0) {
currentAudioIndex = _currentAudioIndex! - 1;
} else {
// if it's the first song, loop back to last song
currentAudioIndex = _audios.length - 1;
}
}
}
// listen to duration
void listenToDuration() {
// listen for total Duration
_audioPlayer.onDurationChanged.listen((newDuration) {
_totalDuration = newDuration;
notifyListeners();
});
// listen for current Duration
_audioPlayer.onAudioPositionChanged.listen((newPosition) {
_currentDuration = newPosition;
notifyListeners();
});
// listen for song completion
_audioPlayer.onPlayerCompletion.listen((event) {});
}
// dispose audio player
/*
G E T T E R S
*/
// List<Audio> get items {
// return [...audios];
// }
List<Audio> get audios => _audios;
int? get currentAudioIndex => _currentAudioIndex;
bool get isPlaying => _isPlaying;
Duration get currentDuration => _currentDuration;
Duration get totalDuration => _totalDuration;
List getAudioFilesById(int audioId) {
final audio = _audios.firstWhere((audio) => audio.audioId == audioId,
orElse: () => throw Exception('Audio ID not found'));
return audio.audioFile;
}
/*
S E T T E R S
*/
set currentAudioIndex(int? newIndex) {
//update current Audio Index
_currentAudioIndex = newIndex;
if (newIndex != null) {
playAudio(newIndex);
}
//update UI
notifyListeners();
}
Audio findById(int id) {
return audios.firstWhere((audio) => audio.audioId == id);
}
}
So now, I have to let it know the selected book and create a listTile of audioFiles related to that book. I also want the audioName to be reflected for each audioFile.
This is the audioList widget I created:
import 'package:audio_book_app/screens/audio_page_screen.dart';
import 'package:flutter/material.dart';
import 'package:audio_book_app/providers/audio.dart';
import 'package:provider/provider.dart';
class AudioList extends StatelessWidget {
const AudioList({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final List<Audio> audios = Provider.of<List<Audio>>(context);
return ListView.builder(
itemCount: audios.length,
itemBuilder: (context, index) {
final audio = audios[index];
return ListTile(
leading: Image.network(
audio.bookImage,
width: 50,
height: 50,
),
title: Text(audio.audioName),
subtitle: Text(audio.audioName),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => AudioPage(),
),
);
},
);
},
);
}
}
This is my audioplayerscreen.dart:
import 'package:audio_book_app/screens/audio_page_screen.dart';
import 'package:audio_book_app/widgets/audio_list.dart';
import 'package:flutter/material.dart';
import 'package:audio_book_app/providers/audios.dart';
import 'package:audio_book_app/widgets/app_drawer.dart';
import 'package:provider/provider.dart';
class AudioPlayerScreen extends StatefulWidget {
final int bookId;
AudioPlayerScreen({required this.bookId});
static const routeName = '/audioplayerscreen';
@override
State<AudioPlayerScreen> createState() => _AudioPlayerScreenState();
}
class _AudioPlayerScreenState extends State<AudioPlayerScreen> {
late final dynamic audiosprovider;
late List audioFiles = [];
bool isPlaying = false;
// Duration duration = Duration.zero;
// Duration position = Duration.zero;
@override
void initState() {
super.initState();
audiosprovider = Provider.of<Audios>(context, listen: false);
final bookId = ModalRoute.of(context)!.settings.arguments as int;
audioFiles = audiosprovider.getAudioFilesById(bookId);
}
void goToAudio(int audioIndex) {
// update current audio index
audiosprovider.currentAudioIndex = audioIndex;
// navigate to song page
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => AudioPage(),
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color.fromARGB(255, 243, 243, 243),
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: const Text('My Library'),
),
drawer: AppDrawer(),
body: ListView.builder(
itemCount: audioFiles.length,
itemBuilder: (context, index) {
return AudioList();
},
));
}
}
This is my main.dart:
import 'package:audio_book_app/providers/audios.dart';
import 'package:audio_book_app/providers/books.dart';
import 'package:audio_book_app/screens/audio_player_screen.dart';
import 'package:audio_book_app/screens/convertin_screen.dart';
import 'package:audio_book_app/screens/home_screen.dart';
import 'package:audio_book_app/screens/login_screen.dart';
import 'package:audio_book_app/screens/logout_screen.dart';
import 'package:audio_book_app/screens/mylibrary_screen.dart';
import 'package:audio_book_app/screens/signup_screen.dart';
// import 'package:audio_book_app/screens/splash_screen.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import './providers/auth.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(
create: (ctx) => Auth(),
),
ChangeNotifierProvider(
create: (ctx) => Books(),
),
ChangeNotifierProvider(
create: (ctx) => Audios(),
),
],
child: Consumer<Auth>(
builder: (ctx, auth, _) => MaterialApp(
title: 'Audify',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.orange, secondary: Colors.deepOrange),
primaryColor: Colors.orange,
useMaterial3: true,
),
home: auth.isAuth
? const HomeScreen()
// : FutureBuilder(
// future: auth.tryAutoLogin(),
// builder: (ctx, authResultSnapshot) =>
// authResultSnapshot.connectionState ==
// ConnectionState.waiting
// ? SplashScreen()
: AuthScreen(),
// ),
routes: {
HomeScreen.routeName: (ctx) => const HomeScreen(),
ConvertingScreen.routeName: (ctx) =>
const ConvertingScreen(),
LogoutScreen.routeName: (ctx) => const LogoutScreen(),
MyLibraryScreen.routeName: (ctx) => const MyLibraryScreen(),
SignUpScreen.routeName: (ctx) => SignUpScreen(),
AuthScreen.routeName: (ctx) => AuthScreen(),
// I also need to pass the bookId here
AudioPlayerScreen.routeName: (ctx) => AudioPlayerScreen()
})),
);
}
}
Please let me know if there are more screens you need to see.
I expect the audioFiles of the selected book to be displayed but in my implementation, I am not able to pass the bookId from the selected book in the previous page as seen below:
mylibraryscreen.dart:
import 'package:audio_book_app/providers/auth.dart';
import 'package:audio_book_app/providers/books.dart';
import 'package:audio_book_app/widgets/app_drawer.dart';
import 'package:audio_book_app/widgets/mylibrary.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class MyLibraryScreen extends StatefulWidget {
const MyLibraryScreen({super.key});
static const routeName = '/mylibrary';
@override
State<MyLibraryScreen> createState() => _MyLibraryScreenState();
}
class _MyLibraryScreenState extends State<MyLibraryScreen> {
// ignore: unused_field
var _isInit = true;
// ignore: unused_field
var _isLoading = false;
@override
void initState() {
setState(() {
_isLoading = true;
});
_isLoading = true;
final authData = Provider.of<Auth>(context, listen: false);
Provider.of<Books>(context, listen: false)
.fetchAndSetBooks(authData.token)
.then((_) {
setState(() {
_isLoading = false;
});
});
super.initState();
}
@override
Widget build(BuildContext context) {
final booksData = Provider.of<Books>(context);
final books = booksData.items;
return Scaffold(
backgroundColor: const Color.fromARGB(255, 243, 243, 243),
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: const Text('My Library'),
),
drawer: AppDrawer(),
body: ListView.builder(
itemBuilder: (ctx, i) => ChangeNotifierProvider.value(
value: books[i],
child: const MyLibrary(),
),
itemCount: books.length,
),
);
}
}
mylibrary.dart:
// import 'package:audio_book_app/providers/audios.dart';
import 'package:audio_book_app/providers/book.dart';
import 'package:audio_book_app/screens/audio_player_screen.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class MyLibrary extends StatelessWidget {
const MyLibrary({super.key});
@override
Widget build(BuildContext context) {
final book = Provider.of<Book>(context, listen: false);
// final audiosProvider = Provider.of<Audios>(context);
return Stack(
alignment: Alignment.centerLeft,
children: [
Container(
margin: const EdgeInsets.symmetric(vertical: 45, horizontal: 18),
width: 550,
height: 100,
color: Colors.white,
),
Positioned(
child: Image.network(
book.bookImage, // Replace with your image asset path
width: 180,
height: 130,
),
),
Positioned(
left: 200,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
FadeTransition(
opacity:
const AlwaysStoppedAnimation(1), // Adjust opacity as needed
child: SizedBox(
width: 130,
child: Text(
book.bookName,
overflow: TextOverflow.ellipsis,
maxLines: 2,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
),
),
const Text("John Smith"),
],
),
),
Positioned(
left: 330,
top: 70,
child: IconButton(
onPressed: () {
Navigator.of(context).pushReplacementNamed(
AudioPlayerScreen.routeName,
arguments: book.bookId,
);
},
icon: const Icon(
Icons.play_circle_fill_rounded,
color: Colors.orange,
size: 50,
),
),
),
],
);
}
}