flutter
/

GetX Simple State Management: GetBuilder & update() Guide

Last Sync: Today

On this page

12
0%
5 min read
Remaining
5 minleft

Click any section to jump — progress syncs automatically

flutter

GetX Simple State Management: GetBuilder & update() Guide

What is Simple State in GetX?

Simple state in GetX is a lightweight approach to state management that gives you explicit control over when the UI should rebuild. Instead of automatically reacting to every change (like reactive state), you manually trigger updates using the update() method. This makes it ideal for performance-sensitive parts of your app where you want to batch updates or avoid unnecessary rebuilds.

The GetBuilder Widget

GetBuilder is the core widget for simple state. It listens to updates from a controller and rebuilds only the widget tree inside it when update() is called. It's efficient because it doesn't rely on reactive variables and only rebuilds the exact part of the UI you wrap with it.

DARTRead-only
1
class CounterController extends GetxController {
  int count = 0;

  void increment() {
    count++;
    update(); // triggers rebuild of all GetBuilder widgets using this controller
  }
}

// In your view
GetBuilder<CounterController>(
  builder: (controller) => Text('Count: ${controller.count}'),
)
// OR using the controller directly if already injected
GetBuilder<CounterController>(
  builder: (_) => Text('Count: ${Get.find<CounterController>().count}'),
)

Updating Specific Parts

You can pass an optional id to update() and to GetBuilder to update only specific widgets. This is useful when a controller manages multiple independent parts of the UI.

DARTRead-only
1
class MyController extends GetxController {
  int count1 = 0;
  int count2 = 0;

  void increment1() {
    count1++;
    update(['counter1']); // only rebuilds widgets with id 'counter1'
  }

  void increment2() {
    count2++;
    update(['counter2']);
  }
}

// In your view
GetBuilder<MyController>(
  id: 'counter1',
  builder: (controller) => Text('Count1: ${controller.count1}'),
)
GetBuilder<MyController>(
  id: 'counter2',
  builder: (controller) => Text('Count2: ${controller.count2}'),
)

GetBuilder vs Obx

Understanding when to use each approach is crucial for building performant apps.

  • GetBuilder (Simple State):
    • Manual rebuilds with update()
    • No reactive variables needed
    • Perfect for simple counters, forms, or when you need precise control
    • Lightweight and fast for many small updates
    • Better for performance when you know exactly when to rebuild
    Obx (Reactive State):
    • Automatic rebuilds on any change
    • Uses .obs variables
    • Great for real-time updates, complex UIs with many dependencies
    • Less boilerplate for frequently changing data
    • Can cause more rebuilds if not used granularly

When to Use Simple State

  • When you have a small number of state variables that change infrequently.
  • When you want to batch multiple state changes before rebuilding.
  • When you need to rebuild only specific parts of the UI with IDs.
  • When you're migrating from a StatefulWidget and want minimal changes.
  • For performance-critical animations or timers where you want to control rebuild frequency.

Lifecycle with GetBuilder

GetBuilder automatically listens to the controller's lifecycle. When the controller is disposed, the GetBuilder widget is also disposed, preventing memory leaks. You don't need to manually manage listeners.

Mixing Simple and Reactive State

You can combine both approaches in the same controller. This gives you the best of both worlds: reactive variables for automatic UI updates and simple state for performance-critical parts.

DARTRead-only
1
class HybridController extends GetxController {
  // Reactive
  var reactiveCount = 0.obs;

  // Simple
  int simpleCount = 0;

  void incrementReactive() => reactiveCount++;

  void incrementSimple() {
    simpleCount++;
    update(); // only GetBuilder widgets rebuild
  }
}

Performance Considerations

  • Use IDs wisely: Updating only necessary widgets prevents unnecessary rebuilds.
  • Avoid large widgets inside GetBuilder: If you wrap a large widget tree, it will all rebuild when update() is called. Use multiple smaller GetBuilder widgets instead.
  • Prefer GetBuilder for animations: Since you control when rebuilds happen, you can synchronize with animation frames.
  • Batch updates: Group multiple state changes before calling update() to avoid multiple rebuilds.

Best Practices

  • Keep controllers focused: One controller per screen or feature, with clear responsibilities.
  • Use Get.put in bindings: Inject controllers outside the build method to avoid duplicates.
  • Call update() only when needed: Don't call it if no state actually changed.
  • Use GetView for cleaner syntax: class MyView extends GetView<MyController> gives you direct controller access.
  • Combine with reactive when beneficial: Don't be afraid to mix both patterns in the same app.

Common Mistakes

  • ❌ Calling update() inside build method – Causes infinite loops. ✅ Call update() only in response to events (like button presses).
  • ❌ Using GetBuilder without a controller – Forgetting to register the controller leads to errors. ✅ Use Get.put or bindings before using GetBuilder.
  • ❌ Not using IDs when needed – Updating unrelated parts of the UI. ✅ Use IDs to target specific widgets.
  • ❌ Overusing GetBuilder when Obx would be simpler – More code than necessary. ✅ For simple reactive needs, consider Obx.

FAQ

  • Q: Can I use GetBuilder without GetMaterialApp?
    A: No, GetX features require GetMaterialApp for full functionality.
  • Q: Does GetBuilder automatically dispose controllers?
    A: Yes, when the controller is no longer referenced (e.g., the route is closed), GetX will dispose it. Use permanent: true if you need it to stay alive.
  • Q: How many GetBuilder widgets can I have per controller?
    A: Unlimited. Each one will rebuild when update() is called (or only specific ones if you use IDs).
  • Q: Can I use GetBuilder inside a ListView?
    A: Yes, but ensure you provide a unique id for each item if you want to update individually, or the whole list will rebuild.
  • Q: Is GetBuilder faster than Obx?
    A: For simple use cases, both are very fast. GetBuilder can be more predictable because you control rebuilds, which can be beneficial in animations or when you want to avoid unnecessary rebuilds.
  • Q: Can I use GetBuilder with reactive variables?
    A: Yes, but it's redundant. If you have reactive variables, use Obx; if you use GetBuilder, you don't need reactive variables.
  • Q: How do I test a controller that uses GetBuilder?
    A: You can instantiate the controller and call its methods; update() will still work, but you don't need to test the UI rebuilding part separately.

Conclusion

GetX simple state with GetBuilder provides a lightweight, manual approach to state management that's perfect for performance-critical parts of your app. By understanding when to use it vs reactive state, and by following best practices, you can build efficient, maintainable Flutter applications.

Try it yourself

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

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      home: SimpleStateDemo(),
    );
  }
}

class CounterController extends GetxController {
  int count = 0;
  int totalClicks = 0;

  void increment() {
    count++;
    totalClicks++;
    update(); // rebuild both counters
  }

  void reset() {
    count = 0;
    update();
  }

  void showTotalClicks() {
    Get.snackbar('Total Clicks', '$totalClicks', snackPosition: SnackPosition.BOTTOM);
  }
}

class SimpleStateDemo extends StatelessWidget {
  final controller = Get.put(CounterController());

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('GetBuilder Example')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('Simple state with GetBuilder'),
            SizedBox(height: 20),
            GetBuilder<CounterController>(
              builder: (ctrl) => Text(
                'Count: ${ctrl.count}',
                style: TextStyle(fontSize: 32),
              ),
            ),
            SizedBox(height: 20),
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                ElevatedButton(
                  onPressed: controller.increment,
                  child: Text('Increment'),
                ),
                SizedBox(width: 10),
                ElevatedButton(
                  onPressed: controller.reset,
                  child: Text('Reset'),
                ),
                SizedBox(width: 10),
                ElevatedButton(
                  onPressed: controller.showTotalClicks,
                  child: Text('Show Total Clicks'),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

Test Your Knowledge

Q1
of 3

Which method is used to trigger a rebuild in GetBuilder?

A
setState()
B
refresh()
C
update()
D
rebuild()
Q2
of 3

How can you update only specific GetBuilder widgets?

A
Use different controllers
B
Use the `id` parameter in update() and GetBuilder
C
Call update() with a widget reference
D
Use Obx instead
Q3
of 3

When should you prefer GetBuilder over Obx?

A
When you want automatic rebuilds
B
When you need manual control over rebuilds
C
When you have many reactive variables
D
Never, Obx is always better

Previous

getx reactive state

Next

getx rx types

Related Content

Need help?

Explore our comprehensive docs or start a chat with our tech experts.