Use get package for state management and API integration

Pravin Palkonda
9 min readFeb 21, 2023

--

While working with the flutter application there comes a time to manage the state of the application. Flutter provides us with various approaches for managing the state of the application by using packages such as Provider, Bloc, MobX, Riverpod, etc.

Why state management is necessary?

Suppose you want to change the data on one page and that change should be updated across the application, it would become difficult to manage. This type of issue can be resolved using state management.

For example: Consider an application that consists main page and a profile page. On these pages, the user name is displayed. If the user edits the profile details and saves them. These changes should be reflected on the main page as well because we are displaying the user name on it. This kind of situation can be easily handled using state management.

GetX

There are lots of other packages which are used for state management but GetX is the most discussed topic in the development of the flutter applications. It is the most liked package available on the pub.dev. It is a fast, stable,extra-lightweight, and powerful framework. GetX helps us to call any widget without any context and it also helps us to easily navigate between the screens with minimal use of code.

Also, GetX has a large community to come up with any solution to the issues faced while the development. It is the most reliable and is used by many organizations for developing the application. So we can easily trust the package and use it for development.

GetX mostly has a three principles

Performance: GetX is mostly focused on performance as it does not use streams and change notifier. Also, there will be less use of statefull widgets due to which the performance of the application increases. Statefull widget always rebuilds the widget even if there is a small change that will not be used in the case of GetX.

Productivity: By using GetX we can easily achieve anything with minimal use of code which helps us to create applications fastly. Also, the syntax used in the GetX is less and the productivity of the application increases higher.

Organization: GetX has a large community and also there are lots of contributors to support the package. So a developer can easily trust the package and start using the features provided by it.

GetX has three pillars of state management

Reactive state managementWe can easily listen to any changes by using observable. We don't need to create any separate class for managing the state of the application. We need to use .obs to make them (variable or any data) observable and the Obx widget to display them on the screen.

Route management We can easily navigate between the screen without using context.

For example :

Navigate to a new screen → Get.to(NewScreen())

Get back to the previous screen → Get.back().

All the other properties of navigations are handled easily using get with minimal use of code.

Dependency management → Get has a simple and powerful dependency management. We just need to create the instance of the controller and we are ready to use all the properties defined in the controller with just a single line.

For example :

final UsersController uc = Get.put(UsersController());

In this aticle, we will learn an api integration using get and http package. We will also maintain the count value of the variable and display it across the application.

Firstly, we need to install the dependencies of the get package and http package available in the pub.dev. So go to pubspec.yaml file and add these dependecies.

pubspec.yaml file

In the main file just return the GetMaterialApp instead of MaterialApp.

This will ensure that we can use all the features of get like navigation, calling widgets without context, etc. In the main file, we also add a theme for the appbar and we will use material 3 by making it true.

This also ensures that the controller is created and initialized whenever the controller is being used and destroyed if it is not used.

GetMaterialApp(
title: 'GetX Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
appBarTheme: AppBarTheme(
centerTitle: true,
titleTextStyle:
const TextStyle(color: Colors.white, fontSize: 20.0),
color: Colors.deepOrange.shade400),
useMaterial3: true,
colorSchemeSeed: Colors.deepOrange),
home: HomePage(),
);

Now create a separate folder for each file that will be used in the application.

pages → Here we will create a file which will displayed as a page in the application.

constants → Here we will create the file which will contain, constants used in the application.

controller → Here we will create the controller file which will separate the business logic from the pages.

api → Here we will create a file which will handle the api request and parse the json data.

models → Here we will create the model file to parse the json data.

folder structure

In the constants directory create a dart file and name it as app_constants.dart. Here we will create AppConstants class and define the constants used in the application.

Here we are defining the URL through which we will get the user data. So it can be accessed easily across the application.

For example : We can get the user api url by simply calling AppConstants.userApiUrl.

class AppConstants {
static const String userApiUrl = "https://jsonplaceholder.typicode.com/users";
}

Now we need to create the model to parse the data which will receive from the user api url.

For this let's create a dart file in the model folder and name it user_model.dart.

We can easily create the model by using the quicktype.io tool. Just copy the response from the user api url and paste the response to generate a model in dart. Copy the dart code generated by the quick type and paste it into the user_model.dart file.

import 'dart:convert';
// To parse this JSON data, do
//
// final usersModel = usersModelFromJson(jsonString);

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

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

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

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

factory UsersModel.fromJson(Map<String, dynamic> json) => UsersModel(
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(),
};
}

Lets fetch the data from the user api by simply making http get request.

Create a dart file in api folder and name it user_api.dart. In this file, we will make a get api request which will fetch the user data from the user api url. Here we will parse the api response to the user model if the status code is 200.

In this file, import the user model,app_constants, and http package.

/// A user api call which will handle all the api request for user details
class UsersApi {
// An api call to get user details which will returns the
// list of user data
Future<List<UsersModel>> getUsers() async {
Response response = await get(Uri.parse(AppConstants.userApiUrl));
if (response.statusCode == 200) {
// parsing the response to user model
return usersModelFromJson(response.body);
} else {
throw Exception(response.reasonPhrase);
}
}
}

Now we need to handle the business logic to call the api we created to display the data on the screen.

Create a dart file in the controller folder and name it user_controller.dart. In this file, we will extend the class with GetxController. By extending the to GetxController we can override the method which is available in getx. We can use functions like onInit(),onClose(), etc.

import 'package:get/get.dart';
import 'package:get_demo/api/user_api.dart';
import 'package:get_demo/model/users_model.dart';

/// A class where we write business logic and place all variable,methods and
/// controller inside it.
class UsersController extends GetxController {
// variable to check the api is loading or not
var isLoading = false.obs;
// An empty list to add the list of user details
// Here we will continuously observe the changes in the list by using .obs
var usersList = <UsersModel>[].obs;

// Initializing the value to count variable
// and observing it continuously
var count = 0.obs;

@override
void onInit() {
// calling the fetch user function as the controller gets initialized
fetchUser();
super.onInit();
}

// Function to call user api and assign the data to user list
fetchUser() async {
try {
isLoading(true);
var users = await UsersApi().getUsers();
usersList.assignAll(users);
} finally {
isLoading(false);
}
}

// Function to increment the value of count by 1
incrementCount() {
count++;
}

// Function to decrement the value of count by 1
decrementCount() {
count--;
}
}

Now its time to display the data received from the api on screen

We will create two pages. On the first page, we will display a button to increment the count value and another button that will navigate to another page where a list of user details is displayed, in this page, we will also have a button to decrease the count value. We will display a count value in the app bar.

Create a file in the pages folder and name it as home_page.dart. This page will contain two buttons and the value of the count is displayed in the app bar.

We will use obx to display the count value as its value gets updated.

The main advantage of using obx is that it will automatically update the view as the data gets updated. We just need to wrap the widget with obx.

import 'package:flutter/material.dart';
import 'package:get/get.dart';

import 'package:get_demo/controller/users_controller.dart';
import 'package:get_demo/pages/api_demo_page.dart';

class HomePage extends StatelessWidget {
HomePage({super.key});

// Creating instance using Get.put() to make it available for all "child" routes.
final UsersController uc = Get.put(UsersController());

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text("GetX Demo "),
// Displaying the count value using obx
Obx(() => Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
uc.count.toString(),
style: const TextStyle(
fontWeight: FontWeight.bold, fontSize: 16.0),
),
)),
],
),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
OutlinedButton(
onPressed: () {
// Calling function to increase the count value
uc.incrementCount();
},
child: const Text("Increment count")),
OutlinedButton(
onPressed: () {
// Navigate to api demo page
Get.to(() => ApiDemoPage());
},
child: const Text("Api Demo"))
],
),
),
);
}
}
home page

Create another dart file in the page folder and name it api_demo_page.dart. In this file, we will display the list of user details using obx and a button to decrease the count value.

// Creating instance using Get.put() to make it available for all "child" routes.
final UsersController uc = Get.put(UsersController());
// Displays the count value using obx in appbar
Obx(() => Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
uc.count.toString(),
style: const TextStyle(
fontWeight: FontWeight.bold, fontSize: 16.0),
),
)),
// Displaying the list of users using obx
Obx(() {
return uc.isLoading.isTrue
? const Center(child: CircularProgressIndicator())
: Container(
height: _height,
child: ListView.builder(
itemCount: uc.usersList.length,
itemBuilder: (context, index) => Padding(
padding: const EdgeInsets.all(2.0),
child: Container(
padding: const EdgeInsets.all(20),
decoration:
BoxDecoration(color: Colors.amber.shade400),
child: Row(
children: [
const Text(
"Name : ",
style: TextStyle(
color: Colors.black,
fontSize: 12.0,
letterSpacing: 2,
fontWeight: FontWeight.bold),
),
Text(
uc.usersList[index].name,
style: const TextStyle(
color: Colors.black,
letterSpacing: 2,
fontSize: 12.0,
fontWeight: FontWeight.bold),
),
],
),
),
)),
);
});

In this article, we conclude how to make an API call using get and http. Listen to the changes for variables, lists, etc by simply making them observable. Display the data fetched from API using Obx. Just simply wrap the widget with obx which will listen to all the observables defined in the controller.

Full source code is available on the git.

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

Clap if you like the article. 👏

Also, go through my article to learn the state management using Riverpod.

--

--