Until now you have been working with static data. Let’s switch up the gears and bring some “real” data from the internet. In this tutorial you will learn how to make network calls in a flutter application. I assume you have basic understanding of http. You will make GET and POST requests in this tutorial.

The code in this tutorial can be found here.

Adding http dependency

Before anything you need to add the http library in your flutter project. Go to the link to grab the latest version (recommended) or just use the version I am using (not recommended, only if you are super lazy).

Open pubspec.yaml and paste the http dependency.

pubspec.yaml dependencies: flutter: sdk: flutter http: ^0.12.0+2 1 2 3 4 dependencies : flutter : sdk : flutter http : ^0.12.0+2

After this, open up a terminal and from the root of your project run the following command.

flutter packages command flutter packages get 1 flutter packages get

This will make sure all the packages mentioned in the pubspec.yaml are fetched and ready to be used by the project.

Making a GET request

We are going to use the JSON placeholder api for purpose of this tutorial. JSON Placeholder is a service that provides you with endpoints that emit fake JSON data, which is all we need for this tutorial.

For GET request we will hit the /posts api that returns a list of posts (format below). First you will display just the raw text from the response. After that you will parse the response as json and display the posts in a list.

[ { "userId": 1, "id": 1, "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit", "body": "quia et suscipit

suscipit recusandae consequuntur expedita et cum

reprehenderit molestiae ut ut quas totam

nostrum rerum est autem sunt rem eveniet architecto" }, { "userId": 1, "id": 2, "title": "qui est esse", "body": "est rerum tempore vitae

sequi sint nihil reprehenderit dolor beatae ea dolores neque

fugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis

qui aperiam non debitis possimus qui neque nisi nulla" } ] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 [ { "userId" : 1 , "id" : 1 , "title" : "sunt aut facere repellat provident occaecati excepturi optio reprehenderit" , "body" : "quia et suscipit

suscipit recusandae consequuntur expedita et cum

reprehenderit molestiae ut ut quas totam

nostrum rerum est autem sunt rem eveniet architecto" } , { "userId" : 1 , "id" : 2 , "title" : "qui est esse" , "body" : "est rerum tempore vitae

sequi sint nihil reprehenderit dolor beatae ea dolores neque

fugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis

qui aperiam non debitis possimus qui neque nisi nulla" } ]

Set up a basic hello world app in flutter.

Flutter basic app import 'package:flutter/material.dart'; void main() { runApp(HttpApp()); } class HttpApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: HttpExampleWidget(), ); } } class HttpExampleWidget extends StatefulWidget { @override _HttpExampleWidgetState createState() => _HttpExampleWidgetState(); } class _HttpExampleWidgetState extends State<HttpExampleWidget> { @override Widget build(BuildContext context) { return Scaffold( body: Center( child: Text('Http Example'), ), ); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 import 'package:flutter/material.dart' ; void main ( ) { runApp ( HttpApp ( ) ) ; } class HttpApp extends StatelessWidget { @ override Widget build ( BuildContext context ) { return MaterialApp ( home : HttpExampleWidget ( ) , ) ; } } class HttpExampleWidget extends StatefulWidget { @ override _HttpExampleWidgetState createState ( ) = > _HttpExampleWidgetState ( ) ; } class _HttpExampleWidgetState extends State < HttpExampleWidget > { @ override Widget build ( BuildContext context ) { return Scaffold ( body : Center ( child : Text ( 'Http Example' ) , ) , ) ; } }

For this you will need a StatefulWidget, you will see why in just a moment. You will be writing code in HttpExampleWidget. Let’s display the response from the api in a Text widget (you already have that in place, with the text ‘HttpExample’). For this you need to store the response from api somewhere inside the widget’s state, lets store that in a variables named _text.

class _HttpExampleWidgetState extends State<HttpExampleWidget> { String _text = "Http Example"; @override Widget build(BuildContext context) { return Scaffold( body: Center( child: Text(_text), ), ); } } 1 2 3 4 5 6 7 8 9 10 11 12 class _HttpExampleWidgetState extends State < HttpExampleWidget > { String _text = "Http Example" ; @ override Widget build ( BuildContext context ) { return Scaffold ( body : Center ( child : Text ( _text ) , ) , ) ; } }

The actual http call will be made in a method named _fetchPosts. This will be an async function. (If you don’t know how asynchronous methods work, read this tutorial).

class _HttpExampleWidgetState extends State<HttpExampleWidget> { String _text = "Http Example"; void _fetchPosts() async { } @override Widget build(BuildContext context) { return Scaffold( body: Center( child: Text(_text), ), ); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class _HttpExampleWidgetState extends State < HttpExampleWidget > { String _text = "Http Example" ; void _fetchPosts ( ) async { } @ override Widget build ( BuildContext context ) { return Scaffold ( body : Center ( child : Text ( _text ) , ) , ) ; } }

Import the http package.

import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; ... class _HttpExampleWidgetState extends State<HttpExampleWidget> { String _text = "Http Example"; void _fetchPosts() async { } @override Widget build(BuildContext context) { return Scaffold( body: Center( child: Text(_text), ), ); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import 'package:flutter/material.dart' ; import 'package:http/http.dart' as http ; . . . class _HttpExampleWidgetState extends State < HttpExampleWidget > { String _text = "Http Example" ; void _fetchPosts ( ) async { } @ override Widget build ( BuildContext context ) { return Scaffold ( body : Center ( child : Text ( _text ) , ) , ) ; } }

The actually GET request is just one line! Await for the response from api, the returned object is of type Response which contains a property named body that has the data returned from the api. Once response is received, update the _text variable and call setState to make the UI re-render with updated data.

class _HttpExampleWidgetState extends State<HttpExampleWidget> { String _text = "Http Example"; void _fetchPosts() async { final response = await http.get('https://jsonplaceholder.typicode.com/posts'); setState(() { _text = response.body; }); } @override Widget build(BuildContext context) { return Scaffold( body: Center( child: Text(_text), ), ); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class _HttpExampleWidgetState extends State < HttpExampleWidget > { String _text = "Http Example" ; void _fetchPosts ( ) async { final response = await http . get ( 'https://jsonplaceholder.typicode.com/posts' ) ; setState ( ( ) { _text = response . body ; } ) ; } @ override Widget build ( BuildContext context ) { return Scaffold ( body : Center ( child : Text ( _text ) , ) , ) ; } }

Now that you have written the _fetchPosts function, you need to call it somewhere. One option is calling it inside build method (wrong, never do something like this), the reason it is a bad practice is because you want the network request to be made once, build method doesn’t get called only once, the framework can call it at multiple occasions that are not under your control, for example when keyboard pops up, build method is triggered. Remember that we made this widget Stateful, we can use the initState method which is only called once when the state is created initially, it is a good place to call the _fetchPosts method.

Flutter Stateful Widget initState class _HttpExampleWidgetState extends State<HttpExampleWidget> { String _text = "Http Example"; void _fetchPosts() async { final response = await http.get('https://jsonplaceholder.typicode.com/posts'); setState(() { _text = response.body; }); } @override void initState() { super.initState(); _fetchPosts(); } @override Widget build(BuildContext context) { return Scaffold( body: Center( child: Text(_text), ), ); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class _HttpExampleWidgetState extends State < HttpExampleWidget > { String _text = "Http Example" ; void _fetchPosts ( ) async { final response = await http . get ( 'https://jsonplaceholder.typicode.com/posts' ) ; setState ( ( ) { _text = response . body ; } ) ; } @ override void initState ( ) { super . initState ( ) ; _fetchPosts ( ) ; } @ override Widget build ( BuildContext context ) { return Scaffold ( body : Center ( child : Text ( _text ) , ) , ) ; } }

Run the app, once the response from api is received it would look something like this:

Never miss a post from TheTechnoCafe

JSON parsing in Flutter

Right now you are just displaying the response from api as is, it is not very appealing. Lets parse this response and display it inside a ListView. Parsing JSON in flutter is very easy, just use the jsonDecode function from dart:convert package (in-built dart package).

import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'dart:convert'; ... class _HttpExampleWidgetState extends State<HttpExampleWidget> { String _text = "Http Example"; void _fetchPosts() async { final response = await http.get('https://jsonplaceholder.typicode.com/posts'); final List<dynamic> parsedResponse = jsonDecode(response.body); setState(() { _text = response.body; }); } ... } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import 'package:flutter/material.dart' ; import 'package:http/http.dart' as http ; import 'dart:convert' ; . . . class _HttpExampleWidgetState extends State < HttpExampleWidget > { String _text = "Http Example" ; void _fetchPosts ( ) async { final response = await http . get ( 'https://jsonplaceholder.typicode.com/posts' ) ; final List < dynamic > parsedResponse = jsonDecode ( response . body ) ; setState ( ( ) { _text = response . body ; } ) ; } . . . }

The returned response from api is a list of JSON objects(with type Map<String, dynamic>), so jsonDecode returns a List, if response is not a List, it would return a Map<String, dynamic>.

Create a model class to hold a single Post.

Post model class Post { final int userId; final int id; final String title; Post({ this.userId, this.id, this.title, }); } 1 2 3 4 5 6 7 8 9 10 11 class Post { final int userId ; final int id ; final String title ; Post ( { this . userId , this . id , this . title , } ) ; }

Every single JSON object inside the parsed list has a type of Map<String, dynamic> and contains the relevant Post data. You need a way to convert this Map<String, dynamic> into a Post object. A good practice is to write a factory constructor name fromJSON (the name clearly suggest that a Post object will be created from JSON data).

class Post { final int userId; final int id; final String title; Post({ this.userId, this.id, this.title, }); factory Post.fromJSON(Map<String, dynamic> json) { return Post( id: json['id'], userId: json['userId'], title: json['title'], ); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Post { final int userId ; final int id ; final String title ; Post ( { this . userId , this . id , this . title , } ) ; factory Post . fromJSON ( Map < String , dynamic > json ) { return Post ( id : json [ 'id' ] , userId : json [ 'userId' ] , title : json [ 'title' ] , ) ; } }

Convert the parsed response to List<Post> rather than List<dynamic>.

class _HttpExampleWidgetState extends State<HttpExampleWidget> { String _text = "Http Example"; void _fetchPosts() async { final response = await http.get('https://jsonplaceholder.typicode.com/posts'); final List<Post> parsedResponse = jsonDecode(response.body) .map<Post>((json) => Post.fromJSON(json)) .toList(); setState(() { _text = response.body; }); } ... } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class _HttpExampleWidgetState extends State < HttpExampleWidget > { String _text = "Http Example" ; void _fetchPosts ( ) async { final response = await http . get ( 'https://jsonplaceholder.typicode.com/posts' ) ; final List < Post > parsedResponse = jsonDecode ( response . body ) . map < Post > ( ( json ) = > Post . fromJSON ( json ) ) . toList ( ) ; setState ( ( ) { _text = response . body ; } ) ; } . . . }

Display List<Post> using a ListView rather than displaying just plain old text. This would require a bit of refactoring and the resulting code is shown below. Go on and try it on your own, don’t just look at the code, practice it!

Replace the String _text with a List<Post> _posts. Use a ListView.builder to display the content in _posts, finally when response is received, update contents of _posts and call setState.

class _HttpExampleWidgetState extends State<HttpExampleWidget> { List<Post> _posts = []; void _fetchPosts() async { final response = await http.get('https://jsonplaceholder.typicode.com/posts'); final List<Post> parsedResponse = jsonDecode(response.body) .map<Post>((json) => Post.fromJSON(json)) .toList(); setState(() { _posts.clear(); _posts.addAll(parsedResponse); }); } @override void initState() { super.initState(); _fetchPosts(); } @override Widget build(BuildContext context) { return Scaffold( body: Center( child: ListView.builder( itemCount: this._posts.length, itemBuilder: (context, index) { final post = this._posts[index]; return ListTile( title: Text(post.title), subtitle: Text('Id: ${post.id} UserId: ${post.userId}'), ); }, ), ), ); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 class _HttpExampleWidgetState extends State < HttpExampleWidget > { List < Post > _posts = [ ] ; void _fetchPosts ( ) async { final response = await http . get ( 'https://jsonplaceholder.typicode.com/posts' ) ; final List < Post > parsedResponse = jsonDecode ( response . body ) . map < Post > ( ( json ) = > Post . fromJSON ( json ) ) . toList ( ) ; setState ( ( ) { _posts . clear ( ) ; _posts . addAll ( parsedResponse ) ; } ) ; } @ override void initState ( ) { super . initState ( ) ; _fetchPosts ( ) ; } @ override Widget build ( BuildContext context ) { return Scaffold ( body : Center ( child : ListView . builder ( itemCount : this . _posts . length , itemBuilder : ( context , index ) { final post = this . _posts [ index ] ; return ListTile ( title : Text ( post . title ) , subtitle : Text ( 'Id: ${post.id} UserId: ${post.userId}' ) , ) ; } , ) , ) , ) ; } }

There you have it, this is how you fetch real data from network!

Making a POST request

I am not going to show you the entire application code for making a POST request, just the method that would make the actual request.

void _createPost() async { final response = await http.post( 'https://jsonplaceholder.typicode.com/posts', body: jsonEncode( { 'title': 'foo', 'body': 'bar', 'userId': 1, }, ), headers: {'Content-Type': "application/json"}, ); final Post parsedResponse = Post.fromJSON(jsonDecode(response.body)); } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 void _createPost ( ) async { final response = await http . post ( 'https://jsonplaceholder.typicode.com/posts' , body : jsonEncode ( { 'title' : 'foo' , 'body' : 'bar' , 'userId' : 1 , } , ) , headers : { 'Content-Type' : "application/json" } , ) ; final Post parsedResponse = Post . fromJSON ( jsonDecode ( response . body ) ) ; }

All you have to do is use the post method from http library instead of the get method (Line 2). post lets you specify body and headers. Headers are of types Map<String, dynamic>, but in case you want to send a body with content-type as application/json, the body needs to be encoded, this is where jsonEncode comes into play, its opposite to jsonDecode, while jsonDecode converts a JSON string response into Map<String, dynamic>, jsonEncode does the opposite, it converts a Map<String, dynamic> to encoded JSON String.

All the code show in this tutorial can be found here.

<< Previous Tutorial – Flutter Crash Course – 5 – Building Lists and Pages