Loading page for ListView builder flutter - flutter

Hello I would like to have a loading page before all the list is created. To display this list, I get the request info in php and I create cards one by one. The problem is that when the user come on the application, there is a lot of loading round because I put "return CircularProgressIndicator()", I would like that we are on a waiting page until all the cards are not loaded
I tried to set variables and pass them to false until all the cards are loaded but I can't get on this loading page
Here is the code of my page
Future<String> getDataSearch(text, index) async {
if (index != "ff") {
var theUrl =
Uri.parse("https://xf8s7auoez.preview.infomaniak.website/getdata.php");
var res = await http.post(theUrl, body: {
"index": index.toString(),
"post": "index",
"file": "get_data",
});
Map<String, dynamic> data = jsonDecode(res.body);
if (text == 1) {
return data["type"];
} else if (text == 2) {
return data["taille"];
} else if (text == 3) {
return data["prix"];
} else if (text == 5) {
return data["image"];
} else if (text == 6) {
return data["description"];
}
}
var theUrl =
Uri.parse("https://xf8s7auoez.preview.infomaniak.website/getdata.php");
var res = await http.post(theUrl, body: {
"post": "rows",
"file": "get_data",
});
print(res.body);
return res.body;
}
class Type extends StatelessWidget {
Type(this.index);
final int index;
#override
Widget build(context) {
return FutureBuilder<String>(
// future: getUserType(index),
future: getDataSearch(1, index),
builder: (context, AsyncSnapshot<String> snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data!);
} else {
return SizedBox(width: 10, height: 10, child: Container());
}
});
}
}
class Description extends StatelessWidget {
Description(this.index);
final int index;
#override
Widget build(context) {
return FutureBuilder<String>(
// future: getUserType(index),
future: getDataSearch(6, index),
builder: (context, AsyncSnapshot<String> snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data!);
} else {
return SizedBox(width: 10, height: 10, child: Container());
}
});
}
}
class Taille extends StatelessWidget {
Taille(this.index);
final int index;
#override
Widget build(context) {
return FutureBuilder<String>(
// future: getUserType(index),
future: getDataSearch(2, index),
builder: (context, AsyncSnapshot<String> snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data!);
} else {
return SizedBox(width: 10, height: 10, child: Container());
}
});
}
}
class Prix extends StatelessWidget {
Prix(this.index);
final int index;
#override
Widget build(context) {
return FutureBuilder<String>(
//future: getAge(index),
future: getDataSearch(3, index),
builder: (context, AsyncSnapshot<String> snapshot) {
if (snapshot.hasData) {
String bio = snapshot.data.toString();
return Text(bio);
} else {
return SizedBox(width: 10, height: 10, child: Container());
}
});
}
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Mediester',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: RefreshIndicatorView(),
);
}
}
class RefreshIndicatorView extends StatefulWidget {
const RefreshIndicatorView({Key? key}) : super(key: key);
#override
_RefreshIndicatorViewState createState() => _RefreshIndicatorViewState();
}
class _RefreshIndicatorViewState extends State<RefreshIndicatorView> {
int initNumber = 1;
int selectedValue = 1;
final GlobalKey<ScaffoldState> _key = GlobalKey();
#override
initState() {
getDataSearch(4, "ff").then((String result) {
if (initNumber != int.parse(result)) {
setState(() {
initNumber = int.parse(result);
});
}
});
}
#override
Widget build(BuildContext context) {
return RefreshIndicator(
displacement: 250,
backgroundColor: Color.fromARGB(255, 110, 155, 191),
color: Colors.white,
strokeWidth: 3,
triggerMode: RefreshIndicatorTriggerMode.onEdge,
onRefresh: () async {
await Future.delayed(Duration(milliseconds: 1500));
setState(() {
int debug = 0;
});
},
child: Scaffold(
key: _key,
backgroundColor: Color.fromARGB(255, 110, 155, 191),
appBar: BaseAppBar(),
drawer: createDrawer(context),
body: _buildCardDesign(),
),
);
}
Widget _buildCardDesign() {
Future.delayed(const Duration(milliseconds: 1500), () {
if (debugg != 1) {
setState(() {});
debugg = 1;
}
});
return Container(
margin: EdgeInsets.all(5),
child: ListView.builder(
itemCount: initNumber,
shrinkWrap: true,
physics: const BouncingScrollPhysics(
parent: AlwaysScrollableScrollPhysics()),
scrollDirection: Axis.vertical,
prototypeItem: _buildCardView(1),
itemBuilder: (BuildContext context, int index) {
return _buildCardView(index + 1);
}),
);
}
Widget _buildPhoto(index) {
return Container(
child: FutureBuilder<String>(
future: getDataSearch(5, index),
builder: (context, AsyncSnapshot<String> snapshot) {
if (snapshot.hasData) {
return ClipRRect(
borderRadius: BorderRadius.circular(5.0),
child: Image.network(
snapshot.data!,
height: 65.0,
width: 65.0,
fit: BoxFit.cover,
),
);
} else {
return CircularProgressIndicator.adaptive();
}
}));
}
Widget _buildCardView(index) {
return Container(
width: MediaQuery.of(context).size.width,
child: Container(
margin: EdgeInsets.all(10),
height: 100,
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(10))),
child: GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ArticlePage(
index, Type(index), Prix(index), Description(index)),
));
},
child: Container(
padding: EdgeInsets.fromLTRB(15, 15, 30, 15),
child: Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
_buildPhoto(index),
//SizedBox(width: 20,),
DefaultTextStyle(
style: TextStyle(
fontSize: 19,
fontWeight: FontWeight.w800,
color: Color.fromARGB(255, 110, 155, 191)),
child: Container(
padding: EdgeInsets.only(left: 40),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
DefaultTextStyle(
child: Container(child: Type(index)),
style: TextStyle(
color: Color.fromARGB(255, 110, 155, 191),
fontSize: 17)),
DefaultTextStyle(
child: Container(
padding: EdgeInsets.only(top: 8),
child: Taille(index)),
style: TextStyle(
color: Color.fromARGB(255, 168, 168, 168)),
),
Container(
padding: EdgeInsets.only(top: 3),
child: Row(
children: [
Prix(index),
Text(" €"),
],
)),
],
),
),
),
],
),
),
),
),
);
}
}

This can be accomplished using a FutureBuilder. In your RefreshIndicatorView class, the _buildCardDesign widget is currently calling your future, and then building the ListView. Instead, try creating one async function that holds the logic for retrieving the data, and a separate class for the widget that is only responsible for building the widget. Then, you can call the future in the FutureBuilder and show your loading page until it has completed.
In the FutureBuilder, pass your function as the argument to future. Then you can use the variable snapshot to check the status of the future. In your builder function, simply check if(snapshot.hasData), and show the loading page or the listView widget accordingly.
Edit: Here's a simple example that retrieves a List<String> and puts one String in each card. Your new function and widget class would look like this:
List<String> _getStrings() async {
//Async logic here
...
}
Widget MyCard extends StatelessWidget {
final String displayText;
myCard(this.displayText);
#override
Widget build(BuildContext context) {
return Text(displayText);
}
}
And in the home page of your MyApp, you can use something like this for the build method:
FutureBuilder<List<String>>(
future: _getStrings,
builder: (BuildContext context, AsyncSnapshot<List<String>> snapshot) {
if(snapshot.hasData) {
return ListView.builder(
itemBuilder: (BuildContext context, int index) => MyCard(snapshot.data[index])
...
);
} else {
return MyLoadingScreen();
}
});

Related

How to get variable from class to future in flutter?

I'm trying to update firestore data with update function as below. The variable I want to use in future is inside a class. How can i get variable from outside the future? I want to update data which is i clicked so i need to get id clicked item.the variable I want to use is '??????' I stated with
What can i do for this issue?
class Test extends StatefulWidget {
const Test({Key? key}) : super(key: key);
#override
State<Test> createState() => _TestState();
}
class _TestState extends State<Test> {
final Stream<QuerySnapshot> _usersStream =
FirebaseFirestore.instance.collection('testinfo').snapshots();
#override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(defaultPadding),
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
StreamBuilder<QuerySnapshot>(
stream: _usersStream,
builder:
(BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return const CircularProgressIndicator();
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const Text("Loading");
}
return SizedBox(
width: double.infinity,
child: DataTable(
columnSpacing: defaultPadding,
columns: const [
DataColumn(
label: Text('Text'),
),
],
rows: _buildList(context, snapshot.data!.docs)));
},
)
]));
}
List<DataRow> _buildList(
BuildContext context, List<DocumentSnapshot> snapshot) {
return snapshot.map((data) => _buildListItem(context, data)).toList();
}
DataRow _buildListItem(BuildContext context, DocumentSnapshot data) {
final ?????? = FirebaseFirestore.instance
.collection('testinfo')
.doc(data.id);
return DataRow(cells: [
DataCell(Text(data['name']!)),
DataCell(Row(children: [
PopupMenuButton<Menu>(
padding: const EdgeInsets.only(right: 2, left: 1, top: 2),
onSelected: (value) async {
if (value == Menu.itemOne) {
controllerName.text = data.get('name');
showDialog(
context: context,
builder: (context) => Dialog(
child: Container(
color: primaryColor,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: ListView(
shrinkWrap: true,
children: <Widget>[
TextField(
onChanged: (value) {
candidateName = value;
},
controller: controllerName,
decoration: InputDecoration(
hintText: 'name',)
)
)const ElevatedButton(
onPressed: (updateData),
child: Text('Save'),
),
itemBuilder: (BuildContext context) => <PopupMenuEntry<Menu>>[
const PopupMenuItem<Menu>(value: Menu.itemTwo,
child: Text('edit'),),
Future updateData() async {
await ??????.update({
'name': controllerName.text,
}).then((value) => print('updated.'));
}
UserDetailsModel currentUser;
List<UserDetailsModel> membersDetails = [];
for (int i = 0; i < data.docs.length; i++) {
membersDetails.add(UserDetailsModel.fromJson(
data.docs[i].data()! as Map<String, dynamic>));
}
UserDetailsModel is your model class
you use it later...

I am trying to fetch collection from shopify admin api with rest api in flutter

I am Using flutter with http package. in my code CircularProgressIndicator() is running and is not showing any error
here is my http request code
import 'package:http/http.dart' as http;
import 'dart:convert';`
class Collections {
Future<List> getAllCollections() async {
var headers = {
'Content-Type': 'application/json',
'X-Shopify-Access-Token': '{token}',
};
var url = Uri.parse(
'https://{shop}.myshopify.com/admin/api/2022-07/collections.json');
try {
var res = await http.get(url, headers: headers);
print(res);
if (res.statusCode == 200) {
return jsonDecode(res.body);
} else {
return Future.error("Server Error");
}
} catch (SocketException) {
return Future.error("Error Fetching Data");
}
}
}
and here I created the object of this class and using it.
class ProCategories extends StatefulWidget {
const ProCategories({Key? key}) : super(key: key);
#override
State<ProCategories> createState() => _ProCategoriesState();
}
class _ProCategoriesState extends State<ProCategories> {
Collections collections = Collections();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Categories"),
centerTitle: true,
foregroundColor: Colors.black,
backgroundColor: Colors.white,
),
body: Container(
margin: EdgeInsets.only(top: 8.0),
child: FutureBuilder<List>(
builder: (context, snapshot) {
if (snapshot.hasData) {
if (snapshot.data?.length == 0) {
return Center(
child: Text("0 Collections"),
);
}
return ListView.builder(
itemCount: snapshot.data?.length,
itemBuilder: (context, index) {
return Card(
child: ListTile(
title: Row(
children: [
Expanded(
child: Container(
child: Text(
snapshot.data![index]['collections']['edges']
['node']['title'],
style: TextStyle(
fontSize: 25,
fontWeight: FontWeight.bold),
),
),
),
],
),
),
);
});
} else if (snapshot.hasError) {
return Center(
child: Text(snapshot.error.toString()),
);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
},
),
),
);
}
}
Add future to FutureBuilder.
late Future<List> getAllCollections;
#override
void initState() {
super.initState();
getAllCollections = collections.getAllCollections();
}
#override
Widget build(BuildContext context) {
return Scaffold(
...
body: Container(
margin: EdgeInsets.only(top: 8.0),
child: FutureBuilder<List>(
future: getAllCollections,
builder: (context, snapshot) {
...
},
),
),
);
}
See Fetch data from the internet for the full example.

How to play a List of video carousel?

I want to create a List of video carousel, something similar to a social media platform.
I tried making video carousel using flutter_swiper and video_player. and created a list using inview_notifier_list 's autoplay example, But unable to get the desired output.
class VideoList extends StatefulWidget {
#override
_VideoListState createState() => _VideoListState();
}
class _VideoListState extends State<VideoList> {
List<String> videoList = [
'https://flutter.github.io/assets-for-api-docs/assets/videos/butterfly.mp4',
'https://flutter.github.io/assets-for-api-docs/assets/videos/butterfly.mp4',
'https://flutter.github.io/assets-for-api-docs/assets/videos/butterfly.mp4',
'https://flutter.github.io/assets-for-api-docs/assets/videos/butterfly.mp4',
];
String videoToPlay;
#override
void initState() {
super.initState();
videoToPlay = videoList[0];
}
#override
Widget build(BuildContext context) {
return Stack(
fit: StackFit.expand,
children: <Widget>[
InViewNotifierList(
scrollDirection: Axis.vertical,
initialInViewIds: ['0'],
isInViewPortCondition:
(double deltaTop, double deltaBottom, double viewPortDimension) {
return deltaTop < (0.5 * viewPortDimension) &&
deltaBottom > (0.5 * viewPortDimension);
},
itemCount: 5,
builder: (context, index) => Container(
width: double.infinity,
height: 300.0,
alignment: Alignment.center,
margin: EdgeInsets.symmetric(vertical: 50.0),
child: SizedBox(
height: 300,
width: double.infinity,
child: Swiper(
onIndexChanged: (i) {
setState(() {
videoToPlay = videoList[i];
});
},
itemCount: videoList.length,
itemBuilder: (context, index) => LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
final InViewState inViewState =
InViewNotifierList.of(context);
inViewState.addContext(context: context, id: '$index');
return AnimatedBuilder(
animation: inViewState,
builder: (BuildContext context, Widget child) {
return VideoWidget(
play: inViewState.inView('$index'),
url: videoToPlay);
},
);
},
),
),
),
),
),
Align(
alignment: Alignment.center,
child: Container(
height: 1.0,
color: Colors.redAccent,
),
)
],
);
}
}
and below is my video class, where the video player is initialized
class VideoWidget extends StatefulWidget {
final String url;
final bool play;
const VideoWidget({Key key, #required this.url, #required this.play})
: super(key: key);
#override
_VideoWidgetState createState() => _VideoWidgetState();
}
class _VideoWidgetState extends State<VideoWidget> {
VideoPlayerController _controller;
Future<void> _initializeVideoPlayerFuture;
#override
void initState() {
super.initState();
_controller = VideoPlayerController.network(widget.url);
_initializeVideoPlayerFuture = _controller.initialize().then((_) {
// Ensure the first frame is shown after the video is initialized, even before the play button has been pressed.
setState(() {});
});
if (widget.play) {
_controller.play();
_controller.setLooping(true);
}
}
#override
void didUpdateWidget(VideoWidget oldWidget) {
if (oldWidget.play != widget.play) {
if (widget.play) {
_controller.play();
_controller.setLooping(true);
} else {
_controller.pause();
}
}
super.didUpdateWidget(oldWidget);
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: _initializeVideoPlayerFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return VideoPlayer(_controller);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
},
);
}
}
Could someone please help out, What should be the correct approach?
So here I am sharing my solution How did i manged to create a List of video cousrsel.
Widget build(BuildContext context) {
return Stack(
fit: StackFit.expand,
children: <Widget>[
InViewNotifierList(
scrollDirection: Axis.vertical,
initialInViewIds: ['0'],
isInViewPortCondition:
(double deltaTop, double deltaBottom, double viewPortDimension) {
return deltaTop < (0.5 * viewPortDimension) &&
deltaBottom > (0.5 * viewPortDimension);
},
itemCount: 5,
builder: (context, index) => Container(
width: double.infinity,
height: 300.0,
alignment: Alignment.center,
margin: EdgeInsets.symmetric(vertical: 50.0),
child: LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
print('layout called');
final InViewState inViewState = InViewNotifierList.of(context);
inViewState.addContext(context: context, id: '$index');
return PageView.builder(
itemCount: videoList.length,
itemBuilder: (context, indexPage) => AnimatedBuilder(
animation: inViewState,
builder: (BuildContext context, Widget child) {
return VideoWidget(
play: inViewState.inView('$index'),
url: videoList[indexPage]);
},
),
);
},
),
),
),
Align(
alignment: Alignment.center,
child: Container(
height: 1.0,
color: Colors.redAccent,
),
)
],
);
}
}
where my video List is the list of URL of videos.

Set state from FutureBuilder in Flutter

I've been trying to set a property based on a response from a FutureBuilder but can't seem to get it working. How can I set _activityLength after the future resolves without setting during build?
FutureBuilder<QuerySnapshot>(
future: _future,
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return Text('Press button to start');
case ConnectionState.waiting:
return Center(
child: CircularProgressIndicator(),
);
default:
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
final documents = snapshot.data.documents;
_activityLength = documents.length;
return Expanded(
child: ListView.separated(
shrinkWrap: true,
separatorBuilder: (context, index) => Divider(
color: Colors.black,
height: 0,
),
itemCount: documents.length,
itemBuilder: (context, index) => _activityTile(
documents[index],
),
),
);
}
}
},
)
The FutureBuilde is in a Column widget in the body of the Scaffold and the value that I need to set is in the _itemsHeaderText Something like this:
body:
...
child: Column(
children: <Widget>[
Container(
width: double.infinity,
decoration: BoxDecoration(
border: Border(
bottom:
BorderSide(color: Colors.grey, width: 1.0),
),
),
child: Padding(
padding: const EdgeInsets.only(
left: 15.0,
right: 15.0,
top: 10.0,
bottom: 10.0),
child: _itemsHeaderText(),
),
),
_itemsBody(),
],
),
You can copy paste run full demo code below
You can use _future.then((value) in initState() and do job in addPostFrameCallback like setState
And after future resolves, you can get value of _future, you can do further processing if need
In demo code, I get value length and display on screen
You can see working demo, data length change from 0 to 9
code snippet
#override
void initState() {
super.initState();
_future = _getUsers();
_future.then((value) {
print("data length ${value.length}");
WidgetsBinding.instance.addPostFrameCallback((_) {
print("other job");
setState(() {
_dataLength = value.length;
});
});
});
}
working demo
output
I/flutter (18414): data length 9
I/flutter (18414): other job
full code
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: CategoryTab(title: 'Flutter Demo Home Page'),
);
}
}
class CategoryTab extends StatefulWidget {
CategoryTab({Key key, this.title}) : super(key: key);
final String title;
#override
_CategoryTabState createState() => _CategoryTabState();
}
class _CategoryTabState extends State<CategoryTab> {
Future<List<CategoryList>> _future;
int _dataLength = 0;
Future<List<CategoryList>> _getUsers() async {
var data = await http
.get("https://appiconmakers.com/demoMusicPlayer/API/getallcategories");
var jsonData = json.decode(data.body);
List<CategoryList> cat = [];
for (var u in jsonData) {
CategoryList categoryList = CategoryList(
u["category_id"],
u["category_name"],
u["parent_category_id"],
u["category_status"],
u["created_date"]);
cat.add(categoryList);
}
return cat;
}
#override
void initState() {
super.initState();
_future = _getUsers();
_future.then((value) {
print("data length ${value.length}");
WidgetsBinding.instance.addPostFrameCallback((_) {
print("other job");
setState(() {
_dataLength = value.length;
});
});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
"Categories",
style: TextStyle(color: Colors.black),
),
backgroundColor: Colors.white,
),
body: Column(
children: [
Text("data length $_dataLength"),
Expanded(
child: Container(
child: FutureBuilder(
future: _future,
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.data == null) {
return Container(child: Center(child: Text("Loading...")));
} else {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
leading: CircleAvatar(),
title: Text(
"${snapshot.data[index].categoryName}",
// subtitle: Text(snapshot.data[index].categoryId
),
);
},
);
}
},
),
),
),
],
),
);
}
}
class CategoryList {
String categoryId;
String categoryName;
String parentCategoryId;
String categoryStatus;
String createdDate;
CategoryList(this.categoryId, this.categoryName, this.parentCategoryId,
this.categoryStatus, this.createdDate);
}
There are a few workarounds to your question.
You could hoist the futureBuilder up the widget tree, instead of setting state the state will be reset once ConnectionState.done.
You could place the future in a function that is called on init state, then the result of the future you set state on it.

Flutter Bloc with audioplayers

Almost a day of figuring way how to do it and I need your help.
I've created an app that would have a state with an instance of audioplayers and using flutter_bloc.
Problems:
The state player plays but the Widget MusicPlayer doesn't rebuild the widget with the BlocBuilder
I'm trying also to get the currentPosition and duration of the music I'm playing and displaying it using LinearPercentIndicator from linear_percent_indicator package but can't seem to find a solution because rebuild doesn't work.
What am I missing?
Here's what I have so far:
Bloc
class AudioPlayerBloc extends Bloc<AudioPlayerEvents, MusicPlayerState> {
#override
MusicPlayerState get initialState => MusicPlayerState(
player: AudioPlayer(),
episode: Episode(),
);
#override
Stream<MusicPlayerState> mapEventToState(AudioPlayerEvents event) async* {
if (event is InitializePlayer) {
this.currentState.episode = event.episode;
this.dispatch(PlayPlayer());
yield this.currentState;
}
if (event is PlayPlayer) {
this.play(this.currentState.episode.source);
}
if (event is PlayRemote) {
this.currentState.player.stop();
this.currentState.player.play(event.remoteURL);
yield this.currentState;
}
if (event is ShowPlayer) {
yield this.currentState;
}
if (event is HidePlayer) {
yield this.currentState;
}
}
void play(String remoteURL) {
this.dispatch(PlayRemote(remoteURL));
}
void stop() async {
await this.currentState.player.stop();
}
void pause() async{
await this.currentState.player.pause();
}
void resume(){
this.currentState.player.resume();
}
#override
void dispose() {
super.dispose();
}
}
Event
abstract class AudioPlayerEvents {}
class InitializePlayer extends AudioPlayerEvents {
Episode episode;
InitializePlayer(this.episode);
}
class PlayRemote extends AudioPlayerEvents {
final String remoteURL;
PlayRemote(this.remoteURL);
}
class PlayPlayer extends AudioPlayerEvents {}
class ShowPlayer extends AudioPlayerEvents {}
class HidePlayer extends AudioPlayerEvents {}
State
import 'package:audioplayers/audioplayers.dart';
class MusicPlayerState {
AudioPlayer player;
Episode episode; // My Custom Class
MusicPlayerState({
this.player,
this.episode,
});
}
main.dart
#override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider<AudioPlayerBloc>(
builder: (_) => AudioPlayerBloc(),
)
],
child: MaterialApp(
navigatorObservers: [],
initialRoute: HomeScreen.id,
routes: {
HomeScreen.id: (context) => HomeScreen(app: widget.app),
},
),
);
}
}
MusicPlayer.dart <-- my Player Widget
class MusicPlayer extends StatelessWidget {
#override
Widget build(BuildContext context) {
final AudioPlayerBloc audioPlayerBloc =
BlocProvider.of<AudioPlayerBloc>(context);
return BlocBuilder<AudioPlayerBloc, MusicPlayerState>(
bloc: audioPlayerBloc,
builder: (context, state) {
return Container(
height: 200.0,
color: Colors.cyan[200],
child: Padding(
padding: const EdgeInsets.only(top: 20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Column(
children: <Widget>[
Text("${state.episode.name}"),
Row(
children: <Widget>[
Expanded(
flex: 1,
child: FutureBuilder<int>(
future: audioPlayerBloc.currentState.player.getCurrentPosition(),
builder: (context, AsyncSnapshot<int> snapshot) {
double seconds = snapshot.data / 1000;
if (snapshot.hasData) {
return Text("${printDuration(Duration(seconds: seconds.toInt()), abbreviated: true)}");
} else {
return Text('Calculating..');
}
},
),
),
Expanded(
flex: 3,
child: LinearPercentIndicator(
lineHeight: 7.0,
// percent: this.currentPosition / this.fileDuration,
percent: 0.3,
backgroundColor: Colors.grey,
progressColor: Colors.blue,
),
),
Expanded(
flex: 1,
child: FutureBuilder<int>(
future: audioPlayerBloc.currentState.player.getDuration(),
builder: (context, AsyncSnapshot<int> snapshot) {
double seconds = snapshot.data / 1000;
if (snapshot.hasData) {
return Text("${printDuration(Duration(seconds: seconds.toInt()), abbreviated: true)}");
} else {
return Text('Calculating..');
}
},
),
),
],
),
Text(state.player.state.toString()),
FlatButton(
onPressed: () {
print('close here');
Navigator.of(context).pop();
},
child: Icon(
Icons.close,
color: Colors.black.withOpacity(0.5),
),
),
Row(
children: <Widget>[
FlatButton(
onPressed: () {
audioPlayerBloc.pause();
},
child: Text('Pause Player'),
),
FlatButton(
onPressed: () {
audioPlayerBloc.resume();
},
child: Text('Resume Player'),
),
FlatButton(
onPressed: () {
audioPlayerBloc.stop();
},
child: Text('Stop Player'),
),
],
)
],
)
],
),
),
);
},
);
}
}
HomeScreen.dart <-- my first screen
#override
Widget build(BuildContext context) {
final AudioPlayerBloc audioPlayerBloc = BlocProvider.of<AudioPlayerBloc>(context);
return MultiBlocProvider(
providers: [
BlocProvider<AudioPlayerBloc>(
builder: (_) => AudioPlayerBloc(),
)
],
child: Scaffold(
appBar: AppBar(
title: Text('Global Audio Player'),
),
body: Container(
child: BlocBuilder<AudioPlayerBloc, MusicPlayerState>(
builder: (context, state) {
return Column(
children: <Widget>[
Flexible(
child: getListView(context)
),
displayPlayer(state.player), // Here I'm trying to display the player when the AudioPlayerState is PLAYING
],
);
},
),
),
),
);
}
Global function
Widget displayPlayer(AudioPlayer player){
return MusicPlayer();
if(player.state == AudioPlayerState.PLAYING) {
return MusicPlayer();
}
return Container();
}
EpisodesScreen.dart <-- ListView of the Episodes
class _EpisodesScreenState extends State<EpisodesScreen> {
#override
void initState() {
super.initState();
print(widget.series);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(widget.series.name)),
body: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Expanded(
flex: 0,
child: Image.network(widget.series.image.original),
),
Expanded(
child: getListView(context),
),
Expanded(
child: BlocBuilder<AudioPlayerBloc, MusicPlayerState>(
builder: (context, state) {
return displayPlayer(state.player);
},
),
)
],
));
}
Widget getListView(BuildContext context) {
List<Episode> episodesList = widget.series.episodes;
final AudioPlayerBloc audioPlayerBloc =
BlocProvider.of<AudioPlayerBloc>(context);
var listView = ListView.separated(
itemCount: episodesList.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(episodesList[index].name),
trailing: BlocBuilder<AudioPlayerBloc, MusicPlayerState>(
builder: (context, state) {
return FlatButton(
onPressed: () {
audioPlayerBloc.dispatch(InitializePlayer(episodesList[index]));
},
child: Icon(
Icons.play_arrow,
color: Colors.black87,
),
);
},
),
);
},
separatorBuilder: (BuildContext context, int index) {
return Divider(
height: 1.0,
color: Colors.black12,
);
},
);
return listView;
}
}
I missed the yield * part of it and it is now working.
Stream<Duration> currentPosition() async* {
yield* this.currentState.audioPlayer.onAudioPositionChanged;
}
Stream<Duration> fileDuration() async* {
yield* this.currentState.audioPlayer.onDurationChanged;
}