Flutter GetX Masterclass — Chapter 2: State Management with GetX

Amanullah Bahram
5 min readOct 17, 2024

--

Welcome back to Chapter 2 of our Flutter GetX Masterclass! In this chapter, we’re diving into state management using GetX. The focus is on real-world examples, and by the end of this chapter, you’ll have a fully functioning shopping cart in your app where users can add and remove products, and the cart’s state will update dynamically in the UI.

Let’s get started!

Goal of Chapter 2

By the end of this chapter, you’ll understand how to:
- Manage reactive and non-reactive state in GetX.
- Automatically update the UI based on state changes.
- Handle user input and manage data flow using a real-world shopping cart example.

This tutorial is based on the video series you can follow on YouTube. Be sure to check it out if you prefer video format!

Understanding Reactive and Non-Reactive State

GetX provides two main approaches for managing state:
1. Reactive State Management: Automatically updates the UI when the state changes, which is a highly efficient way to handle UI updates.
2. Non-Reactive State Management: Updates the UI manually when necessary.

For this chapter, we’ll focus on reactive state management, ensuring the cart state updates seamlessly in the background whenever the user interacts with it.

Setting Up the Folder Structure

We’ll be expanding our project by adding a CartController and corresponding views to handle our shopping cart functionality.

Here’s an overview of the updated folder structure:


lib/

├── app/
│ ├── modules/
│ │ ├── product/
│ │ │ ├── controllers/
│ │ │ │ └── product_controller.dart
│ │ │ │ └── cart_controller.dart // New controller
│ │ │ ├── views/
│ │ │ │ └── product_view.dart
│ │ │ │ └── product_details_view.dart
│ │ │ │ └── cart_view.dart // New cart view
│ │ │ └── models/
│ │ │ └── product_model.dart
│ └── routes/
│ └── app_pages.dart
│ └── app_routes.dart
└── main.dart

Step 1: Creating the Cart Controller

The CartController will handle the cart’s state, including adding and removing products, and calculating the total price.

Let’s start by creating a cart_controller.dart file under `app/modules/product/controllers/`:

import 'package:get/get.dart';
import '../models/product_model.dart';

class CartController extends GetxController {
var cartItems = <ProductModel>[].obs;
var totalAmount = 0.0.obs;
void addToCart(ProductModel product) {
cartItems.add(product);
calculateTotal();
}

void removeFromCart(ProductModel product) {
cartItems.remove(product);
calculateTotal();
}

void calculateTotal() {
totalAmount.value = cartItems.fold(0, (sum, item) => sum + item.price);
}
}

- Reactive State: The `cartItems` list and `totalAmount` are marked as `.obs`, making them observable, meaning any change to these values will automatically update the UI.
- Methods: We have `addToCart()` and `removeFromCart()` for managing items in the cart, and `calculateTotal()` to compute the total price of all items.

Step 2: Creating the Cart View

Next, let’s build the cart screen that will display the products added by the user along with the total amount.

In the `cart_view.dart` file under `app/modules/product/views/`:

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

class CartView extends StatelessWidget {
final CartController cartController = Get.find<CartController>();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Cart'),
),
body: Column(
children: [
Expanded(
child: Obx(() {
return ListView.builder(
itemCount: cartController.cartItems.length,
itemBuilder: (context, index) {
final item = cartController.cartItems[index];
return ListTile(
title: Text(item.name),
subtitle: Text('\$${item.price.toString()}'),
trailing: IconButton(
icon: Icon(Icons.remove_circle_outline),
onPressed: () => cartController.removeFromCart(item),
),
);
},
);
}),
),
Obx(() {
return Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
'Total: \$${cartController.totalAmount.value.toStringAsFixed(2)}',
style: TextStyle(fontSize: 24),
),
);
}),
],
),
);
}
}

- Obx: The `Obx` widget is used to listen for changes in the reactive state, ensuring that the UI is updated automatically whenever the cart or total amount changes.
- ListView.builder: Displays each item in the cart with a remove button, allowing users to remove items dynamically.

Step 3: Adding the Add-to-Cart Functionality

Now that we have a way to view the cart, let’s add functionality to add products to the cart from the ProductDetailsView.

In your product_details_view.dart file under `app/modules/product/views/`:

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../models/product_model.dart';
import '../controllers/cart_controller.dart';

class ProductDetailsView extends StatelessWidget {
final CartController cartController = Get.find<CartController>();
@override
Widget build(BuildContext context) {
final ProductModel product = Get.arguments;
return Scaffold(
appBar: AppBar(
title: Text('Product Details'),
actions: [
IconButton(
icon: Icon(Icons.shopping_cart),
onPressed: () {
Get.toNamed('/cart');
},
),
],
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(product.name, style: TextStyle(fontSize: 24)),
SizedBox(height: 10),
Text('\$${product.price}', style: TextStyle(fontSize: 24)),
SizedBox(height: 20),
ElevatedButton(
onPressed: () {
cartController.addToCart(product);
Get.snackbar(
'Added to Cart', '${product.name} added to your cart');
},
child: Text('Add to Cart'),
),
],
),
),
);
}
}

- The `Add to Cart` button triggers the `addToCart()` method from the CartController, adding the selected product to the cart.
- We also provide an AppBar icon to navigate directly to the cart.

Step 4: Updating Routes

Let’s make sure we update our routes so users can navigate to the new CartView.

In app/routes/app_routes.dart:

part of 'app_pages.dart';

class Routes {
static const PRODUCT = '/product';
static const PRODUCT_DETAILS = '/product-details';
static const CART = '/cart'; // New cart route
}

And in app/routes/app_pages.dart:

import 'package:get/get.dart';
import '../modules/product/views/product_view.dart';
import '../modules/product/views/product_details_view.dart';
import '../modules/product/views/cart_view.dart'; // Import cart view
part 'app_routes.dart';

class AppPages {
static final routes = [
GetPage(
name: Routes.PRODUCT,
page: () => ProductView(),
),
GetPage(
name: Routes.PRODUCT_DETAILS,
page: () => ProductDetailsView(),
),
GetPage(
name: Routes.CART,
page: () => CartView(),
),
];
}

Step 5: Main Application Setup

In the `main.dart` file, initialize the CartController at the start of the app:

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'app/routes/app_pages.dart';
import 'app/modules/product/controllers/cart_controller.dart';

void main() {
Get.put(CartController()); // Initialize CartController
runApp(MyApp());
}

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GetMaterialApp(
debugShowCheckedModeBanner: false,
title: 'Product App',
initialRoute: Routes.PRODUCT,
getPages: AppPages.routes,
);
}
}

This sets up the CartController so it can be accessed throughout the app.

Conclusion

By following this chapter, you’ve learned how to implement reactive state management with GetX, ensuring that your UI updates automatically when the cart state changes. You now have a shopping cart feature where users can add and remove products, and view the total price, all with minimal code thanks to GetX.

In the next chapter, we’ll cover Dependency Injection with GetX and how to keep your app’s architecture clean and scalable!

GitHub Code

You can find the full code for this chapter on GitHub:

[GitHub Repository Link].

Stay tuned for more Flutter tips and tricks in the next chapters!

#flutter #fluttertutorial #getx #fluttergetx

--

--

Amanullah Bahram
Amanullah Bahram

No responses yet