'Bad state: No element' Error - Flutter Future Builder - flutter

I am receiving an error that says:
Bad state: No element
The relevant error-causing widget was:
Expanded file:///C:/Users/***/AndroidStudioProjects/***/lib/ui/screens/price-screen.dart:142:23
The error is referring to this part of my code:
Expanded(
child: (snapshot.connectionState ==
ConnectionState.waiting)
? Container()
: Graph(snapshot.data, crypto, graphType))
I have snapshot.data equal to 'futureData', which is equal to pricesAndTimes. I wrote some print statements in getData() to see if pricesAndTimes is null and it is not null. I'm confused as to why I'm getting this error.
import 'package:bitcoin_ticker/data/coin-data.dart';
import 'package:bitcoin_ticker/ui/components/crypto-card.dart';
import 'package:bitcoin_ticker/ui/components/graph.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import '../../constants.dart';
import 'package:crypto_font_icons/crypto_font_icons.dart';
import '../components/toggle-button.dart';
class PriceScreen extends StatefulWidget {
#override
PriceScreenState createState() => PriceScreenState();
}
class PriceScreenState extends State<PriceScreen> {
String currency = 'USD';
String graphType = '1M';
String crypto = 'LTC';
Map<String, List<String>> pricesAndTimes = {};
Future<Map> futureData;
var isSelectedCrypto = <bool>[false, false, true];
var isSelectedGraph = <bool>[false, false, true, false, false];
var isSelectedCurrency = <bool>[true, false, false];
Future<Map> getData() async {
try {
pricesAndTimes = await CoinData(currency, graphType).getCoinData();
print("pricesAndTimes");
print(pricesAndTimes);
} catch (e) {
print(e);
}
return pricesAndTimes;
}
#override
void initState() {
super.initState();
futureData = getData();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Crypto Watcher'),
),
body: FutureBuilder<Object>(
future: futureData,
builder: (context, snapshot) {
return Stack(
children: <Widget>[
Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Center(
child: ToggleButtons(
borderWidth: 10,
children: <Widget>[
CryptoCard(
currency,
snapshot.connectionState == ConnectionState.waiting
? '---'
: pricesAndTimes['currentCoinPrices'][0],
'Bitcoin',
Icon(CryptoFontIcons.BTC),
),
CryptoCard(
currency,
snapshot.connectionState ==
ConnectionState.waiting
? '---'
: pricesAndTimes['currentCoinPrices'][1],
'Ethereum',
Icon(CryptoFontIcons.ETH)),
CryptoCard(
currency,
snapshot.connectionState ==
ConnectionState.waiting
? '---'
: pricesAndTimes['currentCoinPrices'][2],
'Litecoin',
Icon(CryptoFontIcons.LTC)),
],
onPressed: (int index) {
setState(() {
for (int buttonIndex = 0;
buttonIndex < isSelectedCrypto.length;
buttonIndex++) {
if (buttonIndex == index) {
isSelectedCrypto[buttonIndex] = true;
crypto = cryptoAbbreviation[buttonIndex];
} else {
isSelectedCrypto[buttonIndex] = false;
}
}
});
futureData = getData();
},
isSelected: isSelectedCrypto,
selectedColor: Colors.amber,
renderBorder: false,
fillColor: Colors.transparent,
)),
Center(
child: ToggleButtons(
borderWidth: 0.0,
splashColor: null,
renderBorder: false,
children: <Widget>[
ToggleButton('1D'),
ToggleButton('5D'),
ToggleButton('1M'),
ToggleButton('1Y'),
ToggleButton('5Y'),
],
onPressed: (int index) {
setState(() {
for (int buttonIndex = 0;
buttonIndex < isSelectedGraph.length;
buttonIndex++) {
if (buttonIndex == index) {
isSelectedGraph[buttonIndex] = true;
graphType = graphTypeList[buttonIndex];
} else {
isSelectedGraph[buttonIndex] = false;
}
}
});
futureData = getData();
},
isSelected: isSelectedGraph,
),
),
Expanded(
child: (snapshot.connectionState ==
ConnectionState.waiting)
? Container()
: Graph(snapshot.data, crypto, graphType)),
Center(
child: ToggleButtons(
borderWidth: 0.0,
splashColor: null,
renderBorder: false,
children: <Widget>[
ToggleButton('USD'),
ToggleButton('EUR'),
ToggleButton('GBP'),
],
onPressed: (int index) {
setState(() {
for (int buttonIndex = 0;
buttonIndex < isSelectedCurrency.length;
buttonIndex++) {
if (buttonIndex == index) {
isSelectedCurrency[buttonIndex] = true;
currency = currenciesList[buttonIndex];
} else {
isSelectedCurrency[buttonIndex] = false;
}
}
futureData = getData();
});
},
isSelected: isSelectedCurrency,
)),
]),
Center(
child: (snapshot.connectionState == ConnectionState.waiting)
? CircularProgressIndicator()
: Container())
],
);
}),
);
}
}

This error happens when your stream return a null value. To make sure the data sent to UI is not null, you can check if the snapshot has data. If not, return a loading indicator. Something like this:
FutureBuilder<Object>(
future: futureData,
builder: (context, snapshot) {
if (!snapshot.hasData) return Center(child: CircularProgressIndicator());
return Stack(
children: <Widget>[
// ... other lines
);

I realized that the issue doesn't lie in the code I posted above, but in the referenced Graph file from
Expanded(
child: (snapshot.connectionState ==
ConnectionState.waiting)
? Container()
: Graph(snapshot.data, crypto, graphType))
I forgot to check that file.

Related

how to change the circular progress indicator after loading the data Flutter

the circular progress indicator dont disappear after loading the data .
this is my code where im using the progress indicator
and when i reach the end of the grid view it should load the other data but
the progress indicator makes the same thing it loads and dont disappear after getting data .
i tried to make a boolean isLoading and tried to change it true or false but couldnt find the place where i can do this
int pageNumber = 1;
String filterName = '';
class ShowsListDesign extends StatefulWidget {
#override
_ShowsListDesignState createState() => _ShowsListDesignState();
}
class _ShowsListDesignState extends State<ShowsListDesign> {
ScrollController controller = ScrollController();
ServicesClass service = ServicesClass();
bool isLoading = false;
#override
void initState() {
controller.addListener(listenScrolling);
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: service.getFilms('posts/$pageNumber/$filterName'),
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
if (snapshot.hasData) {
return Stack(
alignment: Alignment.bottomCenter,
children: [
GridView.builder(
itemCount: snapshot.data.length,
gridDelegate: const
SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 250,
crossAxisSpacing: 24,
mainAxisSpacing: 24,
childAspectRatio: (3 / 5),
),
controller: controller,
itemBuilder: (context, index) {
return FilmsCard(
image: snapshot.data[index]['thumbnailUrl'],
title: snapshot.data[index]['title'],
year: snapshot.data[index]['year'],
);
},
),
FloatingActionButton(
onPressed: () {
scrollUp();
},
elevation: 24,
backgroundColor: PRIMARY,
child: const Text(
'Scroll Up',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
),
),
),
],
);
} else {
return const Center(
child: CircularProgressIndicator(),
);
}
},
);
}
void scrollUp() {
const double start = 0;
controller.animateTo(start,
duration: const Duration(seconds: 1, milliseconds: 50),
curve: Curves.easeIn);
}
void listenScrolling() {
if (controller.position.atEdge) {
final isTop = controller.position.pixels == 0;
if (isTop) {
} else {
setState(() {
pageNumber++;
ShowsListDesign();
});
}
}
}
}
It is possible to get errors or no data on future, it will be better with handling those states.
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
} else if (snapshot.hasData &&
snapshot.connectionState == ConnectionState.done) {
return Stack(
alignment: Alignment.bottomCenter,
children: [...],
);
} else if (!snapshot.hasData &&
snapshot.connectionState == ConnectionState.done) {
return const Text("couldn't find any data");
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
} else {
return const Text(" any other");
}
},
More about FutureBuilder class.
You can't use FutureBuilder if it's not a one page load. Try this:
(I don't understand your scrolling mechanism though), also call super.initState when you override
String filterName = '';
class ShowsListDesign extends StatefulWidget {
#override
_ShowsListDesignState createState() => _ShowsListDesignState();
}
class _ShowsListDesignState extends State<ShowsListDesign> {
ScrollController controller = ScrollController();
ServicesClass service = ServicesClass();
bool isLoading = false;
// New
int pageNumber = 1;
List? data;
Future<void> load() async {
setState(() {
isLoading = true;
});
data = await service.getFilms('posts/1/$filterName');
setState(() => isLoading = false);
}
#override
void initState() {
// Make sure you call super.initState() when you override
controller.addListener(listenScrolling);
super.initState();
load();
}
// Also call dispose to remove listener
#override
void dispose() {
controller.removeListener(listener);
controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Builder(
builder: (BuildContext context) {
if (data != null) {
return Stack(
alignment: Alignment.bottomCenter,
children: [
GridView.builder(
itemCount: data!.length,
gridDelegate: const
SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 250,
crossAxisSpacing: 24,
mainAxisSpacing: 24,
childAspectRatio: (3 / 5),
),
controller: controller,
itemBuilder: (context, index) {
return FilmsCard(
image: data![index]['thumbnailUrl'],
title: data![index]['title'],
year: data![index]['year'],
);
},
),
FloatingActionButton(
onPressed: () {
scrollUp();
},
elevation: 24,
backgroundColor: PRIMARY,
child: const Text(
'Scroll Up',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
),
),
),
],
);
} else {
return const Center(
child: CircularProgressIndicator(),
);
}
},
);
}
void scrollUp() {
const double start = 0;
controller.animateTo(start,
duration: const Duration(seconds: 1, milliseconds: 50),
curve: Curves.easeIn);
}
Future<void> listenScrolling() async {
// Change this depending on the scrolling works ?!
if (controller.position.pixels == controller.position.maxScrollExtent && data != null) {
List new_data = await service.getFilms('posts/${pageNumber + 1}/$filterName');
data.addAll(new_data);
setState(() {
pageNumber++;
});
}
}
}

Simple grid gallery with photo_manager?

I have been trying to recreate this (at the bottom of the page) but with flutter version 2.12.0 and the package photo_manager. I have tried to automatically do this, but unfortunately, I am not able to resolve the following part to the newer dart syntax.
builder: (BuildContext context, snapshot) {
if (snapshot.connectionState == ConnectionState.done)
return Stack(
children: <Widget>[
Positioned.fill(
child: Image.memory(
snapshot.data, //wrong data type here
fit: BoxFit.cover,
),
),
In the orignal written code (for 2.7) it worked perfectly fine.
Simply call GridGallery() as a normal widget. Make sure to set up permissions to access the device gallery before. Have a look at the build Widget to configure the grid.
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:photo_manager/photo_manager.dart';
class GridGallery extends StatefulWidget {
final ScrollController? scrollCtr;
const GridGallery({
Key? key,
this.scrollCtr,
}) : super(key: key);
#override
_GridGalleryState createState() => _GridGalleryState();
}
class _GridGalleryState extends State<GridGallery> {
List<Widget> _mediaList = [];
int currentPage = 0;
int? lastPage;
#override
void initState() {
super.initState();
_fetchNewMedia();
}
_handleScrollEvent(ScrollNotification scroll) {
if (scroll.metrics.pixels / scroll.metrics.maxScrollExtent > 0.33) {
if (currentPage != lastPage) {
_fetchNewMedia();
}
}
}
_fetchNewMedia() async {
lastPage = currentPage;
final PermissionState _ps = await PhotoManager.requestPermissionExtend();
if (_ps.isAuth) {
// success
//load the album list
List<AssetPathEntity> albums =
await PhotoManager.getAssetPathList(
onlyAll: true);
print(albums);
List<AssetEntity> media =
await albums[0].getAssetListPaged(size: 60, page: currentPage); //preloading files
print(media);
List<Widget> temp = [];
for (var asset in media) {
temp.add(
FutureBuilder(
future: asset.thumbnailDataWithSize(ThumbnailSize(200, 200)), //resolution of thumbnail
builder:
(BuildContext context, AsyncSnapshot<Uint8List?> snapshot) {
if (snapshot.connectionState == ConnectionState.done)
return Container(
child: Stack(
children: <Widget>[
Positioned.fill(
child: Image.memory(
snapshot.data!,
fit: BoxFit.cover,
),
),
if (asset.type == AssetType.video)
Align(
alignment: Alignment.bottomRight,
child: Padding(
padding: EdgeInsets.only(right: 5, bottom: 5),
child: Icon(
Icons.videocam,
color: Colors.white,
),
),
),
],
),
);
return Container();
},
),
);
}
setState(() {
_mediaList.addAll(temp);
currentPage++;
});
} else {
// fail
/// if result is fail, you can call `PhotoManager.openSetting();` to open android/ios applicaton's setting to get permission
}
}
#override
Widget build(BuildContext context) {
return NotificationListener<ScrollNotification>(
onNotification: (ScrollNotification scroll) {
_handleScrollEvent(scroll);
return false;
},
child: GridView.builder(
controller: widget.scrollCtr,
itemCount: _mediaList.length,
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3),
itemBuilder: (BuildContext context, int index) {
return _mediaList[index];
}),
);
}
}

Flutter FutureBuilder expecting return value

I have below code and it's giving me a warning as below and during runtime it says A build function returned null.
This function has a return type of 'Widget', but doesn't end with a return statement.
Try adding a return statement, or changing the return type to 'void'.
UPDATE:
What's wrong in below code .?.
import 'package:flutter/material.dart';
import 'dart:async';
import 'dart:io';
import 'package:path/path.dart';
import 'package:permission_handler/permission_handler.dart';
List<FileSystemEntity> _pdf = [];
class BrowserScaffold extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return TabsApp();
}
}
Future<List> loadfiles() async {
_pdf = [];
int filecount = 0;
var status = await Permission.storage.status;
if (status.isUndetermined) {
await [
Permission.storage,
].request();
}
Directory dir = Directory('/storage/emulated/0/');
String pdfpath = dir.toString();
print('PATH IS ' + pdfpath);
List<FileSystemEntity> _files;
_files = dir.listSync(recursive: true, followLinks: false);
for (FileSystemEntity entity in _files) {
String path = entity.path;
if (path.endsWith('.pdf')) _pdf.add(entity);
}
for (var i = 0; i < _pdf.length; i++) {
//print(_pdf[i]);
}
filecount = _pdf.length;
print('#############ENTERED');
print(filecount);
return _pdf;
}
class TabsApp extends State<BrowserScaffold> {
#override
Widget build(BuildContext context) {
return Container(
child: DefaultTabController(
length: 2,
child: Scaffold(
appBar: AppBar(
title: Text('MY Files'),
bottom: TabBar(tabs: [
Tab(text: 'ALL FILES'),
Tab(text: 'RECENT FILES'),
]),
),
body: TabBarView(
children: [
RaisedButton(
child: Text('LIST FILES'),
onPressed: () => loadfiles(),
),
FutureBuilder(
future: loadfiles(),
builder: (context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
print('OKOK##################################');
if (snapshot.data != null) {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return Container(
child: Card(
child: Text(
basename(snapshot.data[index].path),
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18),
),
));
//return Text(snapshot.data[index].path);
});
} else {
print('FAIL##################################');
return new CircularProgressIndicator();
}
} else {
print('FAIL2##################################');
return Text("Empty");
}
}),
],
),
),
),
);
}
}
This function has a return type of 'Widget', but doesn't end with a
return statement. Try adding a return statement, or changing the
return type to 'void'.
The warning told you everything. You have two if there, so you need to have two else too.
if (snapshot.hasData) {
if (snapshot.data != null) {
...
}else{
return Text("It is null");
}
}else{
return Text("Empty");
}

Flutter Streambuilder to Datatable Headings Repeated

I am trying to work out how I can get the following data into data table, but I have really struggled, as it keeps repeating the header
import 'dart:async';
import 'dart:convert';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
class PlayerList extends StatefulWidget {
#override
_PlayerListState createState() => new _PlayerListState();
}
class _PlayerListState extends State<PlayerList> {
StreamController<List<Map<String, dynamic>>> _postsController;
final GlobalKey<ScaffoldState> scaffoldKey = new GlobalKey<ScaffoldState>();
List<Map<String, dynamic>> _list = List();
ScrollController _scrollController = new ScrollController();
int count = 0;
var url = '';
bool fetching = false, endReached = false;
List data;
Future<List<dynamic>> fetchPlayer() async {
url =
'https://supercoach.heraldsun.com.au/2020/api/afl/classic/v1/players-cf?embed=notes%2Codds%2Cplayer_stats%2Cpositions';
final response = await http.get(url);
if (response.statusCode == 200) {
print('fetching player');
return json.decode(response.body);
} else {
throw Exception('Failed to load players');
}
}
loadPlayers() async {
fetchPlayer().then((res) async {
res.forEach((model) => _list.add(model));
_postsController.add(_list);
return res;
});
}
Future<Null> _handleRefresh() async {
fetching = true;
fetchPlayer().then((res) async {
if (res != null) {
res.forEach((model) => _list.add(model));
_postsController.add(_list);
if (res.length < 10) {
endReached = true;
_list.add(Map());
_postsController.add(_list);
}
} else {
_postsController.add(null);
}
fetching = false;
return null;
});
}
#override
void initState() {
_postsController = new StreamController();
loadPlayers();
_scrollController
..addListener(() {
var triggerFetchMoreSize =
0.9 * _scrollController.position.maxScrollExtent;
if (_scrollController.position.pixels > triggerFetchMoreSize &&
!fetching &&
!endReached) {
_handleRefresh();
}
});
super.initState();
}
#override
Widget build(BuildContext context) {
return new Scaffold(
key: scaffoldKey,
appBar: AppBar(
title: Text('Exploring Players'),
),
backgroundColor: Color.fromRGBO(246, 249, 255, 1),
body: Column(children: <Widget>[
Expanded(
child: StreamBuilder(
stream: _postsController.stream,
builder: (BuildContext context, AsyncSnapshot snapshot) {
print('Has error: ${snapshot.hasError}');
print('Has data: ${snapshot.hasData}');
// print('Snapshot Data ${snapshot.data}');
print('Connection State ${snapshot.connectionState}');
if (snapshot.hasError) {
return Text(snapshot.error);
}
if (snapshot.connectionState == ConnectionState.waiting ||
snapshot.hasData == false) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
CircularProgressIndicator(
valueColor: new AlwaysStoppedAnimation<Color>(
Colors.purple)),
// Loader Animation Widget
Padding(padding: const EdgeInsets.only(top: 20.0)),
],
),
);
}
if (snapshot.data == null || snapshot.data.length == 0) {
return Column(
children: <Widget>[
Center(child: Text("Unable to find any players"))
],
);
}
// if (snapshot.hasData == false) {
// return Scaffold(
// backgroundColor: Colors.white,
// body: new Stack(
// fit: StackFit.expand,
// children: <Widget>[
// // Render the Title widget, loader and messages below each other
// new Column(
// mainAxisAlignment: MainAxisAlignment.start,
// children: <Widget>[
// Expanded(
// flex: 1,
// child: Column(
// mainAxisAlignment: MainAxisAlignment.center,
// children: <Widget>[
// CircularProgressIndicator(
// valueColor: AlwaysStoppedAnimation<Color>(
// Colors.purple)),
// // Loader Animation Widget
// Padding(
// padding:
// const EdgeInsets.only(top: 20.0)),
// Text("Finding players"),
// ],
// ),
// ),
// ],
// ),
// ],
// ),
// );
// }
if (snapshot.hasData) {
return Column(
children: <Widget>[
Expanded(
child: Scrollbar(
child: ListView.builder(
controller: _scrollController,
physics: const AlwaysScrollableScrollPhysics(),
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
Map<String, dynamic> player =
snapshot.data[index];
print(snapshot.data[index]);
return DataTable(columns: [
DataColumn(label: Text('Photo')),
DataColumn(label: Text('Player')),
DataColumn(label: Text('Round Results')),
DataColumn(label: Text('Played')),
DataColumn(label: Text('Total Points')),
DataColumn(label: Text('Avg')),
DataColumn(label: Text('3 Rd Avg')),
DataColumn(label: Text('5 Rd Avg')),
], rows: [
DataRow(cells: [
// TableRow(children: [
DataCell(Image.network(
"https://s.afl.com.au/staticfile/AFL%20Tenant/AFL/Players/ChampIDImages/XLarge2020/${player['feed_id']}.png?i10c=img.resize(scale_height:0.2)",
height: 54,
width: 54,
fit: BoxFit.fitWidth)),
DataCell(Column(children: [
Text(
"${player['first_name']} ${player['last_name']}"),
Text(
"${player['positions'][0]['position']}"),
Text(
"${player['player_stats'][0]['price']}")
])),
DataCell(Text(
"${player['player_stats'][0]['points']}")),
DataCell(Text(
"${player['player_stats'][0]['total_games']}")),
DataCell(Text(
"${player['player_stats'][0]['total_points']}")),
DataCell(Text(
"${player['player_stats'][0]['avg']}")),
DataCell(Text(
"${player['player_stats'][0]['avg3']}")),
DataCell(Text(
"${player['player_stats'][0]['avg5']}")),
])
]);
}),
),
),
],
);
}
if (!snapshot.hasData &&
snapshot.connectionState == ConnectionState.done) {
return Text('No Players');
}
if (snapshot.connectionState != ConnectionState.done) {
return Center(
child: CircularProgressIndicator(),
);
}
return Text('No Data');
},
),
)
]));
}
}
import 'dart:async';
import 'dart:convert';
import 'package:flutter/cupertino.dart';
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,
),
home: PlayerList(),
);
}
}
class PlayerList extends StatefulWidget {
#override
_PlayerListState createState() => _PlayerListState();
}
class _PlayerListState extends State<PlayerList> {
StreamController<List<Player>> _postsController;
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
List<Player> _list = <Player>[];
ScrollController _scrollController = ScrollController();
int count = 0;
var url = '';
bool fetching = false, endReached = false;
List data;
Future<List<dynamic>> fetchPlayer() async {
url =
'https://supercoach.heraldsun.com.au/2020/api/afl/classic/v1/players-cf?embed=notes%2Codds%2Cplayer_stats%2Cpositions';
final response = await http.get(url);
if (response.statusCode == 200) {
print('fetching player');
return json.decode(response.body);
} else {
throw Exception('Failed to load players');
}
}
Future<void> loadPlayers() async {
await fetchPlayer().then((res) async {
for (var model in res) {
_list.add(Player.fromMap(model));
}
_postsController.add(_list);
});
}
Future<void> _handleRefresh() async {
fetching = true;
await fetchPlayer().then((res) async {
if (res != null) {
for (var model in res) {
_list.add(Player.fromMap(model));
}
_postsController.add(_list);
if (res.length < 10) {
endReached = true;
_postsController.add(_list);
}
} else {
_postsController.add(null);
}
fetching = false;
});
}
#override
void initState() {
_postsController = StreamController();
loadPlayers();
_scrollController
..addListener(() {
var triggerFetchMoreSize =
0.9 * _scrollController.position.maxScrollExtent;
if (_scrollController.position.pixels > triggerFetchMoreSize &&
!fetching &&
!endReached) {
_handleRefresh();
}
});
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
key: scaffoldKey,
appBar: AppBar(
title: Text('Exploring Players'),
),
backgroundColor: Color.fromRGBO(246, 249, 255, 1),
body: StreamBuilder<List<Player>>(
stream: _postsController.stream,
builder: (context, snapshot) {
if (snapshot.hasError) {
return Text(snapshot.error);
}
if (snapshot.connectionState == ConnectionState.waiting ||
snapshot.hasData == false) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
CircularProgressIndicator(
valueColor:
AlwaysStoppedAnimation<Color>(Colors.purple)),
// Loader Animation Widget
Padding(padding: const EdgeInsets.only(top: 20.0)),
],
),
);
}
if (snapshot.data == null || snapshot.data.length == 0) {
return Column(
children: <Widget>[
Center(child: Text("Unable to find any players"))
],
);
}
if (snapshot.hasData) {
return SingleChildScrollView(
scrollDirection: Axis.vertical,
child: SingleChildScrollView(
physics: ClampingScrollPhysics(),
scrollDirection: Axis.horizontal,
child: DataTable(
columns: [
DataColumn(label: Text('Photo')),
DataColumn(label: Text('Player')),
DataColumn(label: Text('Round Results')),
DataColumn(label: Text('Played')),
DataColumn(label: Text('Total Points')),
DataColumn(label: Text('Avg')),
DataColumn(label: Text('3 Rd Avg')),
DataColumn(label: Text('5 Rd Avg')),
],
rows: snapshot.data
.map(
(player) => DataRow(cells: [
DataCell(Image.network(
"https://s.afl.com.au/staticfile/AFL%20Tenant/AFL/Players/ChampIDImages/XLarge2020/${player.feedId}.png?i10c=img.resize(scale_height:0.2)",
height: 54,
width: 54,
fit: BoxFit.fitWidth)),
DataCell(Column(
children: <Widget>[
Text('${player.firstName} ${player.lastName}'),
Text(player.position),
Text(player.price.toString()),
],
)),
DataCell(
Text(player.points.toString()),
),
DataCell(Text(player.totalGames.toString())),
DataCell(Text(player.totalPoints.toString())),
DataCell(Text(player.avg.toString())),
DataCell(Text(player.avg3.toString())),
DataCell(Text(player.avg5.toString())),
]),
)
.toList(),
),
),
);
}
if (!snapshot.hasData &&
snapshot.connectionState == ConnectionState.done) {
return Text('No Players');
}
if (snapshot.connectionState != ConnectionState.done) {
return Center(
child: CircularProgressIndicator(),
);
}
return Text('No Data');
},
));
}
}
class Player {
final String firstName;
final String lastName;
final String position;
final int price;
final int points;
final String feedId;
final int totalGames;
final int totalPoints;
final num avg;
final num avg3;
final num avg5;
Player({
#required this.firstName,
#required this.lastName,
#required this.position,
#required this.price,
#required this.points,
#required this.feedId,
#required this.totalGames,
#required this.totalPoints,
#required this.avg,
#required this.avg3,
#required this.avg5,
});
factory Player.fromMap(Map<String, dynamic> player) {
return Player(
avg: player['player_stats'][0]['avg'],
avg3: player['player_stats'][0]['avg3'],
avg5: player['player_stats'][0]['avg5'],
feedId: player['feed_id'],
firstName: player['first_name'],
lastName: player['last_name'],
points: player['player_stats'][0]['points'],
position: player['positions'][0]['position'],
price: player['player_stats'][0]['price'],
totalGames: player['player_stats'][0]['total_games'],
totalPoints: player['player_stats'][0]['total_points'],
);
}
}

AudioPlayers on Flutter: I can't play mp3 one behind the other

I've been stuck for several days looking for a solution and I can't find it. I need to play mp3 files one behind the other using AudioPlayers and it is impossible for me. I'm using the only tool (as far as I know) that gives you the lib to do this kind of thing, but I can't find how to do it. Thanks for the help.
playLocal(List list) async {
list.forEach((f) async {
final file = new File('${(await getTemporaryDirectory()).path}/voice.mp3');
await file.writeAsBytes((await loadAsset(f)).buffer.asUint8List());
bool finish = false;
if (player.state == null || player.state == AudioPlayerState.COMPLETED) {
await player.play(file.path, isLocal:true);
}
if (player.state != null && player.state != AudioPlayerState.COMPLETED) {
while(!finish){
player.onPlayerCompletion.listen((event){
finish = true;
});
if (finish) {await player.play(file.path, isLocal:true);}
}
}
});
}
You can use package https://pub.dev/packages/assets_audio_player
In assetsAudioPlayer.finished.listen call _next()
code snippet
void _next() {
if (_assetsAudioPlayer.playlist != null) {
_assetsAudioPlayer.playlistNext();
} else {
_currentAssetPosition++;
_open(_currentAssetPosition);
}
}
#override
void initState() {
super.initState();
_assetsAudioPlayer.finished.listen((finished) {
print("paly next");
_next();
});
}
working demo after song1.mp3 finish auto play song2.mp3
full code
import 'package:assets_audio_player/assets_audio_player.dart';
import 'package:assets_audio_player_example/asset_audio_player_icons.dart';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final assets = <String>[
"assets/audios/song1.mp3",
"assets/audios/song2.mp3",
"assets/audios/song3.mp3",
];
final AssetsAudioPlayer _assetsAudioPlayer = AssetsAudioPlayer();
var _currentAssetPosition = -1;
void _open(int assetIndex) {
_currentAssetPosition = assetIndex % assets.length;
_assetsAudioPlayer.open(assets[_currentAssetPosition]);
}
void _playPause() {
_assetsAudioPlayer.playOrPause();
}
void _next() {
if (_assetsAudioPlayer.playlist != null) {
_assetsAudioPlayer.playlistNext();
} else {
_currentAssetPosition++;
_open(_currentAssetPosition);
}
}
void _prev() {
if (_assetsAudioPlayer.playlist != null) {
_assetsAudioPlayer.playlistPrevious();
} else {
_currentAssetPosition--;
_open(_currentAssetPosition);
}
}
#override
void initState() {
super.initState();
_assetsAudioPlayer.finished.listen((finished) {
print("paly next");
_next();
});
}
#override
void dispose() {
_assetsAudioPlayer.stop();
super.dispose();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Plugin example app'),
),
body: Padding(
padding: const EdgeInsets.only(bottom: 48.0),
child: Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
RaisedButton(
onPressed: () {
_assetsAudioPlayer
.openPlaylist(Playlist(assetAudioPaths: this.assets));
},
child: Text("Playlist test"),
),
Expanded(
child: StreamBuilder(
stream: _assetsAudioPlayer.current,
initialData: const PlayingAudio(),
builder: (BuildContext context,
AsyncSnapshot<PlayingAudio> snapshot) {
final PlayingAudio currentAudio = snapshot.data;
return ListView.builder(
itemBuilder: (context, position) {
return ListTile(
title: Text(assets[position].split("/").last,
style: TextStyle(
color: assets[position] ==
currentAudio.assetAudioPath
? Colors.blue
: Colors.black)),
onTap: () {
_open(position);
});
},
itemCount: assets.length,
);
},
),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
StreamBuilder(
stream: _assetsAudioPlayer.isLooping,
initialData: false,
builder:
(BuildContext context, AsyncSnapshot<bool> snapshot) {
return RaisedButton(
child: Text(snapshot.data ? "Looping" : "Not looping"),
onPressed: () {
_assetsAudioPlayer.toggleLoop();
},
);
},
),
SizedBox(width: 20),
RaisedButton(
child: Text("Seek to 2:00"),
onPressed: () {
_assetsAudioPlayer.seek(Duration(minutes: 2));
},
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
StreamBuilder(
stream: _assetsAudioPlayer.currentPosition,
initialData: const Duration(),
builder: (BuildContext context,
AsyncSnapshot<Duration> snapshot) {
Duration duration = snapshot.data;
return Text(durationToString(duration));
},
),
Text(" - "),
StreamBuilder(
stream: _assetsAudioPlayer.current,
builder: (BuildContext context,
AsyncSnapshot<PlayingAudio> snapshot) {
Duration duration = Duration();
if (snapshot.hasData) {
duration = snapshot.data.duration;
}
return Text(durationToString(duration));
},
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: [
IconButton(
onPressed: _prev,
icon: Icon(AssetAudioPlayerIcons.to_start),
),
StreamBuilder(
stream: _assetsAudioPlayer.isPlaying,
initialData: false,
builder:
(BuildContext context, AsyncSnapshot<bool> snapshot) {
return IconButton(
onPressed: _playPause,
icon: Icon(snapshot.data
? AssetAudioPlayerIcons.pause
: AssetAudioPlayerIcons.play),
);
},
),
IconButton(
icon: Icon(AssetAudioPlayerIcons.to_end),
onPressed: _next,
),
],
),
],
),
),
),
);
}
}
String durationToString(Duration duration) {
String twoDigits(int n) {
if (n >= 10) return "$n";
return "0$n";
}
String twoDigitMinutes =
twoDigits(duration.inMinutes.remainder(Duration.minutesPerHour));
String twoDigitSeconds =
twoDigits(duration.inSeconds.remainder(Duration.secondsPerMinute));
return "$twoDigitMinutes:$twoDigitSeconds";
}