Mobile Development 40 min read

Comprehensive Guide to GetX in Flutter: State Management, Routing, Dependency Injection, and Practical Project Implementation

This article provides an in‑depth tutorial on using the GetX library for Flutter, covering its state‑management, routing, dependency‑injection features, code generation tricks, common pitfalls, and a step‑by‑step rewrite of a WanAndroid project with practical examples.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Comprehensive Guide to GetX in Flutter: State Management, Routing, Dependency Injection, and Practical Project Implementation

1. Introduction

When writing the "State Management" chapter, many readers mentioned GetX, a popular Flutter library whose reputation is mixed: it is praised for concise syntax, rich APIs, and simplifying development, but criticized for over‑encapsulation, hidden Flutter internals, and tight coupling that can hinder future maintenance.

The author shares three ways to reduce boilerplate when using Riverpod, including automatic setter generation, dynamic property updates, and function closures, concluding that the closure approach is the most elegant.

2. GetX Quick Documentation

GitHub repository: jonataslaw/getx

GetX is one of the most popular Flutter packages, offering three core capabilities: state management, routing, and dependency management.

2.1. GetX First Experience – "Calculator Plus"

The official demo rewrites the default counter example from ~100 lines to only 26 lines using GetX, demonstrating automatic UI refresh, page navigation, data sharing, and separation of business logic from UI.

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

// Custom Controller
class CounterController extends GetxController {
  var count = 0.obs;
  increment() => count++;
}

void main() => runApp(const GetMaterialApp(home: HomePage()));

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

  @override
  Widget build(context) {
    final CounterController c = Get.put(CounterController());
    return Scaffold(
      appBar: AppBar(title: Obx(() => Text("Clicks: ${c.count}"))),
      body: Center(child: ElevatedButton(child: const Text("Go to Other"), onPressed: () => Get.to(const OtherPage()))),
      floatingActionButton: FloatingActionButton(onPressed: c.increment, child: const Icon(Icons.add)),
    );
  }
}

class OtherPage extends StatelessWidget {
  const OtherPage({super.key});

  @override
  Widget build(context) {
    final CounterController c = Get.find();
    return Scaffold(body: Center(child: Text("${c.count}")));
  }
}

The result shows a concise UI without the need for StatefulWidget or explicit BuildContext usage.

2.2. State Management

GetX defines reactive/observable variables (Rx) in three ways: using the .obs extension, the generic Rx class, or type‑specific RxString, RxInt, etc. Access the value via .value, but the first assignment always triggers listeners unless firstRebuild = false is set.

// ① Recommended: add .obs
final strRx1 = "initial".obs;
final intRx1 = 1.obs;
// ② Generic Rx
final strRx2 = Rx
("initial");
// ③ Type‑specific RxString
final strRx3 = RxString("initial");

Lists and custom objects can be made reactive without .value, and updates can be performed via assign , assignAll , update , or direct assignment followed by refresh() .

// List example
final list = List
().obs;
list.assign(User("CoderPig", 30));
list.assignAll([User("CoderPig", 30), User("Jay", 18)]);

// Custom class example
final user = User().obs;
user.update((u) { u.name = 'Jonny'; u.age = 18; });
user(User(name: 'João', age: 35));

2.2.2. Reactive Widgets – Obx & GetX

Obx rebuilds the UI when observed variables change, while GetX provides a builder with more control. Both require a controller instance to be injected beforehand.

class MyController extends GetxController { var count = 0.obs; }

void main() { Get.put(MyController()); runApp(MyApp()); }

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('Obx Demo')),
        body: Center(child: Obx(() => Text('Clicks: ${Get.find
().count.value}'))),
        floatingActionButton: FloatingActionButton(onPressed: () => Get.find
().count++, child: const Icon(Icons.add)),
      ),
    );
  }
}

2.2.3. Fine‑grained Updates – GetBuilder

GetBuilder lets you manually trigger UI rebuilds via update() , useful when reactive variables are unnecessary.

class CartController extends GetxController {
  var itemCount = 0;
  var totalPrice = 0.0;
  void addItem(double price) { itemCount++; totalPrice += price; update(); }
  void removeItem(double price) { if (itemCount > 0) { itemCount--; totalPrice -= price; update(); } }
}

GetBuilder
(
  init: CartController(),
  builder: (c) => Column(children: [
    Text("Quantity: ${c.itemCount}"),
    Text("Total: ${c.totalPrice}"),
    ElevatedButton(onPressed: () => c.addItem(10.0), child: const Text('Add')),
    ElevatedButton(onPressed: () => c.removeItem(10.0), child: const Text('Remove')),
  ]),
);

2.2.4. Local State Widgets – ObxValue & ValueBuilder

ObxValue binds a single Rx variable to a widget; ValueBuilder mimics StatefulWidget behavior without Rx.

ObxValue((data) => Switch(value: data.value, onChanged: (v) => data.value = v), false.obs);

ValueBuilder
(
  initialValue: false,
  builder: (value, update) => Switch(value: value, onChanged: (v) => update(v)),
  onUpdate: (v) => print('Updated: $v'),
  onDispose: () => print('Disposed'),
);

2.2.5. Helper Widgets – GetView, GetWidget, GetxService

GetView provides a stateless widget with an automatically injected controller; GetWidget creates a new controller per navigation; GetxService lives for the entire app lifecycle, ideal for authentication or theme management.

class HomeController extends GetxController { var count = 0.obs; void increment() => count++; }

class HomeScreen extends GetView
{
  @override HomeController get controller => Get.put(HomeController());
  @override Widget build(BuildContext context) => Scaffold(appBar: AppBar(title: const Text('Home')), body: Center(child: Obx(() => Text('Clicks: ${controller.count}')),
    floatingActionButton: FloatingActionButton(onPressed: controller.increment, child: const Icon(Icons.add)));
}

2.2.6. Workers – ever, once, debounce, interval

Workers allow you to react to variable changes with different strategies (continuous, one‑time, debounced, or throttled).

class CounterController extends GetxController {
  var count = 0.obs;
  @override void onInit() {
    ever(count, (_) => print('changed to $count'));
    once(count, (_) => print('first change $count'));
    debounce(count, (_) => print('debounce $count'), time: const Duration(seconds: 1));
    interval(count, (_) => print('interval $count'), time: const Duration(seconds: 1));
    super.onInit();
  }
  void increment() => count++;
}

2.2.7. Controller Lifecycle – onInit, onReady, onClose, update, refresh, delete

Standard GetX controller callbacks manage initialization, async loading, cleanup, and manual UI refresh.

2.2.8. Bindings – Dependency Registration

Bindings tie a route to its required dependencies, automatically invoking dependencies() when the route is entered.

class HomeBindings extends Bindings {
  @override void dependencies() { Get.lazyPut
(() => HomeController()); }
}

2.3. Routing Management

Replace MaterialApp with GetMaterialApp to use GetX routing APIs such as Get.to() , Get.off() , named routes, middlewares, and context‑free navigation for SnackBars, dialogs, and bottom sheets.

// Simple navigation
Get.to(OtherPage());
Get.back();
Get.off(OtherPage());
Get.offAll(OtherPage());

// Named routes
Get.toNamed('/second');
Get.offNamed('/second');
Get.offAllNamed('/second');
Get.toNamed('/second', arguments: 'data');
print(Get.arguments);

2.3.5. Nested Navigation

Nested navigators can be created with Get.nestedKey(id) and navigated via Get.toNamed(..., id: id) .

Navigator(
  key: Get.nestedKey(1),
  onGenerateRoute: (settings) {
    if (settings.name == '/') return GetPageRoute(page: () => HomePage());
    if (settings.name == '/second') return GetPageRoute(page: () => SecondPage());
  },
);

2.4. Dependency Management

GetX provides Get.put() , Get.lazyPut() , Get.putAsync() , and Get.create() for various injection lifecycles, along with Get.find() to retrieve instances.

// Synchronous injection
Get.put
(ApiClient());
// Lazy injection
Get.lazyPut
(() => ApiClient());
// Async injection
await Get.putAsync
(() async => await ApiClient().init());
// Factory (new instance each find)
Get.create
(() => ApiClient());

2.5. Additional Features

Internationalization via Translations and .tr .

Theme switching with Get.changeTheme() .

Network layer using GetConnect with request/response modifiers, generic response handling, and error encapsulation.

Middleware support for route guards and logging.

3. Project Demo – Rewriting WanAndroid with GetX

The author rewrites the WanAndroid app using GetX for state management, routing, and network requests, demonstrating login flow, loading dialogs, cookie persistence with get_storage , and a clean MVVM architecture.

class LoginVM extends GetxController {
  var userNameController = TextEditingController();
  var passwordController = TextEditingController();
  Future
login() async {
    final result = await AccountApi.login(AccountLoginReq(userNameController.text, passwordController.text));
    if (result.error == null) {
      ToastUtil.show(msg: "${result.data?.toJson()}");
      return true;
    } else {
      ToastUtil.show(msg: result.errorMsg);
      return false;
    }
  }
}

Utility functions such as a global loading dialog are implemented without needing a BuildContext, showcasing GetX’s context‑free capabilities.

4. Conclusion

The article concludes that GetX offers a Swiss‑army‑knife‑like experience for Flutter development, dramatically reducing boilerplate and enabling rapid feature implementation. The full source code is available on GitHub.

FlutterMobile DevelopmentState ManagementroutingDependency InjectionGetX
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.