--- description: "GetX conventions for {{PROJECT_NAME}} (legacy — migration available)" alwaysApply: true --- # GetX Standards — {{PROJECT_NAME}} > ⚠️ This project uses GetX. See `migration-agent` for incremental migration to Riverpod. ## Controller structure ```dart class ProductsController extends GetxController { final ProductRepository _repo; ProductsController(this._repo); final RxList products = [].obs; final RxBool isLoading = false.obs; final Rx error = Rx(null); @override void onInit() { super.onInit(); fetchProducts(); } Future fetchProducts() async { isLoading.value = true; error.value = null; try { products.value = await _repo.getProducts(); } catch (e) { error.value = e.toString(); } finally { isLoading.value = false; } } } ``` ## View pattern ```dart // Views extend GetView — never GetWidget or raw StatelessWidget class ProductsView extends GetView { @override Widget build(BuildContext context) { return Scaffold( body: Obx(() { if (controller.isLoading.value) return const ProductShimmer(); if (controller.error.value != null) return ErrorWidget(controller.error.value!); return ProductList(controller.products); }), ); } } ``` ## Rules - **NEVER** pass `BuildContext` into a controller - Use `Binding` classes for dependency injection — never `Get.put()` in a widget - Use `.obs` for all reactive state — never call `update()` on non-observable state - Use `Get.find()` only in `Binding` classes, not in widgets - **No business logic in Views** — controllers handle all logic ## File locations in {{PROJECT_NAME}} - `lib/features/[feature]/views/[feature]_view.dart` - `lib/features/[feature]/controllers/[feature]_controller.dart` - `lib/features/[feature]/bindings/[feature]_binding.dart` - `lib/features/[feature]/models/[feature]_model.dart`