Introduction to GetX Lifecycle
GetX provides a powerful lifecycle system that automatically manages the creation, state, and disposal of controllers and dependencies. Understanding this lifecycle is essential for building efficient, memory-safe Flutter applications. This guide covers the full lifecycle of GetX controllers, widgets, routes, and navigation.
- Controller Lifecycle
Every controller that extends GetxController follows a predictable lifecycle. You can hook into it using the following methods:
onInit()– Called immediately after the controller is created. Used for initializing data, setting up workers, or starting API calls.onReady()– Called after the widget tree has been rendered. Ideal for UI-dependent actions (e.g., showing dialogs, starting animations).onClose()– Called when the controller is about to be destroyed. Clean up streams, timers, or any resources.
- Widget Lifecycle & Controllers
GetX widgets like GetView, GetWidget, Obx, and GetBuilder integrate seamlessly with controller lifecycle.
When GetView is used, the controller is created when the view is first built (if not already registered). The controller is then disposed when the view is removed from the widget tree (unless permanent: true).
These widgets listen to controllers but do not affect their lifecycle. The controller's lifecycle is independent; the widgets simply rebuild when notified.
- Route & Navigation Lifecycle
GetX's navigation system (using Get.to, Get.off, etc.) automatically manages controllers associated with routes, especially when bindings are used.
When the route is pushed, the binding's dependencies() is called, registering the controller. When the route is popped, the controller is automatically disposed (unless marked permanent: true or fenix: true).
The controller is created when Get.put is called and disposed when the route is removed (because the controller is tied to the route's lifecycle).
- Full Lifecycle Sequence
Here's the typical order when navigating to a new route with a binding and controller:
- Navigation triggered (
Get.toNamed('/details')).
- Navigation triggered (
- Binding's
dependencies()runs, registering controllers (lazy).
- Binding's
- Route page widget is created (but not yet built).
- Controller's constructor runs (if lazy is used, it runs only when first accessed).
- Controller's
onInit()runs (if the controller is accessed in the view).
- Controller's
- Widget is built – the view uses
GetView<MyController>orGet.find()to access the controller.
- Widget is built – the view uses
- After the first frame, controller's
onReady()runs.
- After the first frame, controller's
- While route is active, controllers can be accessed and modified.
- When route is popped, GetX checks if the controller is permanent; if not, it calls
onClose()and disposes the controller.
- When route is popped, GetX checks if the controller is permanent; if not, it calls
- Managing Controller Lifecycle
You can control how long a controller lives using injection options.
- Best Practices
- Use
onInitfor initialization – Never put async code in the constructor; useonInitinstead. - Use
onReadyfor UI-related tasks – Safe to show dialogs or start animations after the UI is ready. - Clean up in
onClose– Dispose streams, cancel timers, and remove listeners to avoid memory leaks. - Use bindings – Automatically tie controllers to routes, ensuring proper disposal.
- Avoid
permanent: trueunless necessary – Let GetX manage lifecycle for most controllers. - Use
Get.isRegistered<T>()– Check before accessing a controller that might not exist yet.
- Common Mistakes
- ❌ Calling
Get.putinside build – Creates a new controller on each rebuild. ✅ Place ininitState, bindings, or useGet.lazyPut. - ❌ Not calling
super.onInit()– Breaks internal setup. ✅ Always callsuper.onInit()when overriding. - ❌ Forgetting to dispose resources in
onClose– Causes memory leaks. ✅ Clean up streams, timers, and controllers manually if needed. - ❌ Using
Get.findbefore registration – Throws an error. ✅ Ensure registration withGet.putor bindings first.
FAQ
- Q: When is
onReadycalled?
A: After the widget tree is rendered for the first time, which is typically after the first frame. It's called once per controller. - Q: Are controllers disposed when using
Get.off?
A: Yes, if the controller was tied to that route (e.g., via binding), it will be disposed. If it was marked permanent, it stays. - Q: What's the difference between
onCloseandonDelete?
A:onCloseis the standard lifecycle method;onDeleteis not part of GetX. UseonClose. - Q: How do I check if a controller is still alive?
A: UseGet.isRegistered<MyController>(). - Q: Can I call
Get.putinonInitof another controller?
A: Yes, but be mindful of circular dependencies. UseGet.lazyPutor pass through constructors when possible. - Q: Does GetX dispose controllers when the app goes to background?
A: No, controllers persist. They are only disposed when the associated route is removed or you manually callGet.delete.
Conclusion
Mastering the GetX lifecycle allows you to build efficient, leak-free Flutter apps. By leveraging onInit, onReady, and onClose, and using bindings to manage dependencies, you can ensure your controllers are created and disposed exactly when needed.