Fetching data from the internet is necessary for most apps. Luckily, Dart andFlutter provide tools for this type of work.
Directions
- Add the
httppackage - Make a network request using the
httppackage - Convert the response into a custom Dart object
- Fetch and Display the data with Flutter
1. Add the http package
The http package provides thesimplest way to fetch data from the internet.
To install the http package, you must add it to the dependencies sectionof the pubspec.yaml. You can find the latest version of the http package onthe Pub site.
dependencies:http: <latest_version>
2. Make a network request
In this example, you’ll fetch a sample post from theJSONPlaceholder REST API using thehttp.get() method.
Future<http.Response> fetchPost() {return http.get('https://jsonplaceholder.typicode.com/posts/1');}
The http.get() method returns a Future that contains a Response.
Futureisa core Dart class for working with async operations.It is used to represent a potential value or error that willbe available at some time in the future.- The
http.Responseclass contains the data received from a successfulhttp call.
3. Convert the response into a custom Dart object
While it’s easy to make a network request, working with a rawFuture<http.Response> isn’t very convenient. To make your life easier,convert the http.Response into a Dart object.
Create a Post class
First, create a Post class that contains the data from thenetwork request. It will include a factory constructor that creates a Post from json.
Converting JSON by hand is only one option. For more information,please see the full article on JSON andserialization.
class Post {final int userId;final int id;final String title;final String body;Post({this.userId, this.id, this.title, this.body});factory Post.fromJson(Map<String, dynamic> json) {return Post(userId: json['userId'],id: json['id'],title: json['title'],body: json['body'],);}}
Convert the http.Response to a Post
Now, update the fetchPost function to return a Future<Post>. To do so,you’ll need to:
- Convert the response body into a json
Mapwith thedart:convertpackage. - If the server returns an “OK” response with a status code of 200, convertthe json
Mapinto aPostusing thefromJsonfactory method. - If the server returns an unexpected response, throw an error
Future<Post> fetchPost() async {final response =await http.get('https://jsonplaceholder.typicode.com/posts/1');if (response.statusCode == 200) {// If server returns an OK response, parse the JSONreturn Post.fromJson(json.decode(response.body));} else {// If that response was not OK, throw an error.throw Exception('Failed to load post');}}
Hooray! Now you’ve got a function that we can call to fetch a Post from theinternet.
4. Fetch and Display the data
In order to fetch the data and display it on screen, you can use theFutureBuilderwidget. The FutureBuilder Widget comes with Flutter and makes it easyto work with async data sources.
You must provide two parameters:
- The
Futureyou want to work with. In this case, call thefetchPost()function. - A
builderfunction that tells Flutter what to render, depending on thestate of theFuture: loading, success, or error.
FutureBuilder<Post>(future: fetchPost(),builder: (context, snapshot) {if (snapshot.hasData) {return Text(snapshot.data.title);} else if (snapshot.hasError) {return Text("${snapshot.error}");}// By default, show a loading spinnerreturn CircularProgressIndicator();},);
5. Moving the fetch call out of the build() method
Although it’s convenient, it’s not recommended to put a call to an API in abuild() method.
Flutter calls the build() method every time it wants to change anythingin the view, and this happens surprisingly often. If you leave the fetchcall in your build() method, you’ll flood the API with unnecessary callsand slow down your app.
Here are some better options so it’ll only hit the API when the page isinitially loaded.
Pass it into a StatelessWidget
With this strategy, the parent widget is responsible for calling the fetchmethod, storing its result, and then passing it to your widget.
class MyApp extends StatelessWidget {final Future<Post> post;MyApp({Key key, this.post}) : super(key: key);
You can see a working example of this in the complete example below.
Call it in the lifecycle of a StatefulWidget’s state
If your widget is stateful, you can call the fetch method in either theinitState ordidChangeDependenciesmethods.
initState is called exactly once and then never again.If you want to have the option of reloading the API in response to anInheritedWidgetchanging, put the call into the didChangeDependencies method. SeeState for moredetails.
class _MyAppState extends State<MyApp> {Future<Post> post;@overridevoid initState() {super.initState();post = fetchPost();}
Testing
For information on how to test this functionality,please see the following recipes:
Complete example
import 'dart:async';import 'dart:convert';import 'package:flutter/material.dart';import 'package:http/http.dart' as http;Future<Post> fetchPost() async {final response =await http.get('https://jsonplaceholder.typicode.com/posts/1');if (response.statusCode == 200) {// If the call to the server was successful, parse the JSONreturn Post.fromJson(json.decode(response.body));} else {// If that call was not successful, throw an error.throw Exception('Failed to load post');}}class Post {final int userId;final int id;final String title;final String body;Post({this.userId, this.id, this.title, this.body});factory Post.fromJson(Map<String, dynamic> json) {return Post(userId: json['userId'],id: json['id'],title: json['title'],body: json['body'],);}}void main() => runApp(MyApp(post: fetchPost()));class MyApp extends StatelessWidget {final Future<Post> post;MyApp({Key key, this.post}) : super(key: key);@overrideWidget build(BuildContext context) {return MaterialApp(title: 'Fetch Data Example',theme: ThemeData(primarySwatch: Colors.blue,),home: Scaffold(appBar: AppBar(title: Text('Fetch Data Example'),),body: Center(child: FutureBuilder<Post>(future: post,builder: (context, snapshot) {if (snapshot.hasData) {return Text(snapshot.data.title);} else if (snapshot.hasError) {return Text("${snapshot.error}");}// By default, show a loading spinnerreturn CircularProgressIndicator();},),),),);}}