REST API in Flutter: A beginner’s guide to fetching data
This Flutter API tutorial shows you how to fetch data from a REST API and display it in your app. You’ll build a working posts application using the http package, learning to make API requests, parse JSON responses, and create dynamic UIs with live data.
Here’s what you’ll accomplish:
- Set up a Flutter project with the http package for API integration
- Create data models to parse JSON from the API endpoint
- Build a service layer to handle API requests
- Display fetched data in a scrollable ListView
A REST API (Representational State Transfer) enables applications to communicate with servers and receive structured data in JSON format. Popular apps like X and Instagram use REST APIs to constantly refresh content. We’ll recreate this by connecting to JSONPlaceholder API, a free testing service that provides mock data for learning.
Note: This Flutter API tutorial introduces three foundational concepts: asynchronous data fetching, JSON parsing, and reactive UI updates. These principles form the backbone of every data-driven app you’ll build.
Prerequisites for Flutter API integration
Before beginning, make sure our system includes:
- Flutter SDK installed (check by running
flutter doctor), check our How to install Flutter guide for this. - A code editor such as VS Code or Android Studio
- Basic understanding of widgets, layouts, and Dart syntax.
- Understanding state management in Flutter.
Building the Flutter API app
Project setup
We’ll begin by creating a new Flutter project. In a terminal or IDE, run:
flutter create api_app
Once created, open the project and organize its structure for scalability. Inside the lib directory, create subfolders for models, services, and UI:

Now, open pubspec.yaml and add the following dependency to enable API communication:
dependencies:flutter:sdk: flutterhttp: ^1.2.1
Then run:
flutter pub get
This installs the HTTP package, which allows Flutter to make network requests. Before we start with the implementation, let’s see how exactly the process of rendering the API will look like.

Note: Defining a clear project architecture early keeps large applications organized. As our app grows, this foundation will make debugging and feature updates much simpler.
Understanding the API and creating the model
We’ll use the JSONPlaceholder API, which provides mock endpoints for posts, comments, and users. Our endpoint for this project will be:
https://jsonplaceholder.typicode.com/posts
Opening this URL in a browser shows a JSON array similar to:
{"userId": 1,"id": 1,"title": "Sample Title","body": "This is a sample post body."}
To work with this data in Flutter, we’ll need to define a Dart model. A model that can convert raw JSON into structured objects that are easier to manipulate.
Let’s create the model file. Inside the models folder, create a file named post.dart and add the following code inside it:
import 'dart:convert';List<Post> postFromJson(String str) =>List<Post>.from(json.decode(str).map((x) => Post.fromJson(x)));class Post {int userId;int id;String title;String? body;Post({required this.userId,required this.id,required this.title,this.body,});factory Post.fromJson(Map<String, dynamic> json) => Post(userId: json["userId"],id: json["id"],title: json["title"],body: json["body"],);}
In this code, the Post class defines the structure of a single post object.
- The
userIdandiduniquely identify the post. - The
titleandbodyfields store the content. - The
postFromJson()function converts a JSON string into a list ofPostobjects.
Creating a model ensures our app can handle structured, type-safe data instead of raw strings or maps.
Note: For larger APIs, using a generator such as quicktype.io can automatically create Dart models from JSON.
Creating the service layer
To keep the code clean, we’ll separate our API logic into a dedicated service layer. This class handles all HTTP requests to the REST API, keeping the UI focused solely on presentation. Inside the services folder, create a file named remote_service.dart and add the following code to it:
import 'package:http/http.dart' as http;import 'package:api_tutorial/models/post.dart';class RemoteService {Future<List<Post>?> getPosts() async {var client = http.Client();var uri = Uri.parse('https://jsonplaceholder.typicode.com/posts');var response = await client.get(uri);if (response.statusCode == 200) {var json = response.body;return postFromJson(json);}return null;}}
In this code:
- The class
RemoteServicecontains the methodgetPosts(), which uses thehttpclient to make a GET request. - When the response returns with status code 200 (success), the raw JSON is passed through our
postFromJson()method to produce a list ofPostobjects. - The
Future<List<Post>?>ensures the function can run asynchronously without blocking the UI.
Here, what we have done is separate API calls into a service layer, which is a best practice. It improves readability, testing, and reusability, especially when multiple screens require the same data source.
Building the UI (the view)
With our data ready, it’s time to display it on screen. Let’s create our main screen where posts will appear. Inside the views folder, create a new file named home_page.dart and then add the following code to it:
import 'package:flutter/material.dart';import 'package:api_tutorial/models/post.dart';import 'package:api_tutorial/services/remote_service.dart';class HomePage extends StatefulWidget {const HomePage({super.key});@overrideState<HomePage> createState() => _HomePageState();}class _HomePageState extends State<HomePage> {List<Post>? posts;var isLoaded = false;@overridevoid initState() {super.initState();getData();}getData() async {posts = await RemoteService().getPosts();if (posts != null) {setState(() {isLoaded = true;});}}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text("Flutter API Example")),body: Visibility(visible: isLoaded,replacement: const Center(child: CircularProgressIndicator()),child: ListView.builder(itemCount: posts?.length,itemBuilder: (context, index) {return Container(padding: const EdgeInsets.all(16),child: Column(crossAxisAlignment: CrossAxisAlignment.start,children: [Text(posts![index].title,maxLines: 2,overflow: TextOverflow.ellipsis,style: const TextStyle(fontSize: 20,fontWeight: FontWeight.bold,),),const SizedBox(height: 8),Text(posts![index].body ?? '',maxLines: 3,overflow: TextOverflow.ellipsis,),],),);},),),);}}
Here’s what’s happening in this code:
The class
_HomePageStateholds two key variables:List<Post>? posts;to store the databool isLoaded = false;to track loading state
In
initState(), we callgetData(), which fetches posts from the API. Once data is received,setState()updatesisLoadedtotrue, triggering a UI rebuild.The
Visibilitywidget toggles between a loading spinner and a list of posts once data is ready.
Note: Always provide feedback during asynchronous operations. A loading indicator prevents confusion and ensures a better user experience.
Connecting the API service to Flutter app
Finally, we’ll tie everything together in our main app entry file. Open the main.dart file and replace its content with:
import 'package:flutter/material.dart';import 'views/home_page.dart';void main() {runApp(const MyApp());}class MyApp extends StatelessWidget {const MyApp({super.key});@overrideWidget build(BuildContext context) {return MaterialApp(debugShowCheckedModeBanner: false,title: 'Flutter API Tutorial',theme: ThemeData(primarySwatch: Colors.blue,),home: const HomePage(),);}}
Running the app (flutter run) should now launch a clean interface that lists post titles and bodies fetched directly from the API.

Note: If your app displays an empty screen, check the API endpoint, internet connection, or ensure the
httpdependency was properly installed.
Conclusion
We’ve successfully built a functional Flutter app that fetches live data, parses JSON into Dart objects, and displays it dynamically. This Flutter API tutorial covered essential concepts for building data-driven mobile apps.
Key skills you learned:
- Making asynchronous HTTP requests to REST APIs
- Handling JSON data using model classes
- Managing state during API calls
- Structuring Flutter apps with service layers
- Implementing error handling for network failures
For a deeper dive into backend fundamentals, explore the free Introduction to Backend Course on Codecademy.
Frequently asked questions
1. What is a REST API, and why do we use it in Flutter apps?
A REST API (Representational State Transfer API) allows applications to communicate with external servers using HTTP requests. In Flutter, we use REST APIs to fetch real-time data such as posts, weather updates, or user information and display it dynamically in our app’s UI. This makes our application interactive and data-driven instead of being static.
2. Why do we need a model class to handle API data?
A model class converts raw JSON data from an API into structured Dart objects. This helps maintain type safety, makes the code cleaner, and simplifies the process of displaying and manipulating data in the UI. Without models, handling nested JSON structures would become error-prone and harder to maintain.
3. How do I use API in Flutter?
To use an API in Flutter, add the http package to your pubspec.yaml file, then create model classes for your data structure. Use async/await with methods like http.get() or http.post() to make API requests. Parse the JSON response into your Dart objects using the model’s fromJson() method, then display the data in your UI using widgets like ListView or FutureBuilder.
4. Which API is best for Flutter?
The best API package for Flutter depends on your project requirements. The http package is ideal for simple REST API calls and is officially recommended by the Flutter team. For more advanced features like request interceptors, timeout handling, and better error management, the dio package is excellent. If you’re working with GraphQL APIs, use the graphql_flutter package instead.
5. How to call HTTP in Flutter?
To call HTTP in Flutter, first import the http package with import 'package:http/http.dart' as http;. Then create an async function and use await http.get(Uri.parse('your-api-url')) to make a GET request. Check the response.statusCode to verify success (200 means successful), then parse the response.body which contains the JSON data. Always wrap HTTP calls in try-catch blocks to handle network errors.
'The Codecademy Team, composed of experienced educators and tech experts, is dedicated to making tech skills accessible to all. We empower learners worldwide with expert-reviewed content that develops and enhances the technical skills needed to advance and succeed in their careers.'
Meet the full teamRelated articles
- Article
Flutter Tutorial for Beginners: Build Your First App
Learn Flutter app development with this beginner tutorial. Build a habit tracker app with UI design, state management, and data storage. - Article
How to Install Flutter on Windows, Mac, & Linux
Learn how to install Flutter on Windows, macOS, and Linux. Complete setup guide for Android Studio and Xcode. - Article
Check Your Flutter Installation with Flutter Doctor
This article will describe using the Flutter Doctor tool to verify our Flutter setup.
Learn more on Codecademy
- This course will introduce learners to the Flutter framework with interactive lessons on basic app components.
- Beginner Friendly.1 hour
- Explore OpenAI’s API and learn how to write more effective generative AI prompts that help improve your results.
- Beginner Friendly.< 1 hour
- Learn how to develop APIs using Swagger tooling and the OpenAPI specification.
- With Certificate
- Intermediate.< 1 hour