I am loading the data from the server using future and displaying the data in list view by using listview builder and adding the new list when we scroll to the end of the list but I am unable to show loading indicator while the new list of items are being loaded. I have attached my code below. I have tried the package loadmore but no luck.
Main.dart
class Herbs extends StatefulWidget {
final String title;
Herbs(this.title);
@override
_HerbsState createState() => new _HerbsState();
}
class _HerbsState extends State<Herbs> {
ScrollController _scrollController = new ScrollController();
var data;
var cname;
String gcm = '';
List pages = [];
@override
void initState() {
super.initState();
fetchPost(gcm);
_scrollController.addListener(() {
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
print('Page reached end of page');
setState(() {
Text('Loading');
});
fetchPost(gcm);
}
});
}
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
cname = widget.title;
var hgt = MediaQuery.of(context).size.width * 0.65;
var wid = MediaQuery.of(context).size.height * 0.58;
return new Scaffold(
appBar: AppBar(
title: Align(
alignment: Alignment(-0.2, 0.3),
child: Text(
cname,
),
),
),
body: Center(
child: FutureBuilder<Herbslist>(
future: fetchPost(gcm),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Scrollbar(
child: ListView.builder(
controller: _scrollController,
shrinkWrap: true,
itemCount: pages == null ? 0 : pages.length,
itemBuilder: (BuildContext context, int index) {
gcm = snapshot.data.herbslistContinue.gcmcontinue;
var img = pages[index].thumbnail.source;
return GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Detailpage(
pages[index].title,
),
));
},
child: Card(
child: Column(
children: <Widget>[
CachedNetworkImage(
placeholder: (context, url) => Image.asset(
'images/image.png',
height: hgt,
width: wid,
fit: BoxFit.fill,
),
imageUrl: img,
height: hgt,
width: wid,
fit: BoxFit.fill,
),
ListTile(
title: Text(pages[index].title, style: TextStyle(fontWeight: FontWeight.bold),),
)
],
)));
},
),
);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
},
),
),
);
}
Future<Herbslist> fetchPost(gcm) async {
String url =
'https://eample.org/api.php?action=query&gcmtitle=Category:$cname&pilimit=max&prop=pageimages&pithumbsize=200&generator=categorymembers&format=json&gcmcontinue=$gcm';
final response = await http.get(url);
print(url);
if (response.statusCode == 200) {
data = Herbslist.fromJson(json.decode(response.body));
pages.addAll(data.query.pages.values);
return data;
} else {
throw (e) {
print("Exception thrown: $e");
Exception(e);
};
}
}
}
Following is the way which worked for me(Cases maybe different for others)
class _HerbsState extends State<Herbs> {
bool _loading = false;
@override
void initState() {
super.initState();
fetchPost(gcm);
_scrollController.addListener(() {
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
print('Page reached end of page');
setState(() {});
fetchPost(gcm);
}
});
}
Widget build(BuildContext context){
return Scaffold(
ListView.builder(
controller: _scrollController,
shrinkWrap: true,
itemCount: pages == null ? 0 : pages.length+1, //add +1 here
itemBuilder: (BuildContext context, int index) {
if(index == pages.length){
_loading=true; // declare the boolean and return loading indicator
return Center(
child: Container(
child: SpinKitThreeBounce(
color: Colors.green,
size: 30,
),
));
}
)
}
}
You can do this by using the Stack
widget and overlapping the CircularProgressIndicator
with the ListView
. Check the code below for a simplified example:
class LoadingOnScroll60619794 extends StatefulWidget {
@override
_LoadingOnScroll60619794State createState() => _LoadingOnScroll60619794State();
}
class _LoadingOnScroll60619794State extends State<LoadingOnScroll60619794> {
bool _loading = false;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: <Widget>[
IconButton(
icon: Icon(Icons.refresh),
onPressed: runLoading
),
],
),
body: Stack(
children: <Widget>[
ListView(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: Text('item 1'),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text('item 1'),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text('item 1'),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text('item 1'),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text('item 1'),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text('item 1'),
),
],
),
_loading
? Center(
child: CircularProgressIndicator(),
)
: SizedBox(width: 0, height: 0,)
],
),
);
}
void runLoading(){
setState(() {
_loading = true;
});
Timer(Duration(milliseconds: 1500), (){
setState(() {
_loading = false;
});
});
}
}
If you modify your code to change the _loading variable whenever your are fetching the data, you will get the same effect. Like the example below:
Future<Herbslist> fetchPost(gcm) async {
setState(() {
_loading = true;
});
String url =
'https://eample.org/api.php?action=query&gcmtitle=Category:$cname&pilimit=max&prop=pageimages&pithumbsize=200&generator=categorymembers&format=json&gcmcontinue=$gcm';
final response = await http.get(url);
print(url);
if (response.statusCode == 200) {
data = Herbslist.fromJson(json.decode(response.body));
pages.addAll(data.query.pages.values);
setState(() {
_loading = false;
});
return data;
} else {
setState(() {
_loading = false;
});
throw (e) {
print("Exception thrown: $e");
Exception(e);
};
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With