If our app is getting data through network request from our backend usually we handle the response with FutureBuilder or StreamBuilder widgets (state management approaches apart).
We can have something like this (this example use a FutureBuilder but of course we can do it with a StreamBuilder):
class MyHomePage extends StatelessWidget {
final String title;
final PostProvider postProvider = PostProvider();
const MyHomePage({@required this.title});
Future<List<Post>> getFuturePostList() async {
return await postProvider.getPostList();
}
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: getFuturePostList(),
builder: (BuildContext context, AsyncSnapshot<List<Post>> snapshot) {
Widget child;
if (snapshot.hasData) {
// Show widget with list of Post
} else if (snapshot.hasError) {
child = Center(child: Text("Error: ${snapshot.error.toString()}"), );
} else {
child = Center(child: CircularProgressIndicator());
}
return child;
});
}
}
The example above, is a very simple app fetching a list of Post objects trough our PostProvider (this class makes a request to our backend using for example http package), and render through a FutureBuilder widget.
It's not a bad approach and of course is running ok, but doing at this way, we are making a new request every time our widget is build.
A simple tip that can reduce drastically these number of calls, is extending our widget from a StatefulWidget and next instead of pass a function to our FutureBuilder, override initState method, and store the result in a variable.
Let's go to see:
class MyHomePage extends StatefulWidget {
const MyHomePage({Key key}) : super(key: key);
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final String title;
final PostProvider postProvider = PostProvider();
Future<List<Post>> futurePostList;
Future<List<Post>> getFuturePostList() async {
return await postProvider.getPostList();
}
@override
void initState() {
futurePostList = getFuturePostList();
}
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: futurePostList,
builder: (BuildContext context, AsyncSnapshot<List<Post>> snapshot) {
Widget child;
if (snapshot.hasData) {
// Show widget with list of Post
} else if (snapshot.hasError) {
child = Center(child: Text("Error: ${snapshot.error.toString()}"), );
} else {
child = Center(child: CircularProgressIndicator());
}
return child;
});
}
}
Now, our widget only do the request at initState, and every time we render (build) our widget we are avoiding to extra requests.
Tip: Avoid calling functions (doing network calls) at build state, and try to find another approach.
Top comments (1)
That is the reason why I chose Bloc over provider :D less spaghetti code in the UI