rxget 0.1.2 copy "rxget: ^0.1.2" to clipboard
rxget: ^0.1.2 copied to clipboard

A lightweight reactive state management and dependency injection library for Dart and Flutter — pure and minimal.

rxget #

RxGet is a lightweight, performance-focused fork of GetX — keeping only reactivity and dependency injection.
No routing, no UI helpers — just pure state management.

About Get #

  • RxGet is an extra-light and powerful solution for Flutter. It combines high-performance state management and intelligent dependency injection quickly and practically.

  • RxGet has 3 basic principles. This means that these are the priority for all resources in the library: PRODUCTIVITY, PERFORMANCE AND ORGANIZATION.

    • PERFORMANCE: Focused on minimum resource consumption. The simple state manager (GetBuilder) avoids ChangeNotifier and Streams entirely in favor of highly optimized, lightweight callbacks, while the reactive state manager (Obx) uses optimized mini-streams.

    • PRODUCTIVITY: RxGet uses an easy and pleasant syntax. No matter what you want to do, there is always an easier way with RxGet. It will save hours of development and will provide the maximum performance your application can deliver.

      Generally, the developer should be concerned with removing controllers from memory. With RxGet this is not necessary because resources are removed from memory when they are not used by default. If you want to keep it in memory, you must explicitly declare "permanent: true" in your dependency. That way, in addition to saving time, you are less at risk of having unnecessary dependencies on memory. Dependency loading is also lazy by default.

    • ORGANIZATION: RxGet allows the total decoupling of the View, presentation logic, business logic, and dependency injection. You do not need context to access your controllers/blocs through an inheritedWidget, so you completely decouple your presentation logic and business logic from your visualization layer. You do not need to inject your Controllers/Models/Blocs classes into your widget tree through MultiProviders. For this, RxGet uses its own dependency injection feature, decoupling the DI from its view completely. With RxGet you know where to find each feature of your application, having clean code by default. In addition to making maintenance easy, this makes the sharing of modules something that until then in Flutter was unthinkable, something totally possible. BLoC was a starting point for organizing code in Flutter, it separates business logic from visualization. RxGet is a natural evolution of this, not only separating the business logic but the presentation logic. Dependency injection is also decoupled, and the data layer is out of it all. You know where everything is, and all of this in an easier way than building a hello world. GetX is the easiest, practical, and scalable way to build high-performance applications with the Flutter SDK. It has a large ecosystem around it that works perfectly together, it's easy for beginners, and it's accurate for experts. It is secure, stable, up-to-date, and offers a huge range of APIs built-in that are not present in the default Flutter SDK.

Installing #

Add Get to your pubspec.yaml file:

dependencies:
  get:

Import get in files that it will be used:

import 'package:rxget/rxget.dart';

rxget Architecture vs GetX #

While rxget maintains the fast and productive syntax of GetX, it introduces a strict separation of concerns between Logic and State to guarantee memory safety and enforce clean code.

In original GetX, developers often mixed reactive variables (.obs) and business logic methods within the same GetxController, leading to bloated classes and forgotten memory disposal. In rxget, controllers must explicitly separate their state into a GetxState class:

  • GetxState: Holds the reactive variables and data. It strictly enforces an onClose() method where you must dispose of your .obs streams, guaranteeing no memory leaks.
  • GetxController<State>: Holds pure business logic and methods, manipulating the state safely without holding variables itself.

Counter App (The rxget Way) #

Let's build a counter app to demonstrate this decoupled architecture!

  • Step 1: Create your state class by extending GetxState. Here you define your reactive variables and handle their mandatory disposal.
part of 'counter_controller.dart';

final class _CounterState extends GetxState {
  final _count = 0.obs;

  int get count => _count.value;

  @override
  void onClose() {
    _count.close(); // Strictly enforce memory disposal!
  }
}
  • Step 2: Create your logic class by extending GetxController<CounterState>.
import 'package:rxget/rxget.dart';
part 'counter_state.dart';

class CounterController extends GetxController<_CounterState> {
  @override
  _CounterState get state => _CounterState();

  void increment() {
    state._count.value++;
  }
}
  • Step 3: Create your View using GetInWidget to automatically manage the controller's lifecycle without needing complex routing.
class Home extends StatelessWidget {
  const Home({super.key});

  @override
  Widget build(BuildContext context) {
    // Inject the controller scoped to this widget tree
    return GetInWidget(
      dependencies: [GetIn(() => CounterController())],
      child: Scaffold(
        appBar: AppBar(
          // Use Obx(()=> to reactively update the UI!
          title: Obx(() {
            final controller = Get.find<CounterController>();
            return Text("Clicks: ${controller.state.count}");
          }),
        ),
        body: Center(
          child: ElevatedButton(
            child: const Text("Go to Other"), 
            onPressed: () => Navigator.push(context, MaterialPageRoute(builder: (context) => const Other()))
          ),
        ),
        floatingActionButton: FloatingActionButton(
          child: const Icon(Icons.add), 
          onPressed: () => Get.find<CounterController>().increment(),
        ),
      ),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    // Safely find the controller provided by the parent GetInWidget
    final controller = Get.find<CounterController>();
    
    return Scaffold(
      body: Center(
        child: Obx(() => Text("${controller.state.count}")),
      ),
    );
  }
}

Result:

This is a simple project but it already makes clear how powerful Get is. As your project grows, this difference will become more significant.

Get was designed to work with teams, but it makes the job of an individual developer simple.

Improve your deadlines, deliver everything on time without losing performance. Get is not for everyone, but if you identified with that phrase, Get is for you!

The Three pillars #

State management #

Get has two different state managers: the simple state manager (we'll call it GetBuilder) and the reactive state manager (GetX/Obx)

Reactive State Manager

Reactive programming can alienate many people because it is said to be complicated. GetX turns reactive programming into something quite simple:

  • You won't need to create StreamControllers.
  • You won't need to create a StreamBuilder for each variable
  • You will not need to create a class for each state.
  • You will not need to create a get for an initial value.
  • You will not need to use code generators

Reactive programming with Get is as easy as using setState.

Let's imagine that you have a name variable and want that every time you change it, all widgets that use it are automatically changed.

This is your count variable:

var name = 'Jonatas Borges';

To make it observable, you just need to add ".obs" to the end of it:

var name = 'Jonatas Borges'.obs;

And in the UI, when you want to show that value and update the screen whenever the values changes, simply do this:

Obx(() => Text("${controller.name}"));

That's all. It's that simple.

More details about state management

See an more in-depth explanation of state management here. There you will see more examples and also the difference between the simple state manager and the reactive state manager

You will get a good idea of GetX power.

Dependency management #

Get has a simple and powerful dependency manager that allows you to retrieve the same class as your Bloc or Controller with just 1 lines of code, no Provider context, no inheritedWidget:

Controller controller = Get.put(Controller()); // Rather Controller controller = Controller();
  • Note: If you are using Get's State Manager, pay more attention to the bindings API, which will make it easier to connect your view to your controller.

Instead of instantiating your class within the class you are using, you are instantiating it within the Get instance, which will make it available throughout your App. So you can use your controller (or class Bloc) normally

Tip: Get dependency management is decoupled from other parts of the package, so if for example, your app is already using a state manager (any one, it doesn't matter), you don't need to rewrite it all, you can use this dependency injection with no problems at all

controller.fetchApi();

Controller controller = Get.find(); //Yes, it looks like Magic, Get will find your controller, and will deliver it to you. You can have 1 million controllers instantiated, Get will always give you the right controller.


And then you will be able to recover your controller data that was obtained back there:

```dart
Text(controller.textFromApi);

More details about dependency management

See a more in-depth explanation of dependency management here

Widget-Scoped Dependency Injection with GetInWidget

After removing the navigator and UI features of GetX, the automatic memory disposal system based on routes was removed. To solve this, rxget provides GetInWidget: a widget-scoped dependency injection system that ensures all resources, controllers, and their memory are completely and automatically disposed of when the widget is removed from the tree. This achieves excellent memory optimization out of the box without requiring manual disposal.

Registering Dependencies

Pass a list of GetIn configurations to GetInWidget. Each dependency uses a factory function (e.g., () => MyController()) to support lazy initialization.

GetInWidget(
  dependencies: [
    // Lazily injected dependency (only created in memory when first accessed via Get.find)
    GetIn<MyController>(() => MyController()),
    
    // Dependencies can reference previously registered ones
    GetIn<OtherController>(() => OtherController(Get.find<MyController>())),
    
    // Eagerly injected dependency
    GetIn<AuthService>(() => AuthService(), lazy: false),
  ],
  child: MyWidget(),
)

When GetInWidget is destroyed (e.g., you navigate away or the widget is removed), all memory tied to the registered controllers is automatically disposed of securely.

Access dependencies anywhere in the widget's descending tree with Get.find<Type>():

final controller = Get.find<MyController>();

Useful tips #

.observables (also known as Rx Types) have a wide variety of internal methods and operators.

Is very common to believe that a property with .obs IS the actual value... but make no mistake! We avoid the Type declaration of the variable, because Dart's compiler is smart enough, and the code looks cleaner, but:

var message = 'Hello world'.obs;
print( 'Message "$message" has Type ${message.runtimeType}');

Even if message prints the actual String value, the Type is RxString!

So, you can't do message.substring( 0, 4 ). You have to access the real value inside the observable: The most "used way" is .value, but, did you know that you can also use...

final name = 'GetX'.obs;
// only "updates" the stream, if the value is different from the current one.
name.value = 'Hey';

// All Rx properties are "callable" and returns the new value.
// but this approach does not accepts `null`, the UI will not rebuild.
name('Hello');

// is like a getter, prints 'Hello'.
name() ;

/// numbers:

final count = 0.obs;

// You can use all non mutable operations from num primitives!
count + 1;

// Watch out! this is only valid if `count` is not final, but var
count += 1;

// You can also compare against values:
count > 2;

/// booleans:

final flag = false.obs;

// switches the value between true/false
flag.toggle();


/// all types:

// Sets the `value` to null.
flag.nil();

// All toString(), toJson() operations are passed down to the `value`
print( count ); // calls `toString()` inside  for RxInt

final abc = [0,1,2].obs;
// Converts the value to a json Array, prints RxList
// Json is supported by all Rx types!
print('json: ${jsonEncode(abc)}, type: ${abc.runtimeType}');

// RxMap, RxList and RxSet are special Rx types, that extends their native types.
// but you can work with a List as a regular list, although is reactive!
abc.add(12); // pushes 12 to the list, and UPDATES the stream.
abc[3]; // like Lists, reads the index 3.


// equality works with the Rx and the value, but hashCode is always taken from the value
final number = 12.obs;
print( number == 12 ); // prints > true

/// Custom Rx Models:

// toJson(), toString() are deferred to the child, so you can implement override on them, and print() the observable directly.

class User {
    String name, last;
    int age;
    User({this.name, this.last, this.age});

    @override
    String toString() => '$name $last, $age years old';
}

final user = User(name: 'John', last: 'Doe', age: 33).obs;

// `user` is "reactive", but the properties inside ARE NOT!
// So, if we change some variable inside of it...
user.value.name = 'Roi';
// The widget will not rebuild!,
// `Rx` don't have any clue when you change something inside user.
// So, for custom classes, we need to manually "notify" the change.
user.refresh();

// or we can use the `update()` method!
user.update((value){
  value.name='Roi';
});

print( user );


#### Tests

You can test your controllers like any other class, including their lifecycles:

```dart
class Controller extends GetxController {
  @override
  void onInit() {
    super.onInit();
    //Change value to name2
    name.value = 'name2';
  }

  @override
  void onClose() {
    name.value = '';
    super.onClose();
  }

  final name = 'name1'.obs;

  void changeName() => name.value = 'name3';
}

void main() {
  test('''
Test the state of the reactive variable "name" across all of its lifecycles''',
      () {
    /// You can test the controller without the lifecycle,
    /// but it's not recommended unless you're not using
    ///  GetX dependency injection
    final controller = Controller();
    expect(controller.name.value, 'name1');

    /// If you are using it, you can test everything,
    /// including the state of the application after each lifecycle.
    Get.put(controller); // onInit was called
    expect(controller.name.value, 'name2');

    /// Test your functions
    controller.changeName();
    expect(controller.name.value, 'name3');

    /// onClose was called
    Get.delete<Controller>();

    expect(controller.name.value, '');
  });
}
Tips
Mockito or mocktail

If you need to mock your GetxController, you should extend GetxController, and mixin it with Mock, that way

class NotificationServiceMock extends GetxController with Mock implements NotificationService {}
Using Get.reset()

If you are testing widgets, or test groups, use Get.reset at the end of your test or in tearDown to reset all settings from your previous test.

Why rxget? #

1.	Reliability through simplicity.

After Flutter updates, many packages break due to dependency conflicts or version mismatches. rxget minimizes this risk by focusing on just two core features — reactive state management and dependency injection. With fewer dependencies and no routing or UI helpers, maintenance becomes painless. Update rxget, and you’re ready to build again without worrying about compatibility issues.

2.	Flutter made simpler.

Flutter is great but can sometimes feel verbose. rxget keeps things simple and expressive. You can turn any variable reactive with .obs and use Obx to automatically rebuild widgets. No boilerplate, no complex setup — just reactive code that works seamlessly. 3. Performance without overhead. rxget removes the extra layers found in larger frameworks. It manages memory efficiently, ensuring unused dependencies are disposed of automatically. You write less, focus more on logic, and still get optimal performance with minimal resource usage. 4. Clean decoupling. With rxget, business logic and UI stay truly separate. Controllers can initialize data, perform async work, and update reactive variables — all without depending on the view’s context. This creates a cleaner architecture where UI developers and logic developers can work independently while keeping everything reactive and synchronized.

In short

rxget brings you the essence of GetX — reactivity and DI — without the noise. No routes, no snackbars, no context gymnastics. Just clean, reactive, and efficient Flutter development. This library will always be updated and implementing new features. Feel free to offer PRs and contribute to them.

Community #

Community channels #

GetX has a highly active and helpful community. If you have questions, or would like any assistance regarding the use of this framework, please join our community channels, your question will be answered more quickly, and it will be the most suitable place. This repository is exclusive for opening issues, and requesting resources, but feel free to be part of GetX Community.

Slack Discord Telegram
Get on Slack Discord Shield Telegram

How to contribute #

Want to contribute to the project? We will be proud to highlight you as one of our collaborators. Here are some points where you can contribute and make Get (and Flutter) even better.

  • Helping to translate the readme into other languages.
  • Adding documentation to the readme (a lot of Get's functions haven't been documented yet).
  • Write articles or make videos teaching how to use Get (they will be inserted in the Readme and in the future in our Wiki).
  • Offering PRs for code/tests.
  • Including new functions.

Any contribution is welcome!

3
likes
150
points
92
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

A lightweight reactive state management and dependency injection library for Dart and Flutter — pure and minimal.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

flutter

More

Packages that depend on rxget