Based on kauemurakami/getx_pattern. ALL code generated for this project MUST strictly follow this pattern.
lib/
├── main.dart # App entry point, runApp(GetMaterialApp(...))
└── app/
├── data/ # Shared data layer (available to ALL modules)
│ ├── models/ # Isar collection classes
│ │ ├── product_model.dart
│ │ ├── cost_lot_model.dart
│ │ ├── sale_model.dart
│ │ ├── sale_item_model.dart
│ │ ├── daily_balance_model.dart
│ │ └── note_model.dart
│ ├── providers/ # Data sources (DB, API)
│ │ └── database_provider.dart # Isar singleton
│ ├── services/ # Business logic services
│ │ ├── cost_calculation_service.dart
│ │ └── export_service.dart
│ └── enums/
│ ├── promotion_type.dart
│ └── export_period.dart
│
├── modules/ # One module per screen/feature
│ ├── home/
│ │ ├── home_page.dart
│ │ ├── home_controller.dart
│ │ └── home_binding.dart
│ ├── products/
│ │ ├── products_page.dart
│ │ ├── products_controller.dart
│ │ ├── products_binding.dart
│ │ └── repository/
│ │ └── products_repository.dart
│ └── ... (same pattern for each module)
│
├── routes/
│ ├── app_routes.dart # Route name constants
│ └── app_pages.dart # GetPage list with bindings
│
├── core/
│ ├── values/
│ │ ├── strings.dart # Spanish UI strings
│ │ └── colors.dart
│ ├── theme/
│ │ ├── app_theme.dart # Light + Dark ThemeData
│ │ ├── text_theme.dart
│ │ └── color_theme.dart
│ └── utils/
│ ├── currency_formatter.dart
│ └── date_helpers.dart
│
└── global_widgets/ # Widgets reusable across modules
├── product_card.dart
├── price_tag.dart
├── quick_action_button.dart
└── responsive_layout.dart
Every controller has exactly ONE repository. If a page needs data from multiple entities,
use multiple GetX<Controller> widgets, each with its own controller.
// ✅ CORRECT
class SalesController extends GetxController {
final SalesRepository repository;
SalesController({required this.repository});
}
// ❌ WRONG — never two repositories in one controller
class SalesController extends GetxController {
final SalesRepository salesRepo;
final ProductsRepository productsRepo; // NO!
}Controllers are initialized via Bindings, never manually with Get.put() in pages.
class HomeBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut<HomeController>(() => HomeController(
repository: HomeRepository(),
));
}
}class HomePage extends GetView<HomeController> {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Obx(() => Text('${controller.totalSales.value}')),
);
}
}class HomeController extends GetxController {
final totalSales = 0.0.obs;
final products = <Product>[].obs;
void updateSales(double amount) {
totalSales.value += amount;
}
}Repositories bridge controllers and providers. They exist INSIDE each module folder.
class ProductsRepository {
final DatabaseProvider _db = Get.find<DatabaseProvider>();
Future<List<Product>> getAll() => _db.getAllProducts();
Future<void> save(Product p) => _db.saveProduct(p);
Future<void> delete(int id) => _db.deleteProduct(id);
}Providers live in data/providers/ and handle direct DB/API communication.
class DatabaseProvider extends GetxService {
late Isar isar;
Future<DatabaseProvider> init() async {
final dir = await getApplicationDocumentsDirectory();
isar = await Isar.open(
[ProductSchema, SaleSchema, ...],
directory: dir.path,
);
return this;
}
Future<List<Product>> getAllProducts() async {
return await isar.products.where().findAll();
}
}// app_routes.dart
abstract class AppRoutes {
static const home = '/home';
static const products = '/products';
static const sales = '/sales';
static const analytics = '/analytics';
static const calendar = '/calendar';
static const notes = '/notes';
static const settings = '/settings';
static const cashRegister = '/cash-register';
}// app_pages.dart
class AppPages {
static final pages = [
GetPage(
name: AppRoutes.home,
page: () => const HomePage(),
binding: HomeBinding(),
),
// ... etc
];
}| Element | Convention | Example |
|---|---|---|
| Models | snake_case_model.dart |
product_model.dart |
| Controllers | snake_case_controller.dart |
sales_controller.dart |
| Pages | snake_case_page.dart |
products_page.dart |
| Bindings | snake_case_binding.dart |
home_binding.dart |
| Repositories | snake_case_repository.dart |
products_repository.dart |
| Variables | camelCase | totalSales, currentStock |
| Classes | PascalCase | ProductModel, SalesController |
// In GetMaterialApp
GetMaterialApp(
defaultTransition: Transition.noTransition,
transitionDuration: Duration.zero,
// ...
)NEVER use SharedPreferences, GetStorage, Hive, or SQLite.
All persistent data goes through Isar via DatabaseProvider.