Flutter FutureBuilder expecting return value - flutter

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");
}

Related

Flutter - Autocomplete with displaying selected item information on screen

I'm trying to implement Autocomplete text, then display records related to selected item. For example, If I select 'IBM' from below example, display records related to IBM in a listview.
Autocomplete is working as expected but upon selecting the item list view is not generating. i.e. in onSelected(), widget buildPositions() should be corrected, any help would be highly appreciated.
import 'package:e2/Models/model_positions.dart';
import 'package:flutter/material.dart';
class ContractControl extends StatefulWidget {
const ContractControl({super.key});
#override
State<ContractControl> createState() => _ContractControlState();
}
class _ContractControlState extends State<ContractControl> {
List<dynamic> _selectedItems = [];
static const List<String> listItems = <String>['TCS', 'IBM', 'WIPRO'];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Contract Control"),
centerTitle: true,
//automaticallyImplyLeading: false,
),
body: Autocomplete(optionsBuilder: (TextEditingValue textEditingValue) {
if (textEditingValue.text == '') {
return const Iterable<String>.empty();
}
return listItems.where((String item) {
return item.contains(textEditingValue.text.toUpperCase());
});
}, onSelected: (String item) {
buildPositions(item);
}));
}
## *****************need correction here ******************
Widget buildPositions(String item) {
return Container(
child: FutureBuilder<List<dynamic>>(
future: ModelsPositions().detailedContractControlData(item),
builder: (context, snapshot) {
if (snapshot.hasData) {
List<dynamic> positions = snapshot.data ?? [];
return ListView.builder(
itemCount: positions.length,
itemBuilder: (context, index) {
return Card(
child: Row(children: [
Checkbox(
value: _selectedItems.contains(positions[index]),
onChanged: (value) {
setState(() {
if (value == null) {
return null;
}
if (value) {
_selectedItems.add(positions[index]);
} else {
_selectedItems
.removeWhere((item) => item == positions[index]);
}
});
},
),
]));
},
);
} else if (snapshot.hasError) {
return Center(
child: Text('Failed to fetch Positions Summary'),
);
}
return Center(
child: CircularProgressIndicator(),
);
},
),
);
}
}

The search bar does not return any results

I am trying to add a search function in my flutter app, the search bar is showing and there's not errors but its not working and it doesn't return any results.
the data list is from an API that I already called using the rest API
// ignore_for_file: use_key_in_widget_constructors, avoid_print, avoid_unnecessary_containers, curly_braces_in_flow_control_structures, prefer_const_constructors, non_constant_identifier_names, unnecessary_new, avoid_function_literals_in_foreach_calls, unused_import, avoid_types_as_parameter_names, unused_label
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:myapp2/Service_Request/SR.dart';
import 'package:myapp2/main.dart';
import 'package:myapp2/Service_Request/second.dart';
import '../Classes/demandes.dart';
import 'SR_details.dart';
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: DataFromAPI(),
);
}
}
class DataFromAPI extends StatefulWidget {
#override
_DataFromAPIState createState() => _DataFromAPIState();
}
List<Attributes> _MyAllData = [];
var _srAttributes = [];
class _DataFromAPIState extends State<DataFromAPI> {
#override
void initState() {
loadData().then((value) {
setState(() {
_srAttributes.addAll(value);
});
});
super.initState();
}
Future<List<Sr>> loadData() async {
try {
var response = await http.get(Uri.parse(
'http://192.168.1.30:9080/maxrest/rest/mbo/sr/?_lid=azizl&_lpwd=max12345m&_format=json'));
if (response.statusCode == 200) {
final jsonBody = json.decode(response.body);
Demandes data = Demandes.fromJson(jsonBody);
final srAttributes = data.srMboSet.sr;
return srAttributes;
}
} catch (e) {
throw Exception(e.toString());
}
throw Exception("");
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: new Scaffold(
appBar: AppBar(
title: Text('Liste des Demandes'),
leading: IconButton(
icon: Icon(Icons.arrow_back),
onPressed: () => Navigator.push(
context, MaterialPageRoute(builder: (context) => SR()))),
),
body: FutureBuilder<List<Sr>?>(
future: loadData(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return CircularProgressIndicator();
} else {
return new ListView.builder(
itemCount: snapshot.data?.length,
itemBuilder: ((_, index) {
return index == 0
? _searchbar()
: new ListTile(
title: new Card(
margin: new EdgeInsets.symmetric(
vertical: 2.0, horizontal: 8.0),
elevation: 10,
child: new ListTile(
title: new Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(padding: new EdgeInsets.all(2.0)),
new Text(
'Ticket ID : ${snapshot.data![index].attributes.ticketid.content}'),
new Text(
'status : ${snapshot.data![index].attributes.status.content}'),
new Text(
'description : ${snapshot.data![index].attributes.description?.content}'),
new Text(
'Reported by : ${snapshot.data![index].attributes.reportedby.content}'),
new Text(
'Reoprt date : ${snapshot.data![index].attributes.statusdate.content}'),
],
),
trailing: Icon(Icons.arrow_forward_ios_rounded),
),
),
onTap: () {
Navigator.of(context)
.push(
new MaterialPageRoute(
builder: (BuildContext context) =>
new SrDetailsScreen(
sr: snapshot.data![index]),
),
)
.then((data) {});
});
}),
);
}
},
),
),
);
}
_searchbar() {
return Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
decoration: InputDecoration(hintText: "Search ..."),
onChanged: (text) {
text = text.toLowerCase();
setState(() {
_srAttributes = _MyAllData.where((srAttributes) {
var idticket = srAttributes.description!.content.toLowerCase();
return idticket.contains(text);
}).toList();
});
},
),
);
}
}
FutureBuilder loads values of current future. You are assigning a function result to FutureBuilder so its value always changes dynamically.
Create variable to keep Future's value.
Future<List<Sr>>? dataToLoad;
Whenever you want to load data from server ( for example, on text changed ):
setState((){
dataToLoad = loadData();
});
And use it in FutureBuilder:
FutureBuilder<List<Sr>?>(
future: dataToLoad,

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

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.

How to create Dynamic Tabs, equals to the length of list

How can I create Tabs equals to the length of a list and their name should be equals to the name of items in list and I'm fetching the list from firebase realtime database.
Here's my code:
class _ItemDetailsDemoState extends State<ItemDetailsDemo> with SingleTickerProviderStateMixin {
#override
void initState() {
getSubCategories();
super.initState();
}
List<SubCategoryLoader> subCategoryLoaderList = List();
Future<void> getSubCategories() async {
await FirebaseDatabase.instance.reference().child("SubCategoryNames").once()
.then((DataSnapshot snapshot) {
var key = snapshot.value.keys;
;
for (var i in key) {
SubCategoryLoader subCategoryLoader = new SubCategoryLoader(
snapshot.value[i]['Name']
);
subCategoryLoaderList.add(subCategoryLoader);
}
for (int j = 0; j < subCategoryLoaderList.length; j++) {
print(subCategoryLoaderList[j].Name);
}
if (mounted) {
setState(() {
print(subCategoryLoaderList.length);
});
}
});
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: FirebaseDatabase.instance.reference().child("SubCategoryNames").once(),
builder: (context,snapshot){
if(snapshot.hasData){
if(snapshot.data!=null)
{
return DefaultTabController(
length: subCategoryLoaderList.length, // I made this dynamic but this is throwing an error "controller's length property does not match with number of tabs, this is because my Tab is static which is 2 how can I make it dynamic.
child: Scaffold(
appBar: AppBar(
bottom: TabBar(
tabs: [
Tab(icon: Icon(Icons.looks_one), text: "List1"), //How can i make this dynamic and text:"List1" must be the name of list items
Tab(icon: Icon(Icons.looks_two), text: "List2"),
],
),
),
body: TabBarView(
children: [
_buildList(key: "key1", string: "a"),
_buildList(key: "key2", string: "List2: "),
],
),
));
}else{
return Loader();
}
}else{
return Loader();
}
},
);
}
Widget _buildList({String key, String string}) {
return ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.vertical,
itemCount: subCategoryLoaderList.length,
itemBuilder: (context, index1){
return Container(
child: Text(subCategoryLoaderList[index1].Name+string),
);
},
);
}
}
And I also want TabBarView to be dynamic as well, so that it will render the items accordingly.I need to fetch all the data belongs to that subcategory.
The number of tabs and TabBarView's children must be the same as DefaultTabController's length. one way of doing that is to have a map function that turns SubCategoryLoader into Tabs or pages:
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: FirebaseDatabase.instance
.reference()
.child("SubCategoryNames")
.once(),
builder: (context, snapshot) {
if (snapshot.hasData) {
if (snapshot.data != null) {
return DefaultTabController(
length: subCategoryLoaderList.length,
child: Scaffold(
appBar: AppBar(
bottom: TabBar(
tabs: subCategoryLoaderList
.map((subCatagory) => Tab(text: subCatagory.Name))
.toList(),
),
),
body: TabBarView(
children: subCategoryLoaderList.map((sub){
return _buildList(key: "key${sub.id}", string: "some string");
}).toList(),
),
));
} else {
return Loader();
}
} else {
return Loader();
}
},
);
}

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'],
);
}
}