Popular Searches
Popular Course Categories
Popular Courses

Introduction to APIs in Flutter

Introduction to APIs in Flutter

Flutter APIs & Networking


Introduction to APIs in Flutter


APIs (Application Programming Interfaces) allow a Flutter application to communicate with external servers and services. Through APIs, a mobile, web, or desktop Flutter application can fetch data, send user information, authenticate users, update records, delete data, and communicate with backend systems.


Flutter's official networking documentation recommends the http package as a simple way to make HTTP requests. The package supports Flutter applications across Android, iOS, desktop, and web platforms. Flutter Networking Documentation




1. What is an API?


API stands for Application Programming Interface. An API provides a defined way for one software application to communicate with another application or server.


For example, a Flutter weather application may request weather information from a weather server. The Flutter app sends a request to an API, the server processes the request, and the server returns data, commonly in JSON format.


Simple API Communication


Flutter App
    |
    | HTTP Request
    v
Backend API / Server
    |
    | JSON Response
    v
Flutter App
    |
    v
Display Data



2. Real-World Examples of APIs



  • Login and registration APIs

  • Payment APIs

  • Weather APIs

  • Maps and location APIs

  • Social media APIs

  • E-commerce product APIs

  • News APIs

  • Banking APIs

  • Food delivery APIs

  • Student management APIs


Example


Suppose an e-commerce Flutter application needs to display products. Instead of storing thousands of products directly inside the application, the app can request product data from a backend API.


GET https://example.com/api/products

The server might return:


{
  "products": [
    {
      "id": 1,
      "name": "Laptop",
      "price": 65000
    },
    {
      "id": 2,
      "name": "Mobile Phone",
      "price": 30000
    }
  ]
}



3. Why APIs Are Important in Flutter


Most real-world Flutter applications need data from external systems. APIs make it possible to connect the user interface with a backend.



  • Fetch dynamic data from servers

  • Submit forms to a backend

  • Create user accounts

  • Authenticate users

  • Update existing records

  • Delete records

  • Load products and categories

  • Retrieve user profiles

  • Process application data

  • Synchronize application data with a server




4. How an API Works


API communication generally follows a request-response process.



  1. The Flutter application creates a request.

  2. The request is sent to the API server.

  3. The server receives the request.

  4. The server processes the request.

  5. The server returns a response.

  6. Flutter receives the response.

  7. The response is converted into Dart data.

  8. The UI displays the data.


User Action
     |
     v
Flutter Widget
     |
     v
API Service
     |
     v
HTTP Request
     |
     v
Backend Server
     |
     v
JSON Response
     |
     v
Dart Model
     |
     v
Flutter UI



5. What is HTTP?


HTTP stands for Hypertext Transfer Protocol. It is one of the primary protocols used for communication between applications and web servers.


Flutter applications commonly use HTTP requests to communicate with REST APIs.


Common HTTP Methods









MethodPurposeTypical Example
GETRetrieve dataGet products
POSTCreate or submit dataCreate user
PUTReplace or update dataUpdate profile
PATCHPartially update dataUpdate email
DELETEDelete dataDelete account



6. REST API


REST stands for Representational State Transfer. A REST API organizes backend resources and allows applications to interact with those resources using HTTP methods.


For example:


GET     /users
GET     /users/10
POST    /users
PUT     /users/10
DELETE  /users/10

Here, users is a resource and 10 identifies a particular user.




7. JSON and APIs


JSON stands for JavaScript Object Notation. It is one of the most common formats used to transfer data between Flutter applications and APIs.


Example JSON


{
  "id": 101,
  "name": "Rahul",
  "email": "[email protected]",
  "age": 25
}

Flutter can decode this JSON into Dart objects using the dart:convert library.




8. Adding the HTTP Package


The http package provides convenient APIs for making HTTP requests from Flutter. Flutter's documentation demonstrates adding it with flutter pub add http. http Package on pub.dev


Installation


flutter pub add http

Then import the package:


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



9. Making a Simple GET Request


A GET request is used when the application wants to retrieve information from a server.


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

Future fetchData() async {
  final response = await http.get(
    Uri.parse('https://jsonplaceholder.typicode.com/posts/1'),
  );

  print(response.statusCode);
  print(response.body);
}


The http.get() method returns a Future containing an HTTP response. Flutter's official example uses the same basic approach for fetching remote data. Fetch Data from the Internet




10. Understanding Future and async/await


Network requests take time because data must travel between the application and server. Dart uses Future, async, and await to work with asynchronous operations.


Example


Future loadData() async {
  print('Request started');

  final response = await http.get(
    Uri.parse('https://jsonplaceholder.typicode.com/posts/1'),
  );

  print(response.body);
  print('Request completed');
}


async allows a function to perform asynchronous work, while await waits for the asynchronous operation to complete.




11. Understanding HTTP Response


An HTTP response contains information returned by the server.


Important Response Properties



  • statusCode - HTTP status code returned by the server.

  • body - Response data, usually a String containing JSON.

  • headers - Metadata about the response.

  • request - Information about the original request where available.


Example


final response = await http.get(
  Uri.parse('https://jsonplaceholder.typicode.com/posts/1'),
);

print(response.statusCode);
print(response.body);
print(response.headers);




12. HTTP Status Codes


Status codes help Flutter understand whether an API request succeeded or failed.














Status CodeMeaning
200OK - request succeeded
201Created - new resource created
204No Content
400Bad Request
401Unauthorized
403Forbidden
404Not Found
422Validation or semantic error
500Internal Server Error
503Service Unavailable

Checking a Status Code


if (response.statusCode == 200) {
  print('Request successful');
} else {
  print('Request failed');
}



13. Decoding JSON in Flutter


The dart:convert library provides jsonDecode() for converting a JSON string into Dart data structures.


import 'dart:convert';

final data = jsonDecode(response.body);

print(data);


If the JSON contains an object, it can commonly be represented as Map.


final Map data =
    jsonDecode(response.body) as Map;

print(data['title']);




14. Converting API JSON into a Dart Model


Using model classes makes API data easier to manage and provides better type safety than working with raw maps throughout the application.


Example JSON


{
  "id": 1,
  "name": "Flutter Course",
  "price": 9999
}

Dart Model


class Course {
  final int id;
  final String name;
  final double price;

  Course({
    required this.id,
    required this.name,
    required this.price,
  });

  factory Course.fromJson(Map json) {
    return Course(
      id: json['id'] as int,
      name: json['name'] as String,
      price: (json['price'] as num).toDouble(),
    );
  }
}




15. Complete API Fetch Example


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

class Post {
  final int id;
  final String title;
  final String body;

  Post({
    required this.id,
    required this.title,
    required this.body,
  });

  factory Post.fromJson(Map json) {
    return Post(
      id: json['id'] as int,
      title: json['title'] as String,
      body: json['body'] as String,
    );
  }
}

Future fetchPost() async {
  final response = await http.get(
    Uri.parse('https://jsonplaceholder.typicode.com/posts/1'),
  );

  if (response.statusCode == 200) {
    final data = jsonDecode(response.body) as Map;
    return Post.fromJson(data);
  }

  throw Exception('Failed to load post');
}




16. Displaying API Data with FutureBuilder


FutureBuilder is useful for displaying asynchronous API data because the UI can react to loading, success, and error states. Flutter's official networking recipe uses this pattern. Flutter FutureBuilder API Example


FutureBuilder(
  future: fetchPost(),
  builder: (context, snapshot) {
    if (snapshot.connectionState == ConnectionState.waiting) {
      return const CircularProgressIndicator();
    }

    if (snapshot.hasError) {
      return Text('Error: ${snapshot.error}');
    }

    if (snapshot.hasData) {
      return Column(
        children: [
          Text(snapshot.data!.title),
          Text(snapshot.data!.body),
        ],
      );
    }

    return const Text('No data available');
  },
)




17. Important: Do Not Make API Calls Directly Inside build()


An API call should generally not be created directly inside the build() method because Flutter may rebuild widgets many times. Creating a new Future during every build can cause repeated network requests.


Incorrect Approach


@override
Widget build(BuildContext context) {
  return FutureBuilder(
    future: fetchPost(),
    builder: (context, snapshot) {
      return const Text('Data');
    },
  );
}

Better Approach


late Future postFuture;

@override
void initState() {
  super.initState();
  postFuture = fetchPost();
}

@override
Widget build(BuildContext context) {
  return FutureBuilder(
    future: postFuture,
    builder: (context, snapshot) {
      if (snapshot.hasData) {
        return Text(snapshot.data!.title);
      }

      if (snapshot.hasError) {
        return Text('Error: ${snapshot.error}');
      }

      return const CircularProgressIndicator();
    },
  );
}




18. Sending Data with POST


A POST request is commonly used when an application needs to send data to a server or create a resource.


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

Future createPost() async {
  final response = await http.post(
    Uri.parse('https://jsonplaceholder.typicode.com/posts'),
    headers: {
      'Content-Type': 'application/json',
    },
    body: jsonEncode({
      'title': 'Flutter API',
      'body': 'Learning API integration',
      'userId': 1,
    }),
  );

  print(response.statusCode);
  print(response.body);
}


Flutter's official networking documentation demonstrates using http.post() together with jsonEncode() for sending JSON data. Send Data to the Internet




19. Updating Data with PUT


PUT is commonly used to update an existing resource.


Future updatePost() async {
  final response = await http.put(
    Uri.parse('https://jsonplaceholder.typicode.com/posts/1'),
    headers: {
      'Content-Type': 'application/json',
    },
    body: jsonEncode({
      'title': 'Updated Flutter Post',
      'body': 'Updated content',
      'userId': 1,
    }),
  );

  print(response.statusCode);
  print(response.body);
}


Flutter Update Data Documentation




20. Deleting Data with DELETE


DELETE is used to request deletion of a resource on the server.


Future deletePost() async {
  final response = await http.delete(
    Uri.parse('https://jsonplaceholder.typicode.com/posts/1'),
  );

  if (response.statusCode == 200 || response.statusCode == 204) {
    print('Post deleted');
  } else {
    print('Delete failed');
  }
}


Flutter Delete Data Documentation




21. API Headers


Headers provide additional information about an HTTP request. Common headers include Content-Type, Accept, and Authorization.


final response = await http.get(
  Uri.parse('https://example.com/api/users'),
  headers: {
    'Accept': 'application/json',
  },
);

POST Header Example


headers: {
  'Content-Type': 'application/json',
  'Accept': 'application/json',
}



22. Authentication and Authorization


Many APIs require authentication before allowing access to protected resources. A common approach is to send an access token through the Authorization header.


final response = await http.get(
  Uri.parse('https://example.com/api/profile'),
  headers: {
    'Authorization': 'Bearer YOUR_ACCESS_TOKEN',
    'Accept': 'application/json',
  },
);

The exact authentication mechanism depends on the backend API. Flutter's official documentation demonstrates adding authorization headers to requests. Authenticated Requests in Flutter




23. API Request with Query Parameters


Query parameters are commonly used to filter, search, sort, or paginate API results.


Example URL


https://example.com/products?category=mobile&page=1

Using Uri


final uri = Uri.https(
  'example.com',
  '/products',
  {
    'category': 'mobile',
    'page': '1',
  },
);

final response = await http.get(uri);




24. Handling API Errors


Network operations can fail for many reasons, including unavailable servers, invalid requests, authentication failures, connection problems, and malformed responses.


Future fetchPost() async {
  try {
    final response = await http.get(
      Uri.parse('https://jsonplaceholder.typicode.com/posts/1'),
    );

    if (response.statusCode == 200) {
      final json = jsonDecode(response.body);
      return Post.fromJson(json);
    }

    throw Exception(
      'Server returned status ${response.statusCode}',
    );
  } catch (e) {
    throw Exception('Unable to fetch post: $e');
  }
}




25. Loading, Success, and Error States


A good Flutter application should clearly handle different API states.








StateUI Example
LoadingCircularProgressIndicator
SuccessDisplay API data
ErrorShow error message and retry button
EmptyShow "No data found"

Example


if (loading) {
  return const CircularProgressIndicator();
}

if (errorMessage != null) {
  return Text(errorMessage!);
}

if (items.isEmpty) {
  return const Text('No data found');
}

return ListView.builder(
  itemCount: items.length,
  itemBuilder: (context, index) {
    return Text(items[index].name);
  },
);




26. Fetching a List from an API


Many APIs return arrays containing multiple objects.


Example JSON


[
  {
    "id": 1,
    "name": "Laptop"
  },
  {
    "id": 2,
    "name": "Mobile"
  },
  {
    "id": 3,
    "name": "Tablet"
  }
]

Dart Code


Future> fetchProducts() async {
  final response = await http.get(
    Uri.parse('https://example.com/api/products'),
  );

  if (response.statusCode == 200) {
    final List data = jsonDecode(response.body);

    return data
        .map((item) => Product.fromJson(item))
        .toList();
  }

  throw Exception('Failed to load products');
}




27. Displaying API List Data with ListView.builder


FutureBuilder>(
  future: fetchProducts(),
  builder: (context, snapshot) {
    if (snapshot.connectionState == ConnectionState.waiting) {
      return const Center(
        child: CircularProgressIndicator(),
      );
    }

    if (snapshot.hasError) {
      return Center(
        child: Text('Error: ${snapshot.error}'),
      );
    }

    final products = snapshot.data ?? [];

    if (products.isEmpty) {
      return const Center(
        child: Text('No products found'),
      );
    }

    return ListView.builder(
      itemCount: products.length,
      itemBuilder: (context, index) {
        final product = products[index];

        return ListTile(
          title: Text(product.name),
          subtitle: Text('₹${product.price}'),
        );
      },
    );
  },
)




28. API Service Class


For larger applications, API-related code should be separated from UI code. An API service class can contain network operations.


class ApiService {
  final String baseUrl = 'https://example.com/api';

  Future> getProducts() async {
    final response = await http.get(
      Uri.parse('$baseUrl/products'),
    );

    if (response.statusCode != 200) {
      throw Exception('Failed to load products');
    }

    final List data = jsonDecode(response.body);

    return data
        .map((item) => Product.fromJson(item))
        .toList();
  }
}


This approach keeps networking logic separate from widgets and makes the project easier to maintain.




29. Recommended Flutter API Project Structure


lib/
├── models/
│   └── product.dart
├── services/
│   └── api_service.dart
├── screens/
│   └── products_screen.dart
├── widgets/
│   └── product_card.dart
└── main.dart


  • models: Contains Dart classes representing API data.

  • services: Contains API and networking logic.

  • screens: Contains application screens.

  • widgets: Contains reusable UI components.

  • main.dart: Application entry point.




30. API Base URL


When an application uses many endpoints from the same backend, define a base URL.


class ApiConstants {
  static const String baseUrl = 'https://example.com/api';

  static const String products = '$baseUrl/products';
  static const String users = '$baseUrl/users';
  static const String login = '$baseUrl/login';
}


This avoids repeating URLs throughout the application.




31. API Timeout


Network requests can take longer than expected. A timeout can prevent the application from waiting indefinitely.


final response = await http
    .get(Uri.parse('https://example.com/api/products'))
    .timeout(
      const Duration(seconds: 10),
    );

Handling Timeout


try {
  final response = await http
      .get(Uri.parse('https://example.com/api/products'))
      .timeout(const Duration(seconds: 10));

  print(response.body);
} catch (e) {
  print('Request failed or timed out');
}




32. Internet Permission on Android


Android applications that access the internet need the appropriate internet permission in the Android manifest. Flutter's official documentation shows the following permission for Android networking. Flutter Networking



For a standard Flutter Android project, check the Android manifest configuration if network requests are not working as expected.




33. API Security Basics


API integration should be designed with security in mind.



  • Use HTTPS instead of unencrypted HTTP.

  • Do not hard-code sensitive production secrets in the application.

  • Use secure authentication mechanisms provided by the backend.

  • Validate data on the server.

  • Handle expired authentication tokens.

  • Do not expose private API keys unnecessarily.

  • Return only the data required by the application.




34. API Pagination


When an API contains thousands of records, loading everything at once can be inefficient. APIs often provide pagination.


Example


GET /products?page=1&limit=20

The Flutter application can request the next page when the user scrolls near the bottom of a list.


Basic Pagination Concept


Page 1 → Products 1-20
Page 2 → Products 21-40
Page 3 → Products 41-60
Page 4 → Products 61-80



35. API Search


Search functionality can be implemented by passing the search keyword to the API.


final uri = Uri.https(
  'example.com',
  '/api/products',
  {
    'search': 'laptop',
  },
);

final response = await http.get(uri);




36. API Filtering


Filtering allows users to request only specific data.


final uri = Uri.https(
  'example.com',
  '/api/products',
  {
    'category': 'electronics',
    'minPrice': '10000',
    'maxPrice': '50000',
  },
);

final response = await http.get(uri);




37. Refreshing API Data


A common Flutter pattern is RefreshIndicator, which allows users to pull down to refresh content.


RefreshIndicator(
  onRefresh: () async {
    setState(() {
      productsFuture = apiService.getProducts();
    });

    await productsFuture;
  },
  child: ListView.builder(
    itemCount: products.length,
    itemBuilder: (context, index) {
      return Text(products[index].name);
    },
  ),
)




38. Using an HTTP Client


For applications making multiple requests, an http.Client can be useful instead of creating isolated requests everywhere. The official http package documentation describes Client as a way to keep a persistent connection and improve organization for repeated requests. Dart HTTP API Documentation


final client = http.Client();

try {
  final response = await client.get(
    Uri.parse('https://example.com/api/products'),
  );

  print(response.body);
} finally {
  client.close();
}




39. API Data Flow in a Flutter Application


User
  |
  v
Flutter Screen
  |
  v
API Service
  |
  v
HTTP Request
  |
  v
Backend API
  |
  v
Database
  |
  v
Backend API
  |
  v
JSON Response
  |
  v
Dart Model
  |
  v
State Management
  |
  v
Flutter UI



40. Complete Mini API Example


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

class Post {
  final int id;
  final String title;

  Post({
    required this.id,
    required this.title,
  });

  factory Post.fromJson(Map json) {
    return Post(
      id: json['id'] as int,
      title: json['title'] as String,
    );
  }
}

Future> fetchPosts() async {
  final response = await http.get(
    Uri.parse('https://jsonplaceholder.typicode.com/posts'),
  );

  if (response.statusCode != 200) {
    throw Exception('Failed to load posts');
  }

  final List data = jsonDecode(response.body);

  return data
      .map((item) => Post.fromJson(item))
      .toList();
}

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

  @override
  State createState() => _PostsScreenState();
}

class _PostsScreenState extends State {
  late Future> postsFuture;

  @override
  void initState() {
    super.initState();
    postsFuture = fetchPosts();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('API Posts'),
      ),
      body: FutureBuilder>(
        future: postsFuture,
        builder: (context, snapshot) {
          if (snapshot.connectionState == ConnectionState.waiting) {
            return const Center(
              child: CircularProgressIndicator(),
            );
          }

          if (snapshot.hasError) {
            return Center(
              child: Text('Error: ${snapshot.error}'),
            );
          }

          final posts = snapshot.data ?? [];

          if (posts.isEmpty) {
            return const Center(
              child: Text('No posts available'),
            );
          }

          return ListView.builder(
            itemCount: posts.length,
            itemBuilder: (context, index) {
              final post = posts[index];

              return ListTile(
                leading: CircleAvatar(
                  child: Text('${post.id}'),
                ),
                title: Text(post.title),
              );
            },
          );
        },
      ),
    );
  }
}




41. Common Mistakes When Working with APIs



  • Making API calls repeatedly inside build().

  • Ignoring HTTP status codes.

  • Not handling network errors.

  • Not displaying a loading state.

  • Not handling empty API responses.

  • Assuming every response contains valid JSON.

  • Putting all networking logic directly inside UI widgets.

  • Hard-coding sensitive credentials.

  • Loading very large datasets without pagination.

  • Ignoring authentication expiration.




42. Best Practices for API Integration



  1. Keep API logic separate from UI code.

  2. Create model classes for API responses.

  3. Use async and await for asynchronous operations.

  4. Check HTTP status codes.

  5. Handle loading, success, empty, and error states.

  6. Use HTTPS for production APIs.

  7. Use authentication securely.

  8. Use pagination for large datasets.

  9. Use reusable API service classes.

  10. Use appropriate timeouts.

  11. Test API services independently.

  12. Keep API URLs and configuration centralized.




43. API Integration Workflow



  1. Understand the API documentation.

  2. Identify the endpoint.

  3. Identify the HTTP method.

  4. Identify required parameters.

  5. Identify required headers.

  6. Identify authentication requirements.

  7. Add the http package.

  8. Create the API service.

  9. Make the HTTP request.

  10. Check the status code.

  11. Decode JSON.

  12. Convert JSON into Dart models.

  13. Display the data in Flutter widgets.

  14. Handle loading and error states.

  15. Test successful and failed requests.




44. Quick Revision Table



















ConceptPurpose
APIAllows applications to communicate with external systems
HTTPProtocol used for web communication
GETRetrieve data
POSTCreate/send data
PUTUpdate/replace data
PATCHPartially update data
DELETEDelete data
JSONCommon format for API data
FutureRepresents asynchronous work
asyncMarks a function as asynchronous
awaitWaits for an asynchronous operation
jsonDecode()Converts JSON text into Dart data
FutureBuilderBuilds UI based on asynchronous Future state
http.ClientReusable HTTP client for multiple requests
Status CodeIndicates the result of an HTTP request



45. Practice Exercises



  1. Create a Flutter app that fetches a single post from an API.

  2. Create a product model using fromJson().

  3. Display a list of products using ListView.builder.

  4. Create a POST request for submitting a registration form.

  5. Create a PUT request for updating a user profile.

  6. Create a DELETE request for deleting a product.

  7. Add loading and error states to an API screen.

  8. Add pull-to-refresh functionality.

  9. Implement API search using query parameters.

  10. Implement pagination for a large API response.




46. Interview Questions


Q1. What is an API?


An API is an interface that allows different software systems to communicate with each other.


Q2. What package is commonly used for HTTP requests in Flutter?


The http package is a commonly used package for making HTTP requests in Flutter.


Q3. What is the difference between GET and POST?


GET is generally used to retrieve data, while POST is generally used to send data or create a resource.


Q4. What is JSON?


JSON is a lightweight text-based data format commonly used for exchanging structured data between applications and servers.


Q5. Why should API calls not normally be created directly inside build()?


Because widgets can rebuild frequently, creating a new API Future during every build can cause repeated network requests.


Q6. Why are model classes useful?


Model classes provide structured, typed representations of API data and make application code easier to maintain.


Q7. What is FutureBuilder?


FutureBuilder is a Flutter widget that builds UI based on the current state of a Future, such as loading, successful completion, or an error.


Q8. What does status code 200 mean?


HTTP 200 generally indicates that the request was successfully processed.




47. Official Resources





48. JustAcademy Flutter Resources


For structured Flutter learning and course information, visit the JustAcademy Flutter Training Course.


You can also register for a course demo through the Register for Flutter Course Demo page.




Conclusion


APIs are an essential part of modern Flutter development because they connect the Flutter interface with backend services and external data sources. By learning HTTP methods, JSON parsing, Dart models, asynchronous programming, authentication, error handling, and API service architecture, developers can build Flutter applications that work with real-world data.


The basic API workflow to remember is:


Flutter UI
   ↓
API Service
   ↓
HTTP Request
   ↓
Backend API
   ↓
JSON Response
   ↓
Dart Model
   ↓
Flutter UI

Once this workflow is understood, developers can move from simple API requests to complete applications involving authentication, CRUD operations, search, filtering, pagination, and real-time backend communication.


whatsapp