I wish I knew this earlier on, so I’ll will make it quick and straightforward. If this is the scenario you find yourself in, then this article might be what you’re looking for.
- You’re using the
ListView.builder
to build a list which is incoming from an endpoint (say, REST API) - You want the user to, upon swipe down (using the RefreshIndicator), Flutter goes to load new data from server, and update the
ListView.builder
content accordingly
If that’s what you wish to do, then please, here we go with what we’ll do in this article
- Retrieve a list of posts from a wordpress endpoint (will use data from blog.khophi.co – which is data that is being used to feed khophi.blog)
- Upon refresh, go to endpoint to pull more data.
Let’s get going then.
Below is the entire code for this article. I’m not here to waste your time. So go ahead and read the code to see how the StreamBuilder
is being used
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:async';
import 'dart:convert';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Basic Project',
home: new MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
StreamController _postsController;
final GlobalKey<ScaffoldState> scaffoldKey = new GlobalKey<ScaffoldState>();
int count = 1;
Future fetchPost([howMany = 5]) async {
final response = await http.get(
'https://blog.khophi.co/wp-json/wp/v2/posts/?per_page=$howMany&context=embed');
if (response.statusCode == 200) {
return json.decode(response.body);
} else {
throw Exception('Failed to load post');
}
}
loadPosts() async {
fetchPost().then((res) async {
_postsController.add(res);
return res;
});
}
showSnack() {
return scaffoldKey.currentState.showSnackBar(
SnackBar(
content: Text('New content loaded'),
),
);
}
Future<Null> _handleRefresh() async {
count++;
print(count);
fetchPost(count * 5).then((res) async {
_postsController.add(res);
showSnack();
return null;
});
}
@override
void initState() {
_postsController = new StreamController();
loadPosts();
super.initState();
}
@override
Widget build(BuildContext context) {
return new Scaffold(
key: scaffoldKey,
appBar: new AppBar(
title: new Text('StreamBuilder'),
actions: <Widget>[
IconButton(
tooltip: 'Refresh',
icon: Icon(Icons.refresh),
onPressed: _handleRefresh,
)
],
),
body: StreamBuilder(
stream: _postsController.stream,
builder: (BuildContext context, AsyncSnapshot snapshot) {
print('Has error: ${snapshot.hasError}');
print('Has data: ${snapshot.hasData}');
print('Snapshot Data ${snapshot.data}');
if (snapshot.hasError) {
return Text(snapshot.error);
}
if (snapshot.hasData) {
return Column(
children: <Widget>[
Expanded(
child: Scrollbar(
child: RefreshIndicator(
onRefresh: _handleRefresh,
child: ListView.builder(
physics: const AlwaysScrollableScrollPhysics(),
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
var post = snapshot.data[index];
return ListTile(
title: Text(post['title']['rendered']),
subtitle: Text(post['date']),
);
},
),
),
),
),
],
);
}
if (snapshot.connectionState != ConnectionState.done) {
return Center(
child: CircularProgressIndicator(),
);
}
if (!snapshot.hasData &&
snapshot.connectionState == ConnectionState.done) {
return Text('No Posts');
}
},
),
);
}
}
With the code above, you should get a result looking like this:
Our basic Project
flutter create basic && cd basic && flutter run
Delete everything from the lib/main.dart
file and replace with the code above.
Boom, you’re done, after you install the Http package from https://pub.dartlang.org/packages/http#-installing-tab- and add to your project
So that’s it?
Yes, that’s it. You wanted more? I didn’t bother going into details because I assume the code is obvious.
A
StreamBuilder
basically allows you to fetch new content, and push it through to yourListView.builder
which will automatically rebuild itself based on new incoming data.I assume to be interested to use Flutter’s StreamBuilder, then you know your way around Flutter and not here for any long ‘distin‘
In any case, the comment section is below. Lemme know if any issue.
Conclusion
The full source code of the above project is in the RefreshIndicator with StreamBuilder project I have on Github.
If any questions, please leave them in the comments.