Answer
Overview
GetBuilder is a lightweight state management widget from the GetX package. It rebuilds only when explicitly notified, making it more efficient than Obx for non-reactive state updates.
GetBuilder vs Obx
| Feature | GetBuilder | Obx |
|---|---|---|
| Reactive | ❌ No | ✅ Yes |
| Rebuild Trigger | Manual (update()) | Automatic (.obs) |
| Memory | Lower | Higher |
| Performance | Faster | Slightly slower |
| Use Case | Complex widgets | Simple reactive UI |
| Syntax | More verbose | Cleaner |
Basic Usage
Step 1: Install GetX
yamldependencies: get: ^4.6.0
Step 2: Create Controller
dartimport 'package:get/get.dart'; class CounterController extends GetxController { int count = 0; // Not reactive (.obs not needed) void increment() { count++; update(); // Manually trigger rebuild } void decrement() { count--; update(); // Must call update() } void reset() { count = 0; update(); } }
Step 3: Use GetBuilder
dartclass CounterScreen extends StatelessWidget { Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('GetBuilder Demo')), body: Center( child: GetBuilder<CounterController>( init: CounterController(), // Initialize controller builder: (controller) { return Text( 'Count: ${controller.count}', style: TextStyle(fontSize: 32), ); }, ), ), floatingActionButton: FloatingActionButton( onPressed: () { Get.find<CounterController>().increment(); }, child: Icon(Icons.add), ), ); } }
Advanced Features
1. Selective Rebuild with ID
Only rebuild specific GetBuilders.
dartclass TodoController extends GetxController { List<String> todos = []; int completedCount = 0; void addTodo(String todo) { todos.add(todo); update(['todo-list']); // Only rebuild widgets with id 'todo-list' } void incrementCompleted() { completedCount++; update(['counter']); // Only rebuild widgets with id 'counter' } } // UI GetBuilder<TodoController>( id: 'todo-list', // This widget only rebuilds when update(['todo-list']) is called builder: (controller) { return ListView.builder( itemCount: controller.todos.length, itemBuilder: (context, index) => Text(controller.todos[index]), ); }, ) GetBuilder<TodoController>( id: 'counter', // This widget only rebuilds when update(['counter']) is called builder: (controller) { return Text('Completed: ${controller.completedCount}'); }, )
2. Conditional Rebuild
dartclass UserController extends GetxController { String name = ''; int age = 0; void updateName(String newName) { name = newName; update(); // Rebuild all } void updateAge(int newAge) { age = newAge; update((ids) => ids.contains('age-display')); // Conditional rebuild } }
3. Auto Dispose
dartGetBuilder<CounterController>( init: CounterController(), dispose: (state) => Get.delete<CounterController>(), // Auto cleanup builder: (controller) => Text('${controller.count}'), )
Complete Example
dartimport 'package:flutter/material.dart'; import 'package:get/get.dart'; // Controller class ThemeController extends GetxController { bool isDarkMode = false; int fontSize = 16; void toggleTheme() { isDarkMode = !isDarkMode; update(['theme']); // Only rebuild theme-related widgets } void increaseFontSize() { fontSize++; update(['font']); // Only rebuild font-related widgets } void decreaseFontSize() { fontSize--; update(['font']); } } // Screen class SettingsScreen extends StatelessWidget { Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Settings')), body: Column( children: [ // Theme Toggle GetBuilder<ThemeController>( init: ThemeController(), id: 'theme', builder: (controller) { return SwitchListTile( title: Text('Dark Mode'), value: controller.isDarkMode, onChanged: (_) => controller.toggleTheme(), ); }, ), // Font Size Control GetBuilder<ThemeController>( id: 'font', builder: (controller) { return Column( children: [ Text( 'Preview Text', style: TextStyle(fontSize: controller.fontSize.toDouble()), ), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ IconButton( icon: Icon(Icons.remove), onPressed: controller.decreaseFontSize, ), Text('${controller.fontSize}'), IconButton( icon: Icon(Icons.add), onPressed: controller.increaseFontSize, ), ], ), ], ); }, ), ], ), ); } } void main() { runApp(GetMaterialApp(home: SettingsScreen())); }
GetBuilder vs setState
dart// With setState class CounterScreen extends StatefulWidget { _CounterScreenState createState() => _CounterScreenState(); } class _CounterScreenState extends State<CounterScreen> { int count = 0; Widget build(BuildContext context) { return Scaffold( body: Center(child: Text('$count')), floatingActionButton: FloatingActionButton( onPressed: () => setState(() => count++), ), ); } } // With GetBuilder class CounterScreen extends StatelessWidget { Widget build(BuildContext context) { return Scaffold( body: Center( child: GetBuilder<CounterController>( init: CounterController(), builder: (c) => Text('${c.count}'), ), ), floatingActionButton: FloatingActionButton( onPressed: () => Get.find<CounterController>().increment(), ), ); } }
When to Use GetBuilder
✅ Use GetBuilder When:
- Performance is critical
- Complex widgets with expensive builds
- Need fine-grained rebuild control (IDs)
- Non-reactive state updates
- Large lists or grids
❌ Don't Use GetBuilder When:
- Simple reactive state (use Obx)
- Real-time updates (use Obx)
- Two-way binding needed (use .obs)
Best Practices
Tip: Use IDs to rebuild only necessary widgets
dart// ✅ Good - Selective rebuild void updateName() { name = newName; update(['name-display']); } // ❌ Bad - Rebuilds everything void updateName() { name = newName; update(); }
Initialize Controllers Properly
dart// ✅ Good - Use Get.put() or init in GetBuilder Get.put(CounterController()); // Or GetBuilder<CounterController>( init: CounterController(), ... ) // ❌ Bad - Creating new instance every build GetBuilder<CounterController>( builder: (_) => CounterController(), // Wrong! )