Understand API integration in a flutter in a simple way

Pravin Palkonda
5 min readJan 11, 2023

--

As a beginner, it is easy to learn about the widgets and implement them practically. So after working on the widgets and understanding how they are used, it’s time to understand how APIs are integrated. When we work on any application, it is necessary how to communicate with the server. Any application may not be just a static application, it has to communicate with the backend server to send and receive data. For this, it is necessary to understand how to implement API integration.

Working with API integration may be difficult for a beginner, but if you understand it and practice daily, it will be very easy to work with API.

Let's understand API integration with a small demo application. In this demo application, we will use fake rest API just for understanding purposes.

Head to this web URL to know what response we will get. We will fetch users' data from this URL and display them in our demo application.

In the demo project, we create separate directories for models, screens, constants, and services.

Models : In this folders, files will contain the models of the response

Screens: In this folder we will create UI files

Services : In this folder we create files for API related stuff.

Constants : In this folder we create file for constants.

In flutter, we have packages such as http, Dio, etc for API integration. HTTP package is the most common package which is used for API integration and it is also recommended to start with the HTTP package for the beginning level.

Http package provides different methods to deal with network calls like, get, post, put, delete, etc.

get : It is used to fetch the data which is in json format.

post : It is used to send the data.

put : It is used to update the data.

delete : It is used to delete the data.

Firstly you need to add a package in the pubspec.yaml file. Go to pub.dev and search for http package. Check for the latest version and add it into pubspec.yaml file.

Let's create a file under the constants directory and name it api_constants.dart. Under this file, we will create a class ApiConstants and add an API end URL to it. So it can be accessed easily in the application.

class ApiConstants {
/// Endpoint
static String apiEndUrl = "https://jsonplaceholder.typicode.com/users";
}

Let's create a file under the model's directory and name it user_data_model.dart. In this file, we will add a model of the response we are getting from API.

When we work with APIs we may get a large amount of data so it is good practice to convert the JSON response into a dart object. So handling the data would become easy in the application.

Go to jsonlint and validate your response to check data is valid JSON or not.

If it is valid JSON data go to quicktype.io to generate a model of the response.

import 'dart:convert';

/// model of the response

List<UsersData> usersDataFromJson(String str) =>
List<UsersData>.from(json.decode(str).map((x) => UsersData.fromJson(x)));

String usersDataToJson(List<UsersData> data) =>
json.encode(List<dynamic>.from(data.map((x) => x.toJson())));

class UsersData {
UsersData({
this.id,
this.name,
this.username,
this.email,
this.address,
this.phone,
this.website,
this.company,
});

int? id;
String? name;
String? username;
String? email;
Address? address;
String? phone;
String? website;
Company? company;

factory UsersData.fromJson(Map<String, dynamic> json) => UsersData(
id: json["id"],
name: json["name"],
username: json["username"],
email: json["email"],
address: Address.fromJson(json["address"]),
phone: json["phone"],
website: json["website"],
company: Company.fromJson(json["company"]),
);

Map<String, dynamic> toJson() => {
"id": id,
"name": name,
"username": username,
"email": email,
"address": address?.toJson(),
"phone": phone,
"website": website,
"company": company?.toJson(),
};
}

class Address {
Address({
this.street,
this.suite,
this.city,
this.zipcode,
this.geo,
});

String? street;
String? suite;
String? city;
String? zipcode;
Geo? geo;

factory Address.fromJson(Map<String, dynamic> json) => Address(
street: json["street"],
suite: json["suite"],
city: json["city"],
zipcode: json["zipcode"],
geo: Geo.fromJson(json["geo"]),
);

Map<String, dynamic> toJson() => {
"street": street,
"suite": suite,
"city": city,
"zipcode": zipcode,
"geo": geo?.toJson(),
};
}

class Geo {
Geo({
this.lat,
this.lng,
});

String? lat;
String? lng;

factory Geo.fromJson(Map<String, dynamic> json) => Geo(
lat: json["lat"],
lng: json["lng"],
);

Map<String, dynamic> toJson() => {
"lat": lat,
"lng": lng,
};
}

class Company {
Company({
this.name,
this.catchPhrase,
this.bs,
});

String? name;
String? catchPhrase;
String? bs;

factory Company.fromJson(Map<String, dynamic> json) => Company(
name: json["name"],
catchPhrase: json["catchPhrase"],
bs: json["bs"],
);

Map<String, dynamic> toJson() => {
"name": name,
"catchPhrase": catchPhrase,
"bs": bs,
};
}

Now let's create a file for service under the service directory and name it user_service.dart. In this file, we write code to get user data for the API end URL.

import 'package:api_integration_demo/constants/api_constants.dart';
import 'package:api_integration_demo/models/users_data_model.dart';
import 'package:http/http.dart' as http;

class UserService {
/// get method to get user data
Future<List<UsersData>> getUserData() async {
try {
final response = await http.get(Uri.parse(ApiConstants.apiEndUrl));
if (response.statusCode == 200) {
return usersDataFromJson(response.body);/// return the future list of user's data
}
return [];/// return empty list
} catch (e) {
throw Exception("Failed to fetch data..");
}
}
}

Let's create a file to display a list of users' data in the list and name it home_screen.dart under the screens directory.

import 'package:api_integration_demo/models/users_data_model.dart';
import 'package:api_integration_demo/services/user_service.dart';
import 'package:flutter/material.dart';

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

@override
State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
List<UsersData> userDataList = []; // empty list to add user data
bool isLoading = false; // boolean variable to show loader
@override
void initState() {
// TODO: implement initState
super.initState();

/// to call service to get user data as page gets loaded
getData();
}

getData() async {
try {
isLoading = true;
// calling the service and adding response to user data list
userDataList.addAll(await UserService().getUserData());
isLoading = false;
setState(() {}); //to change the state after receiving the response
} catch (e) {
isLoading = false;
setState(() {});
}
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Home screen"),
),
body: isLoading
? const Center(
child: CircularProgressIndicator(
color: Colors.blue,
),
)
: ListView.builder(
shrinkWrap: true,
itemCount: userDataList.length,
itemBuilder: (context, index) => Container(
margin: const EdgeInsets.all(4.0),
decoration: BoxDecoration(
color: Colors.blueAccent.shade400,
borderRadius: BorderRadius.circular(10.0)),
child: Column(
children: [
_rowData("Name", "${userDataList[index].name}"),
_rowData("Username", "${userDataList[index].username}"),
_rowData("Email", "${userDataList[index].email}"),
_rowData("Phone", "${userDataList[index].phone}"),
_rowData("Website", "${userDataList[index].website}"),
_rowData("Company", "${userDataList[index].company?.name}"),
_rowData("Address",
"${userDataList[index].address?.street}/${userDataList[index].address?.suite}/${userDataList[index].address?.city}/${userDataList[index].address?.zipcode}")
],
),
),
),
);
}

/// functional widget to display the data in row format (for reusable purpose)
_rowData(String title, String value) {
return Row(
children: [
SizedBox(
width: MediaQuery.of(context).size.width * 0.3,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
title,
textAlign: TextAlign.start,
style: const TextStyle(
color: Colors.white, fontWeight: FontWeight.bold),
),
)),
const Text(
": ",
style: TextStyle(fontWeight: FontWeight.w300, color: Colors.white),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: SizedBox(
width: MediaQuery.of(context).size.width * 0.6,
child: Text(
value,
textAlign: TextAlign.start,
style: const TextStyle(
fontWeight: FontWeight.w300, color: Colors.white),
)),
),
],
);
}
}
Video demonstration

The full code is available here.

Let me know in the comments if I need to correct anything. I will try to improve it.

Clap if you like the article. 👏

--

--