- Overview
- Installation
- Architecture
- Commands
- JSON Configuration
- Code Generation Process
- Generated File Structure
- Templates
- Helper Functions
- Advanced Usage
- Examples
- Troubleshooting
Ercode CLI is a powerful code generator tool for Flutter applications that automates the creation of boilerplate code for CRUD (Create, Read, Update, Delete) operations. It generates a complete module structure including models, repositories, controllers, and views based on a simple JSON configuration file.
- 🚀 Rapid Development: Generate complete CRUD modules in seconds
- 📝 JSON-based Configuration: Simple, declarative module definitions
- 🏗️ Structured Architecture: Follows clean architecture principles
- 🎨 Customizable Templates: Pre-built templates for common UI patterns
- 🔧 Helper Utilities: Built-in API client, state management, and widgets
- 📦 Dependency Management: Auto-installs required packages
For each module, Ercode CLI generates:
- Model: Data class with JSON serialization
- Repository: API communication layer
- List View: Display data in a list with pagination
- Add View: Form for creating/editing records
- Detail View: Display single record details
- Controllers: State management for each view
- Dart SDK >= 2.18.6 < 3.0.0
- Flutter SDK (latest stable version recommended)
# Using dart
dart pub global activate ercode_cli
# Or using flutter
flutter pub global activate ercode_cliercodeYou should see:
ercode
init to init ercode generator
generate <folder/generator.json> to generate code
Ercode CLI follows a modular architecture with clear separation of concerns:
ercode_cli/
├── bin/
│ └── ercode_cli.dart # CLI entry point
├── lib/
│ ├── ercode.dart # Init project functionality
│ ├── generate_code.dart # Code generation orchestrator
│ ├── models/ # Data models
│ │ ├── module.dart # Module configuration model
│ │ └── field.dart # Field configuration model
│ ├── generator/ # Code generators
│ │ ├── model_generator.dart
│ │ ├── repo_generator.dart
│ │ ├── list/ # List view generators
│ │ ├── add/ # Add view generators
│ │ └── detail/ # Detail view generators
│ ├── templates/ # Code templates
│ │ ├── model.dart
│ │ ├── repository.dart
│ │ ├── list/
│ │ ├── add/
│ │ └── detail/
│ └── helpers/ # Utility functions
│ ├── extension.dart # String extensions
│ ├── rb_helpers.dart # Helper functions
│ └── cli/ # CLI utilities
└── templates/
└── init/ # Initial project setup
ercode initWhat it does:
-
Installs required dependencies:
dio: HTTP client for API callspretty_dio_logger: Request/response loggingvalidators: Input validationcached_network_image: Image cachingimage_picker: Select images from gallery/cameraimage_crop: Crop imagesflutter_easyloading: Loading indicatorspath: File path manipulation
-
Creates helper files in
lib/helpers/:state_util.dart: State management utilitiesapi.dart: API client wrapperconstants.dart: App constants (API URL, tokens)validator.dart: Form validation helpersrb_helpers.dart: General helper functions
-
Creates reusable widgets in
lib/widgets/:crop_image.dart: Image cropping widgetdialog_confirm.dart: Confirmation dialogdropdown_spinner.dart: Dropdown with loading stateedit_text.dart: Custom text inputerror_layout.dart: Error state displayimage_text.dart: Image with text widgetloading_layout.dart: Loading state displaypick_image.dart: Image picker widgetshow_page.dart: Page navigation helpersubmit_button.dart: Submit button with loadingtext_info.dart: Info text display
-
Creates models in
lib/models/:response_data.dart: Standardized API response model
-
Modifies
lib/main.dart:- Adds required imports
- Configures EasyLoading
- Sets up navigation key
Post-initialization:
Edit lib/helpers/constants.dart to set your API configuration:
const baseUrl = 'https://your-api.com/api';
const apiToken = 'your-api-token'; // Optionalercode generate <path-to-json-config>Examples:
# Generate from generator folder
ercode generate generator/book.json
# Generate from any path
ercode generate config/user.json
# Force overwrite existing files
ercode generate generator/book.json forceWhat it does: Reads the JSON configuration and generates:
- Model class
- Repository class
- List controller & view
- Add/Edit controller & view
- Detail controller & view
Force Mode:
By default, existing files are not overwritten. Use the force argument to replace existing files.
{
"name": "module_name",
"api": "api_endpoint",
"modelName": "ModelName",
"only": [],
"fields": [
{
"name": "field_name",
"type": "String",
"input": "text",
"hidden": false,
"primary": false,
"list": true
}
]
}| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
name |
String | ✅ Yes | - | Module name (used for folder/file names) |
api |
String | ❌ No | Same as name |
API endpoint path |
modelName |
String | ❌ No | Same as name |
Model class name |
only |
Array | ❌ No | [] |
Limit generation to specific components |
fields |
Array | ✅ Yes | - | List of field configurations |
only values:
"model": Generate only the model class"repository": Generate only the repository"list": Generate list controller & view"add": Generate add/edit controller & view"detail": Generate detail controller & view
Example - Generate only model and repository:
{
"name": "product",
"only": ["model", "repository"],
"fields": [...]
}| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
name |
String | ✅ Yes | - | Field name (snake_case recommended) |
type |
String | ❌ No | "String" |
Dart data type |
input |
String | ❌ No | "text" |
Input type for forms |
hidden |
Boolean | ❌ No | false |
Hide from create/edit forms |
primary |
Boolean | ❌ No | false |
Mark as primary key (usually id) |
list |
Boolean | ❌ No | false |
Show in list view |
Supported Data Types:
String: Text dataint: Integer numbersbool: Boolean valuesdouble: Floating-point numbers (experimental)
Supported Input Types:
text: Text input field (default)image: Image picker with upload
Field Naming Convention:
- Use snake_case in JSON:
"release_year" - Converts to camelCase in code:
releaseYear - Converts to PascalCase for classes:
ReleaseYear
Every model automatically includes timestamp fields:
createdAt: DateTime?updatedAt: DateTime?deletedAt: DateTime?
These are added automatically and don't need to be specified in the JSON.
The generator reads the JSON file and creates a Module object:
// lib/models/module.dart
class Module {
final String name;
final String api;
final String modelName;
final List<String> only;
List<Field>? fields;
}
// lib/models/field.dart
class Field {
final String name;
final String type;
final String input;
final bool hidden;
final bool showList;
final bool primary;
}Location: lib/modules/{module_name}/data/{model_name}.dart
Generated Code:
- Class with all fields as properties
- Constructor with named parameters
toMap(): Convert to Map<String, dynamic>fromMap(): Create instance from MaptoJson(): Serialize to JSON stringfromJson(): Deserialize from JSON stringtoString(): String representation
Example Output:
class Book {
final int id;
final String cover;
final String name;
final String author;
DateTime? createdAt;
DateTime? updatedAt;
DateTime? deletedAt;
Book({
required this.id,
required this.cover,
required this.name,
required this.author,
this.createdAt,
this.updatedAt,
this.deletedAt,
});
Map<String, dynamic> toMap() {
return {
'id': id,
'cover': cover,
'name': name,
'author': author,
'created_at': createdAt,
'updated_at': updatedAt,
'deleted_at': deletedAt,
};
}
factory Book.fromMap(Map<String, dynamic> map) {
return Book(
id: map['id'] ?? 0,
cover: map['cover'] ?? '',
name: map['name'] ?? '',
author: map['author'] ?? '',
createdAt: DateTime.tryParse(map['created_at'] ?? ''),
updatedAt: DateTime.tryParse(map['updated_at'] ?? ''),
deletedAt: DateTime.tryParse(map['deleted_at'] ?? ''),
);
}
String toJson() => json.encode(toMap());
factory Book.fromJson(String source) =>
Book.fromMap(json.decode(source));
@override
String toString() => 'Book(id: $id, cover: $cover, name: $name, author: $author)';
}Location: lib/modules/{module_name}/data/{module_name}_repository.dart
Generated Methods:
getData(): Fetch list with optional paginationgetDetail(id): Fetch single recordsave(url, params): Create or update recorddelete(id): Delete record
Features:
- Error handling with callbacks
- Success/error callbacks
- Integration with API helper
- ResponseData wrapper for consistent responses
Files Generated:
- Controller:
lib/modules/{module_name}/controllers/{module_name}_controller.dart - View:
lib/modules/{module_name}/views/{module_name}_view.dart - View Item:
lib/modules/{module_name}/views/{module_name}_view_item.dart
List Controller Features:
- Fetch data from repository
- Handle loading/error states
- Navigation to detail/add views
- Pull-to-refresh support
List View Features:
- AppBar with add button
- Loading indicator
- Error display
- List of items (only shows fields marked with
"list": true) - Pull to refresh
- Tap to view details
Files Generated:
- Controller:
lib/modules/{module_name}/controllers/{module_name}_add_controller.dart - View:
lib/modules/{module_name}/views/{module_name}_add_view.dart - View Item:
lib/modules/{module_name}/views/{module_name}_add_view_item.dart
Add Controller Features:
- Form validation
- Edit mode detection
- Image upload handling
- Success/error callbacks
- Auto-navigation on success
Add View Features:
- Form with validation
- Text fields for text inputs
- Image picker for image inputs
- Submit button with loading state
- Edit mode support (pre-fills form)
Input Type Handling:
Text Input:
// Generated controller code
final txtName = TextEditingController();
// Form parameters
'name': txtName.text,Image Input:
// Generated controller code
var coverImage = '';
var coverImageError = '';
setCoverImage(String v) {
setState(() {
coverImage = v;
});
}
// Upload parameters
if (coverImage.isNotEmpty) {
File file = File(coverImage);
params['cover_image'] = await dio.MultipartFile.fromFile(
file.path,
filename: basename(file.path),
);
}Files Generated:
- Controller:
lib/modules/{module_name}/controllers/{module_name}_detail_controller.dart - View:
lib/modules/{module_name}/views/{module_name}_detail_view.dart - View Item:
lib/modules/{module_name}/views/{module_name}_detail_view_item.dart
Detail Controller Features:
- Fetch single record
- Edit navigation
- Delete confirmation
- Loading states
Detail View Features:
- Display all field values
- Edit button
- Delete button with confirmation
- Loading indicator
- Error handling
After running ercode generate generator/book.json, the following structure is created:
lib/
└── modules/
└── book/
├── data/
│ ├── book.dart # Model
│ └── book_repository.dart # Repository
├── controllers/
│ ├── book_controller.dart # List controller
│ ├── book_add_controller.dart # Add/Edit controller
│ └── book_detail_controller.dart # Detail controller
└── views/
├── book_view.dart # List view
├── book_view_item.dart # List item widget
├── book_add_view.dart # Add/Edit view
├── book_add_view_item.dart # Add/Edit form widget
├── book_detail_view.dart # Detail view
└── book_detail_view_item.dart # Detail display widget
Templates are located in lib/templates/ and use placeholder strings that are replaced during generation:
| Placeholder | Description | Example Replacement |
|---|---|---|
@ClassName |
Pascal case class name | Book |
@className |
Pascal case module name | Book |
@modelName |
Model class name | Book |
@varName |
Camel case variable name | book |
@filename |
Snake case file name | book |
@filenameModel |
Snake case model file | book |
@packageName |
Project package name | my_app |
@api |
API endpoint | books |
@variableName |
Generated field declarations | final String name; |
@constructorVar |
Generated constructor params | required this.name, |
@toMapVar |
Generated toMap fields | 'name': name, |
@fromMapVar |
Generated fromMap fields | name: map['name'] ?? '', |
@toStringVar |
Generated toString fields | name: $name, |
@varInput |
Generated input variables | final txtName = TextEditingController(); |
@varParameters |
Generated form parameters | 'name': txtName.text, |
-
Model Template (
templates/model.dart)- Basic model class structure
- JSON serialization methods
-
Repository Template (
templates/repository.dart)- CRUD operation methods
- API integration
- Error handling
-
List Templates (
templates/list/)controller.dart: List state managementview.dart: List page scaffoldview_item.dart: List item widget
-
Add Templates (
templates/add/)controller.dart: Form state managementview.dart: Form page scaffoldview_item.dart: Form fields
-
Detail Templates (
templates/detail/)controller.dart: Detail state managementview.dart: Detail page scaffoldview_item.dart: Detail display
// Convert to PascalCase
"book_store".pascalCase // "BookStore"
// Convert to camelCase
"book_store".camelCase // "bookStore"
// Convert to snake_case
"Book Store".toLowerCaseWithUnderscore() // "book_store"
// Convert to spaces
"book_store".toLowerCaseSpace() // "book store"Generated API client with methods:
get(url, {params, onSuccess, onError}): GET requestpost(url, params, {onSuccess, onError}): POST requestput(url, params, {onSuccess, onError}): PUT requestdelete(url, {onSuccess, onError}): DELETE request
Features:
- Automatic auth token injection
- Request/response logging
- Error handling
- Base URL configuration
enum ConnectionStatus { loading, done, error }
// Navigation helper
Get.to(AnotherPage());
Get.back();Form validation helpers:
- Required field validation
- Email validation
- Min/max length validation
- Custom pattern validation
Utility functions:
readJsonFile(path): Read and parse JSON configformatterDartFile(content): Format Dart codetrace(error, stackTrace): Error logging
{
"name": "user",
"api": "users/v2",
"fields": [...]
}Generated repository will use /users/v2 instead of /user.
{
"name": "product",
"modelName": "item",
"fields": [...]
}- Folder:
lib/modules/product/ - Model class:
Item - Model file:
item.dart
Generate only specific components:
{
"name": "category",
"only": ["model", "repository"],
"fields": [...]
}{
"fields": [
{
"name": "id",
"type": "int",
"primary": true,
"hidden": true
}
]
}primary: true: Treats as ID field (skipped in forms)hidden: true: Excluded from create/edit views
Hidden Fields
{
"fields": [
{
"name": "created_by",
"type": "int",
"hidden": true
}
]
}Field exists in model but not in forms (useful for auto-populated server fields).
ercode generate generator/book.json forceReplaces existing files without prompts.
Config (generator/post.json):
{
"name": "post",
"fields": [
{
"name": "id",
"type": "int",
"primary": true,
"hidden": true
},
{
"name": "title",
"type": "String",
"list": true
},
{
"name": "content",
"type": "String"
},
{
"name": "author",
"type": "String",
"list": true
},
{
"name": "published",
"type": "bool"
}
]
}Generate:
ercode generate generator/post.jsonResult:
- Model with all fields
- Repository with CRUD operations
- List showing title and author
- Form with title, content, author, and published fields
- Detail view showing all information
Config (generator/product.json):
{
"name": "product",
"api": "products/v1",
"fields": [
{
"name": "id",
"type": "int",
"primary": true,
"hidden": true
},
{
"name": "image",
"type": "String",
"input": "image",
"list": true
},
{
"name": "name",
"type": "String",
"list": true
},
{
"name": "price",
"type": "int",
"list": true
},
{
"name": "description",
"type": "String"
},
{
"name": "stock",
"type": "int"
},
{
"name": "category",
"type": "String",
"list": true
}
]
}Generate:
ercode generate generator/product.jsonResult:
- Product model with image upload support
- List showing image, name, price, and category
- Form with image picker, text inputs
- API calls to
/products/v1endpoint
Config (generator/profile.json):
{
"name": "profile",
"only": ["model"],
"fields": [
{
"name": "id",
"type": "int",
"primary": true
},
{
"name": "name",
"type": "String"
},
{
"name": "email",
"type": "String"
},
{
"name": "avatar",
"type": "String"
},
{
"name": "bio",
"type": "String"
}
]
}Generate:
ercode generate generator/profile.jsonResult:
- Only generates
lib/modules/profile/data/profile.dart - No views, controllers, or repository
Solution: Use force mode to overwrite:
ercode generate generator/book.json forceSolution: Ensure you have the dependencies installed:
flutter pub getSolution:
- Check
lib/helpers/constants.darthas correctbaseUrl - Verify API endpoint in JSON config
- Check network connectivity
- Enable logging in
lib/helpers/api.dart
Solution: Run Flutter's code generator:
flutter pub get
dart fix --applySolution: Validate your JSON:
- Use a JSON validator
- Check for trailing commas
- Ensure all strings are quoted
- Verify proper bracket matching
Solution: Run Dart formatter:
dart format lib/Solution: Run init again:
ercode init- Use descriptive, lowercase module names
- Group related modules in subfolders
- Keep JSON configs in a
generator/folder
- Use snake_case in JSON configurations
- Avoid reserved Dart keywords
- Be consistent with backend field names
- Design RESTful endpoints
- Use consistent response formats
- Return proper HTTP status codes
- Commit JSON configs to version control
- Consider
.gitignorefor generated code if regenerating frequently - Document custom modifications to generated code
- Modify templates for project-specific needs
- Extend generated classes carefully
- Use inheritance for shared functionality
Ercode CLI expects the following response format from your API:
{
"message": "Success",
"data": {
"data": [
{"id": 1, "name": "Item 1"},
{"id": 2, "name": "Item 2"}
],
"current_page": 1,
"total": 50
}
}{
"message": "Success",
"data": [
{"id": 1, "name": "Item 1"},
{"id": 2, "name": "Item 2"}
]
}Use params['paginate'] = 'no' in repository call.
{
"message": "Success",
"data": {
"id": 1,
"name": "Item 1",
"description": "Description"
}
}{
"message": "Record created successfully"
}Ercode CLI automatically installs:
| Package | Version | Purpose |
|---|---|---|
| dio | ^4.0.6 | HTTP client |
| pretty_dio_logger | ^1.2.0-beta-1 | Request logging |
| validators | ^3.0.0 | Input validation |
| cached_network_image | ^3.2.3 | Image caching |
| image_picker | ^0.8.6+1 | Image selection |
| image_crop | ^0.4.1 | Image cropping |
| flutter_easyloading | ^3.0.5 | Loading indicators |
| path | ^1.8.3 | Path manipulation |
To contribute to Ercode CLI:
- Fork the repository on GitHub
- Create a feature branch
- Make your changes
- Write/update tests
- Submit a pull request
Repository: https://github.com/xrb21/ercode_cli
Check the LICENSE file for details.
For issues, questions, or contributions:
- GitHub Issues: https://github.com/xrb21/ercode_cli/issues
- Repository: https://github.com/xrb21/ercode_cli
See CHANGELOG.md for version history and updates.
Version: 1.0.2+1
Last Updated: 2026
Author: xrb21