Getting Started
Create your first entity with full CRUD operations, UI layouts, and permissions
Getting Started with Vyuh Entity System
This guide will walk you through creating your first entity using the Vyuh Entity System. We'll build a simple Product entity with full CRUD operations, UI layouts, and permissions in under 10 minutes.
Prerequisites
Before starting, ensure you have:
- A Flutter project with Vyuh Framework set up
- The
vyuh_entity_systempackage added to your dependencies - Basic understanding of Flutter and Dart
Step 1: Define Your Entity Model
First, create your entity model by extending EntityBase:
// lib/models/product.dart
import 'package:json_annotation/json_annotation.dart';
import 'package:vyuh_entity_system/vyuh_entity_system.dart';
part 'product.g.dart';
@JsonSerializable()
class Product extends EntityBase {
final String name;
final String description;
final double price;
final String category;
final int stockQuantity;
final bool isActive;
Product({
required super.id,
required super.schemaType,
required this.name,
required this.description,
required this.price,
required this.category,
required this.stockQuantity,
this.isActive = true,
super.createdAt,
super.layout,
super.modifiers,
});
factory Product.fromJson(Map<String, dynamic> json) =>
_$ProductFromJson(json);
@override
Map<String, dynamic> toJson() => _$ProductToJson(this);
}Step 2: Generate JSON Serialization
Run the build runner to generate serialization code:
flutter pub run build_runner build --delete-conflicting-outputsStep 3: Create the Entity API
Implement the API class for server communication:
// lib/api/product_api.dart
import 'package:vyuh_entity_system/vyuh_entity_system.dart';
import '../models/product.dart';
class ProductApi extends EntityApi<Product> {
ProductApi({required super.client});
@override
Product fromJson(Map<String, dynamic> json) => Product.fromJson(json);
@override
Future<List<Product>> performList({
int? offset,
int? limit,
String? sortBy,
String? sortOrder,
String? search,
}) async {
// The base class handles the API call
return super.list(
offset: offset,
limit: limit,
sortBy: sortBy,
sortOrder: sortOrder,
search: search,
);
}
@override
Future<Product?> performGetById(String id) async {
return super.byId(id);
}
@override
Future<Product> performCreate(Product entity) async {
return super.createNew(entity);
}
@override
Future<Product> performUpdate(String id, Product entity) async {
return super.update(id, entity);
}
@override
Future<void> performDelete(String id) async {
return super.delete(id);
}
}Step 4: Define Layouts
Create layouts for displaying your entity:
// lib/layouts/product_layouts.dart
import 'package:flutter/material.dart';
import 'package:vyuh_entity_system/vyuh_entity_system.dart';
import '../models/product.dart';
class ProductLayouts {
static EntityLayoutDescriptor<Product> create() {
return EntityLayoutDescriptor<Product>(
list: [
// Table layout for desktop
TableListLayout<Product>(
title: 'Products',
columns: [
TableColumn(
key: 'name',
label: 'Product Name',
getValue: (product) => product.name,
),
TableColumn(
key: 'category',
label: 'Category',
getValue: (product) => product.category,
),
TableColumn(
key: 'price',
label: 'Price',
getValue: (product) => '\$${product.price.toStringAsFixed(2)}',
),
TableColumn(
key: 'stockQuantity',
label: 'Stock',
getValue: (product) => product.stockQuantity.toString(),
),
TableColumn(
key: 'isActive',
label: 'Status',
getValue: (product) => product.isActive ? 'Active' : 'Inactive',
),
],
),
// Grid layout for mobile
GridListLayout<Product>(
title: 'Products',
buildCard: (context, product) => Card(
child: ListTile(
title: Text(product.name),
subtitle: Text('${product.category} • \$${product.price}'),
trailing: Chip(
label: Text('Stock: ${product.stockQuantity}'),
backgroundColor: product.stockQuantity > 0
? Colors.green.shade100
: Colors.red.shade100,
),
),
),
),
],
details: [
// Detail view layout
EntityLayoutConfiguration<Product>(
build: (context, product) => SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
product.name,
style: Theme.of(context).textTheme.headlineMedium,
),
const SizedBox(height: 8),
Text(
product.description,
style: Theme.of(context).textTheme.bodyLarge,
),
const SizedBox(height: 24),
_buildInfoRow('Category', product.category),
_buildInfoRow('Price', '\$${product.price.toStringAsFixed(2)}'),
_buildInfoRow('Stock Quantity', '${product.stockQuantity}'),
_buildInfoRow('Status', product.isActive ? 'Active' : 'Inactive'),
],
),
),
),
],
);
}
static Widget _buildInfoRow(String label, String value) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: Row(
children: [
SizedBox(
width: 150,
child: Text(
label,
style: const TextStyle(fontWeight: FontWeight.bold),
),
),
Text(value),
],
),
);
}
}Step 5: Create the Form
Define a form for creating and editing products:
// lib/forms/product_form.dart
import 'package:vyuh_feature_forms/vyuh_feature_forms.dart';
import 'package:vyuh_entity_system/vyuh_entity_system.dart';
import '../models/product.dart';
class ProductFormDescriptor extends EntityFormDescriptor<Product> {
@override
StepForm prepare(Product? entity) {
final isEdit = entity != null;
return singleFormAsStep(
title: isEdit ? 'Edit Product' : 'New Product',
form: FormBuilder(
title: 'Product Details',
fields: [
TextField(
name: 'name',
label: 'Product Name',
value: entity?.name,
validators: [required()],
),
TextField(
name: 'description',
label: 'Description',
value: entity?.description,
validators: [required()],
multiline: true,
),
NumberField(
name: 'price',
label: 'Price',
value: entity?.price,
validators: [required(), min(0)],
decimal: true,
),
SelectField(
name: 'category',
label: 'Category',
value: entity?.category,
options: [
Option(value: 'electronics', label: 'Electronics'),
Option(value: 'clothing', label: 'Clothing'),
Option(value: 'food', label: 'Food & Beverages'),
Option(value: 'other', label: 'Other'),
],
validators: [required()],
),
NumberField(
name: 'stockQuantity',
label: 'Stock Quantity',
value: entity?.stockQuantity,
validators: [required(), min(0)],
),
ToggleField(
name: 'isActive',
label: 'Active',
value: entity?.isActive ?? true,
),
],
),
);
}
@override
Map<String, dynamic> toFormData(Product entity) {
return {
'name': entity.name,
'description': entity.description,
'price': entity.price,
'category': entity.category,
'stockQuantity': entity.stockQuantity,
'isActive': entity.isActive,
};
}
@override
Product fromFormData(Map<String, dynamic> data, {Product? entity}) {
return Product(
id: entity?.id ?? '',
schemaType: 'products',
name: data['name'],
description: data['description'],
price: data['price'],
category: data['category'],
stockQuantity: data['stockQuantity'],
isActive: data['isActive'],
createdAt: entity?.createdAt ?? DateTime.now(),
);
}
}Step 6: Configure the Entity
Bring everything together in the entity configuration:
// lib/config/product_config.dart
import 'package:flutter/material.dart';
import 'package:vyuh_entity_system/vyuh_entity_system.dart';
import '../models/product.dart';
import '../api/product_api.dart';
import '../layouts/product_layouts.dart';
import '../forms/product_form.dart';
class ProductConfig {
static final instance = EntityConfiguration<Product>(
metadata: EntityMetadata(
identifier: 'products',
name: 'Product',
pluralName: 'Products',
description: 'Manage your product catalog',
icon: Icons.inventory,
themeColor: Colors.blue,
category: 'Inventory',
route: EntityRouteBuilder.fromIdentifier('products'),
),
api: (client) => ProductApi(client: client),
layouts: ProductLayouts.create(),
form: ProductFormDescriptor(),
actions: EntityActions<Product>(
list: [
EntityAction(
label: 'Export',
icon: Icons.download,
onTap: (context, entities) async {
// Export logic here
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Exporting ${entities.length} products')),
);
},
),
],
single: [
EntityAction(
label: 'Duplicate',
icon: Icons.copy,
onTap: (context, entities) async {
final product = entities.first;
// Duplicate logic here
},
),
],
),
);
}Step 7: Register the Entity
Register your entity in your feature descriptor:
// lib/feature_descriptor.dart
import 'package:vyuh_core/vyuh_core.dart';
import 'package:vyuh_entity_system/vyuh_entity_system.dart';
import 'config/product_config.dart';
class InventoryFeatureDescriptor extends FeatureDescriptor {
InventoryFeatureDescriptor()
: super(
name: 'inventory',
title: 'Inventory Management',
description: 'Product inventory features',
icon: Icons.inventory,
);
@override
void init(Vyuh vyuh) {
// Register the product entity
vyuh.entity?.register<Product>(ProductConfig.instance);
}
}Step 8: Use Your Entity
Your entity is now ready to use! The entity system automatically provides:
Routes
/products- List all products/products/new- Create new product/products/:id- View product details/products/:id/edit- Edit product
Navigation
Access your entity through the dashboard or use the command palette (Cmd/Ctrl+K) to search for "Products".
Programmatic Access
// In your widgets
final provider = EntityProvider.of<Product>(context);
// List products
final products = await provider.list();
// Get single product
final product = await provider.byId('product-id');
// Create product
final newProduct = await provider.create(product);
// Update product
await provider.update('product-id', updatedProduct);
// Delete product
await provider.delete('product-id');What's Next?
Congratulations! You've created your first entity with:
- ✅ Full CRUD operations
- ✅ Multiple UI layouts
- ✅ Form validation
- ✅ Custom actions
- ✅ Automatic routing
Explore Advanced Features
-
Add Permissions - See Permissions
if (await hasPermission(Permission.create('products'))) { // Show create button } -
Custom Layouts - See Layouts and Views
class ProductMapLayout extends EntityLayout<Product> { // Custom map visualization } -
Entity Relationships - See Entity Lifecycle
class Product extends EntityBase { final String categoryId; // Reference to Category entity // ... other fields } -
Activity Tracking - See Activity Tracking
// Automatic audit trail for all operations
Troubleshooting
Common Issues
-
Build runner fails
- Ensure
json_annotationandbuild_runnerare in dev_dependencies - Delete
.dart_tool/buildand try again
- Ensure
-
Routes not working
- Make sure entity is registered in feature descriptor
- Check that feature is loaded in main app
-
API calls failing
- Verify server endpoints match entity identifier
- Check authentication configuration
Getting Help
- Check the Examples for more complex scenarios
- Review Best Practices for optimization tips
- File issues in the repository for bugs or feature requests
Ready for more? Continue to Entity Configuration for a deep dive into all configuration options!