Fetch data from the internet

Fetching data from the internet is necessary for most apps. Luckily, Dart and Flutter provide tools, such as the http package, for this type of work.

NOTE

You should avoid directly using dart:io or dart:html to make HTTP requests. Those libraries are platform-dependent and tied to a single implementation.

This recipe uses the following steps:

  1. Add the http package.
  2. Make a network request using the http package.
  3. Convert the response into a custom Dart object.
  4. Fetch and display the data with Flutter.

1. Add the http package

The http package provides the simplest way to fetch data from the internet.

To add the http package as a dependency, run flutter pub add:

flutter pub add http

Import the http package.

import 'package:http/http.dart' as http;

If you are deploying to Android, edit your AndroidManifest.xml file to add the Internet permission.

<!-- Required to fetch data from the internet. -->
<uses-permission android:name="android.permission.INTERNET" />

Likewise, if you are deploying to macOS, edit your macos/Runner/DebugProfile.entitlements and macos/Runner/Release.entitlements files to include the network client entitlement.

<!-- Required to fetch data from the internet. -->
<key>com.apple.security.network.client</key>
<true/>

Testing

For information on how to test this functionality, see the following recipes:

  • Introduction to unit testing
  • Mock dependencies using Mockito

Complete example

import 'dart:async';
import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

Future<Album> fetchAlbum() async {
  final response = await http.get(
    Uri.parse('https://jsonplaceholder.typicode.com/albums/1'),
  );

  if (response.statusCode == 200) {
    // If the server did return a 200 OK response,
    // then parse the JSON.
    return Album.fromJson(jsonDecode(response.body) as Map<String, dynamic>);
  } else {
    // If the server did not return a 200 OK response,
    // then throw an exception.
    throw Exception('Failed to load album');
  }
}

class Album {
  final int userId;
  final int id;
  final String title;

  const Album({required this.userId, required this.id, required this.title});

  factory Album.fromJson(Map<String, dynamic> json) {
    return switch (json) {
      {'userId': int userId, 'id': int id, 'title': String title} => Album(
        userId: userId,
        id: id,
        title: title,
      ),
      _ => throw const FormatException('Failed to load album.'),
    };
  }
}

void main() => runApp(const MyApp());

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  late Future<Album> futureAlbum;

  
  void initState() {
    super.initState();
    futureAlbum = fetchAlbum();
  }

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Fetch Data Example',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
      ),
      home: Scaffold(
        appBar: AppBar(title: const Text('Fetch Data Example')),
        body: Center(
          child: FutureBuilder<Album>(
            future: futureAlbum,
            builder: (context, snapshot) {
              if (snapshot.hasData) {
                return Text(snapshot.data!.title);
              } else if (snapshot.hasError) {
                return Text('${snapshot.error}');
              }

              // By default, show a loading spinner.
              return const CircularProgressIndicator();
            },
          ),
        ),
      ),
    );
  }

}