đī¸ Model Code Samples
FLX CLI āĻĻāĻŋāϝāĻŧā§ āϤā§āϰāĻŋ āĻšāĻāϝāĻŧāĻž āĻŦāĻŋāĻāĻŋāύā§āύ āϧāϰāύā§āϰ Model class-āĻāϰ comprehensive examplesāĨ¤
đ¯ Basic Model Patternâ
Simple User Modelâ
import '../../domain/entities/user_entity.dart';
class UserModel extends UserEntity {
const UserModel({
required super.id,
required super.name,
required super.email,
super.profilePicture,
required super.createdAt,
});
/// Create UserModel from JSON
factory UserModel.fromJson(Map<String, dynamic> json) {
return UserModel(
id: json['id'] as String,
name: json['name'] as String,
email: json['email'] as String,
profilePicture: json['profile_picture'] as String?,
createdAt: DateTime.parse(json['created_at'] as String),
);
}
/// Convert to JSON
Map<String, dynamic> toJson() {
return {
'id': id,
'name': name,
'email': email,
'profile_picture': profilePicture,
'created_at': createdAt.toIso8601String(),
};
}
/// Create copy with updated fields
UserModel copyWith({
String? id,
String? name,
String? email,
String? profilePicture,
DateTime? createdAt,
}) {
return UserModel(
id: id ?? this.id,
name: name ?? this.name,
email: email ?? this.email,
profilePicture: profilePicture ?? this.profilePicture,
createdAt: createdAt ?? this.createdAt,
);
}
@override
String toString() {
return 'UserModel(id: $id, name: $name, email: $email, profilePicture: $profilePicture, createdAt: $createdAt)';
}
}
đĸ Complex Model with Nested Objectsâ
Product Model with Variantsâ
import '../../domain/entities/product_entity.dart';
class ProductModel extends ProductEntity {
const ProductModel({
required super.id,
required super.name,
required super.description,
required super.price,
required super.category,
required super.variants,
required super.inventory,
required super.images,
required super.isActive,
required super.createdAt,
required super.updatedAt,
});
factory ProductModel.fromJson(Map<String, dynamic> json) {
return ProductModel(
id: json['id'] as String,
name: json['name'] as String,
description: json['description'] as String,
price: (json['price'] as num).toDouble(),
category: ProductCategoryModel.fromJson(json['category']),
variants: (json['variants'] as List)
.map((variant) => ProductVariantModel.fromJson(variant))
.toList(),
inventory: ProductInventoryModel.fromJson(json['inventory']),
images: (json['images'] as List)
.map((image) => ProductImageModel.fromJson(image))
.toList(),
isActive: json['is_active'] as bool,
createdAt: DateTime.parse(json['created_at'] as String),
updatedAt: DateTime.parse(json['updated_at'] as String),
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'name': name,
'description': description,
'price': price,
'category': (category as ProductCategoryModel).toJson(),
'variants': variants
.map((variant) => (variant as ProductVariantModel).toJson())
.toList(),
'inventory': (inventory as ProductInventoryModel).toJson(),
'images': images
.map((image) => (image as ProductImageModel).toJson())
.toList(),
'is_active': isActive,
'created_at': createdAt.toIso8601String(),
'updated_at': updatedAt.toIso8601String(),
};
}
ProductModel copyWith({
String? id,
String? name,
String? description,
double? price,
ProductCategory? category,
List<ProductVariant>? variants,
ProductInventory? inventory,
List<ProductImage>? images,
bool? isActive,
DateTime? createdAt,
DateTime? updatedAt,
}) {
return ProductModel(
id: id ?? this.id,
name: name ?? this.name,
description: description ?? this.description,
price: price ?? this.price,
category: category ?? this.category,
variants: variants ?? this.variants,
inventory: inventory ?? this.inventory,
images: images ?? this.images,
isActive: isActive ?? this.isActive,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
);
}
}
class ProductCategoryModel extends ProductCategory {
const ProductCategoryModel({
required super.id,
required super.name,
required super.slug,
super.parentId,
required super.description,
});
factory ProductCategoryModel.fromJson(Map<String, dynamic> json) {
return ProductCategoryModel(
id: json['id'] as String,
name: json['name'] as String,
slug: json['slug'] as String,
parentId: json['parent_id'] as String?,
description: json['description'] as String,
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'name': name,
'slug': slug,
'parent_id': parentId,
'description': description,
};
}
}
class ProductVariantModel extends ProductVariant {
const ProductVariantModel({
required super.id,
required super.name,
required super.sku,
required super.price,
required super.attributes,
required super.stockQuantity,
});
factory ProductVariantModel.fromJson(Map<String, dynamic> json) {
return ProductVariantModel(
id: json['id'] as String,
name: json['name'] as String,
sku: json['sku'] as String,
price: (json['price'] as num).toDouble(),
attributes: Map<String, String>.from(json['attributes']),
stockQuantity: json['stock_quantity'] as int,
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'name': name,
'sku': sku,
'price': price,
'attributes': attributes,
'stock_quantity': stockQuantity,
};
}
}
đ Model with Date/Time Handlingâ
Order Model with Timestampsâ
import '../../domain/entities/order_entity.dart';
class OrderModel extends OrderEntity {
const OrderModel({
required super.id,
required super.userId,
required super.items,
required super.totalAmount,
required super.discountAmount,
required super.taxAmount,
required super.shippingAddress,
required super.billingAddress,
required super.status,
required super.paymentStatus,
required super.orderDate,
super.shippedDate,
super.deliveredDate,
super.cancelledDate,
required super.createdAt,
required super.updatedAt,
});
factory OrderModel.fromJson(Map<String, dynamic> json) {
return OrderModel(
id: json['id'] as String,
userId: json['user_id'] as String,
items: (json['items'] as List)
.map((item) => OrderItemModel.fromJson(item))
.toList(),
totalAmount: (json['total_amount'] as num).toDouble(),
discountAmount: (json['discount_amount'] as num).toDouble(),
taxAmount: (json['tax_amount'] as num).toDouble(),
shippingAddress: AddressModel.fromJson(json['shipping_address']),
billingAddress: AddressModel.fromJson(json['billing_address']),
status: OrderStatus.values.firstWhere(
(status) => status.name == json['status'],
),
paymentStatus: PaymentStatus.values.firstWhere(
(status) => status.name == json['payment_status'],
),
orderDate: DateTime.parse(json['order_date'] as String),
shippedDate: json['shipped_date'] != null
? DateTime.parse(json['shipped_date'] as String)
: null,
deliveredDate: json['delivered_date'] != null
? DateTime.parse(json['delivered_date'] as String)
: null,
cancelledDate: json['cancelled_date'] != null
? DateTime.parse(json['cancelled_date'] as String)
: null,
createdAt: DateTime.parse(json['created_at'] as String),
updatedAt: DateTime.parse(json['updated_at'] as String),
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'user_id': userId,
'items': items
.map((item) => (item as OrderItemModel).toJson())
.toList(),
'total_amount': totalAmount,
'discount_amount': discountAmount,
'tax_amount': taxAmount,
'shipping_address': (shippingAddress as AddressModel).toJson(),
'billing_address': (billingAddress as AddressModel).toJson(),
'status': status.name,
'payment_status': paymentStatus.name,
'order_date': orderDate.toIso8601String(),
'shipped_date': shippedDate?.toIso8601String(),
'delivered_date': deliveredDate?.toIso8601String(),
'cancelled_date': cancelledDate?.toIso8601String(),
'created_at': createdAt.toIso8601String(),
'updated_at': updatedAt.toIso8601String(),
};
}
/// Calculate final amount after discounts and taxes
double get finalAmount {
return (totalAmount - discountAmount) + taxAmount;
}
/// Check if order can be cancelled
bool get canBeCancelled {
return status == OrderStatus.pending || status == OrderStatus.confirmed;
}
/// Get order status in Bangla
String get statusInBangla {
switch (status) {
case OrderStatus.pending:
return 'āĻ
āĻĒā§āĻā§āώāĻŽāĻžāĻŖ';
case OrderStatus.confirmed:
return 'āύāĻŋāĻļā§āĻāĻŋāϤ';
case OrderStatus.processing:
return 'āĻĒā§āϰāϏā§āϏāĻŋāĻ';
case OrderStatus.shipped:
return 'āĻĒāĻžāĻ āĻžāύ⧠āĻšāϝāĻŧā§āĻā§';
case OrderStatus.delivered:
return 'āĻĄā§āϞāĻŋāĻāĻžāϰ āĻšāϝāĻŧā§āĻā§';
case OrderStatus.cancelled:
return 'āĻŦāĻžāϤāĻŋāϞ';
case OrderStatus.returned:
return 'āĻĢā§āϰāϤ';
}
}
}
đ Model with Validationâ
Registration Model with Validationâ
import '../../domain/entities/registration_entity.dart';
class RegistrationModel extends RegistrationEntity {
const RegistrationModel({
required super.firstName,
required super.lastName,
required super.email,
required super.phone,
required super.password,
required super.dateOfBirth,
required super.gender,
required super.address,
required super.acceptedTerms,
required super.marketingOptIn,
});
factory RegistrationModel.fromJson(Map<String, dynamic> json) {
return RegistrationModel(
firstName: json['first_name'] as String,
lastName: json['last_name'] as String,
email: json['email'] as String,
phone: json['phone'] as String,
password: json['password'] as String,
dateOfBirth: DateTime.parse(json['date_of_birth'] as String),
gender: Gender.values.firstWhere(
(gender) => gender.name == json['gender'],
),
address: AddressModel.fromJson(json['address']),
acceptedTerms: json['accepted_terms'] as bool,
marketingOptIn: json['marketing_opt_in'] as bool,
);
}
Map<String, dynamic> toJson() {
return {
'first_name': firstName,
'last_name': lastName,
'email': email,
'phone': phone,
'password': password, // Note: Never send password in real API
'date_of_birth': dateOfBirth.toIso8601String(),
'gender': gender.name,
'address': (address as AddressModel).toJson(),
'accepted_terms': acceptedTerms,
'marketing_opt_in': marketingOptIn,
};
}
/// Convert to API payload (without sensitive data)
Map<String, dynamic> toApiPayload() {
final payload = toJson();
payload.remove('password'); // Remove password from API payload
return payload;
}
/// Validate registration data
List<String> validate() {
final errors = <String>[];
// Name validation
if (firstName.trim().isEmpty) {
errors.add('āĻĒā§āϰāĻĨāĻŽ āύāĻžāĻŽ āĻĒā§āϰāϝāĻŧā§āĻāύ');
}
if (lastName.trim().isEmpty) {
errors.add('āĻļā§āώ āύāĻžāĻŽ āĻĒā§āϰāϝāĻŧā§āĻāύ');
}
// Email validation
if (email.trim().isEmpty) {
errors.add('āĻāĻŽā§āĻāϞ āĻĒā§āϰāϝāĻŧā§āĻāύ');
} else if (!_isValidEmail(email)) {
errors.add('āϏāĻ āĻŋāĻ āĻāĻŽā§āĻāϞ āĻĢāϰāĻŽā§āϝāĻžāĻ āĻĒā§āϰāϝāĻŧā§āĻāύ');
}
// Phone validation
if (phone.trim().isEmpty) {
errors.add('āĻĢā§āύ āύāĻŽā§āĻŦāϰ āĻĒā§āϰāϝāĻŧā§āĻāύ');
} else if (!_isValidPhone(phone)) {
errors.add('āϏāĻ āĻŋāĻ āĻĢā§āύ āύāĻŽā§āĻŦāϰ āĻĒā§āϰāϝāĻŧā§āĻāύ');
}
// Password validation
if (password.isEmpty) {
errors.add('āĻĒāĻžāϏāĻāϝāĻŧāĻžāϰā§āĻĄ āĻĒā§āϰāϝāĻŧā§āĻāύ');
} else if (password.length < 8) {
errors.add('āĻĒāĻžāϏāĻāϝāĻŧāĻžāϰā§āĻĄ āĻāĻŽāĻĒāĻā§āĻˇā§ ā§Ž āĻ
āĻā§āώāϰā§āϰ āĻšāϤ⧠āĻšāĻŦā§');
} else if (!_hasUppercase(password)) {
errors.add('āĻĒāĻžāϏāĻāϝāĻŧāĻžāϰā§āĻĄā§ āĻāĻŽāĻĒāĻā§āώ⧠āĻāĻāĻāĻŋ āĻŦāĻĄāĻŧ āĻšāĻžāϤā§āϰ āĻ
āĻā§āώāϰ āĻĨāĻžāĻāϤ⧠āĻšāĻŦā§');
} else if (!_hasLowercase(password)) {
errors.add('āĻĒāĻžāϏāĻāϝāĻŧāĻžāϰā§āĻĄā§ āĻāĻŽāĻĒāĻā§āώ⧠āĻāĻāĻāĻŋ āĻā§āĻ āĻšāĻžāϤā§āϰ āĻ
āĻā§āώāϰ āĻĨāĻžāĻāϤ⧠āĻšāĻŦā§');
} else if (!_hasNumber(password)) {
errors.add('āĻĒāĻžāϏāĻāϝāĻŧāĻžāϰā§āĻĄā§ āĻāĻŽāĻĒāĻā§āώ⧠āĻāĻāĻāĻŋ āϏāĻāĻā§āϝāĻž āĻĨāĻžāĻāϤ⧠āĻšāĻŦā§');
}
// Age validation
final age = DateTime.now().difference(dateOfBirth).inDays ~/ 365;
if (age < 13) {
errors.add('āύā§āϝā§āύāϤāĻŽ āĻŦāϝāĻŧāϏ ā§§ā§Š āĻŦāĻāϰ āĻšāϤ⧠āĻšāĻŦā§');
} else if (age > 120) {
errors.add('āĻ
āĻŦā§āϧ āĻāύā§āĻŽ āϤāĻžāϰāĻŋāĻ');
}
// Terms validation
if (!acceptedTerms) {
errors.add('āĻļāϰā§āϤāĻžāĻŦāϞ⧠āϏā§āĻŦā§āĻāĻžāϰ āĻāϰāϤ⧠āĻšāĻŦā§');
}
return errors;
}
/// Check if registration is valid
bool get isValid => validate().isEmpty;
// Private validation methods
bool _isValidEmail(String email) {
return RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$').hasMatch(email);
}
bool _isValidPhone(String phone) {
// Bangladeshi phone number validation
return RegExp(r'^(\+88)?01[3-9]\d{8}$').hasMatch(phone);
}
bool _hasUppercase(String password) {
return RegExp(r'[A-Z]').hasMatch(password);
}
bool _hasLowercase(String password) {
return RegExp(r'[a-z]').hasMatch(password);
}
bool _hasNumber(String password) {
return RegExp(r'\d').hasMatch(password);
}
}
đ Model with Calculationsâ
Invoice Model with Calculationsâ
import '../../domain/entities/invoice_entity.dart';
class InvoiceModel extends InvoiceEntity {
const InvoiceModel({
required super.id,
required super.invoiceNumber,
required super.customerId,
required super.customerName,
required super.customerEmail,
required super.items,
required super.subtotal,
required super.taxRate,
required super.discountAmount,
required super.shippingCost,
required super.currency,
required super.status,
required super.issueDate,
required super.dueDate,
super.paidDate,
required super.notes,
});
factory InvoiceModel.fromJson(Map<String, dynamic> json) {
return InvoiceModel(
id: json['id'] as String,
invoiceNumber: json['invoice_number'] as String,
customerId: json['customer_id'] as String,
customerName: json['customer_name'] as String,
customerEmail: json['customer_email'] as String,
items: (json['items'] as List)
.map((item) => InvoiceItemModel.fromJson(item))
.toList(),
subtotal: (json['subtotal'] as num).toDouble(),
taxRate: (json['tax_rate'] as num).toDouble(),
discountAmount: (json['discount_amount'] as num).toDouble(),
shippingCost: (json['shipping_cost'] as num).toDouble(),
currency: json['currency'] as String,
status: InvoiceStatus.values.firstWhere(
(status) => status.name == json['status'],
),
issueDate: DateTime.parse(json['issue_date'] as String),
dueDate: DateTime.parse(json['due_date'] as String),
paidDate: json['paid_date'] != null
? DateTime.parse(json['paid_date'] as String)
: null,
notes: json['notes'] as String,
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'invoice_number': invoiceNumber,
'customer_id': customerId,
'customer_name': customerName,
'customer_email': customerEmail,
'items': items
.map((item) => (item as InvoiceItemModel).toJson())
.toList(),
'subtotal': subtotal,
'tax_rate': taxRate,
'discount_amount': discountAmount,
'shipping_cost': shippingCost,
'currency': currency,
'status': status.name,
'issue_date': issueDate.toIso8601String(),
'due_date': dueDate.toIso8601String(),
'paid_date': paidDate?.toIso8601String(),
'notes': notes,
};
}
/// Calculate tax amount
double get taxAmount {
return (subtotal - discountAmount) * (taxRate / 100);
}
/// Calculate total amount
double get totalAmount {
return subtotal - discountAmount + taxAmount + shippingCost;
}
/// Calculate discount percentage
double get discountPercentage {
if (subtotal == 0) return 0;
return (discountAmount / subtotal) * 100;
}
/// Check if invoice is overdue
bool get isOverdue {
if (status == InvoiceStatus.paid) return false;
return DateTime.now().isAfter(dueDate);
}
/// Get days until due
int get daysUntilDue {
final difference = dueDate.difference(DateTime.now());
return difference.inDays;
}
/// Get days overdue
int get daysOverdue {
if (!isOverdue) return 0;
final difference = DateTime.now().difference(dueDate);
return difference.inDays;
}
/// Format currency amount
String formatAmount(double amount) {
switch (currency.toUpperCase()) {
case 'BDT':
return 'ā§ŗ${amount.toStringAsFixed(2)}';
case 'USD':
return '\$${amount.toStringAsFixed(2)}';
case 'EUR':
return 'âŦ${amount.toStringAsFixed(2)}';
default:
return '${amount.toStringAsFixed(2)} $currency';
}
}
/// Get formatted total
String get formattedTotal => formatAmount(totalAmount);
/// Get status in Bangla
String get statusInBangla {
switch (status) {
case InvoiceStatus.draft:
return 'āĻāϏāĻĄāĻŧāĻž';
case InvoiceStatus.sent:
return 'āĻĒāĻžāĻ āĻžāύ⧠āĻšāϝāĻŧā§āĻā§';
case InvoiceStatus.viewed:
return 'āĻĻā§āĻāĻž āĻšāϝāĻŧā§āĻā§';
case InvoiceStatus.paid:
return 'āĻĒā§āĻŽā§āύā§āĻ āϏāĻŽā§āĻĒāύā§āύ';
case InvoiceStatus.overdue:
return 'āĻŽā§āϝāĻŧāĻžāĻĻ āĻļā§āώ';
case InvoiceStatus.cancelled:
return 'āĻŦāĻžāϤāĻŋāϞ';
}
}
/// Generate payment reminder message
String generatePaymentReminder() {
if (isOverdue) {
return 'āĻāĻĒāύāĻžāϰ āĻāύāĻāϝāĻŧā§āϏ #$invoiceNumber ${daysOverdue} āĻĻāĻŋāύ āĻŽā§āϝāĻŧāĻžāĻĻ āĻļā§āώāĨ¤ '
'āĻ
āύā§āĻā§āϰāĻš āĻāϰ⧠${formattedTotal} āĻĒā§āĻŽā§āύā§āĻ āĻāϰā§āύāĨ¤';
} else {
return 'āĻāĻĒāύāĻžāϰ āĻāύāĻāϝāĻŧā§āϏ #$invoiceNumber ${daysUntilDue} āĻĻāĻŋāύā§āϰ āĻŽāϧā§āϝ⧠āĻĒā§āĻŽā§āύā§āĻ āĻāϰā§āύāĨ¤ '
'āĻŽā§āĻ āĻĒāϰāĻŋāĻŽāĻžāĻŖ: ${formattedTotal}';
}
}
}
class InvoiceItemModel extends InvoiceItem {
const InvoiceItemModel({
required super.id,
required super.productId,
required super.productName,
required super.description,
required super.quantity,
required super.unitPrice,
required super.taxRate,
});
factory InvoiceItemModel.fromJson(Map<String, dynamic> json) {
return InvoiceItemModel(
id: json['id'] as String,
productId: json['product_id'] as String,
productName: json['product_name'] as String,
description: json['description'] as String,
quantity: json['quantity'] as int,
unitPrice: (json['unit_price'] as num).toDouble(),
taxRate: (json['tax_rate'] as num).toDouble(),
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'product_id': productId,
'product_name': productName,
'description': description,
'quantity': quantity,
'unit_price': unitPrice,
'tax_rate': taxRate,
};
}
/// Calculate line total
double get lineTotal {
return quantity * unitPrice;
}
/// Calculate tax amount for this line
double get taxAmount {
return lineTotal * (taxRate / 100);
}
/// Calculate total with tax
double get totalWithTax {
return lineTotal + taxAmount;
}
}
đ Model with State Managementâ
Task Model with State Transitionsâ
import '../../domain/entities/task_entity.dart';
class TaskModel extends TaskEntity {
const TaskModel({
required super.id,
required super.title,
required super.description,
required super.assigneeId,
required super.assigneeName,
required super.projectId,
required super.priority,
required super.status,
required super.tags,
required super.dueDate,
super.startDate,
super.completedDate,
required super.estimatedHours,
required super.actualHours,
required super.createdAt,
required super.updatedAt,
});
factory TaskModel.fromJson(Map<String, dynamic> json) {
return TaskModel(
id: json['id'] as String,
title: json['title'] as String,
description: json['description'] as String,
assigneeId: json['assignee_id'] as String,
assigneeName: json['assignee_name'] as String,
projectId: json['project_id'] as String,
priority: TaskPriority.values.firstWhere(
(priority) => priority.name == json['priority'],
),
status: TaskStatus.values.firstWhere(
(status) => status.name == json['status'],
),
tags: List<String>.from(json['tags']),
dueDate: DateTime.parse(json['due_date'] as String),
startDate: json['start_date'] != null
? DateTime.parse(json['start_date'] as String)
: null,
completedDate: json['completed_date'] != null
? DateTime.parse(json['completed_date'] as String)
: null,
estimatedHours: (json['estimated_hours'] as num).toDouble(),
actualHours: (json['actual_hours'] as num).toDouble(),
createdAt: DateTime.parse(json['created_at'] as String),
updatedAt: DateTime.parse(json['updated_at'] as String),
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'title': title,
'description': description,
'assignee_id': assigneeId,
'assignee_name': assigneeName,
'project_id': projectId,
'priority': priority.name,
'status': status.name,
'tags': tags,
'due_date': dueDate.toIso8601String(),
'start_date': startDate?.toIso8601String(),
'completed_date': completedDate?.toIso8601String(),
'estimated_hours': estimatedHours,
'actual_hours': actualHours,
'created_at': createdAt.toIso8601String(),
'updated_at': updatedAt.toIso8601String(),
};
}
/// Get allowed status transitions
List<TaskStatus> get allowedTransitions {
switch (status) {
case TaskStatus.todo:
return [TaskStatus.inProgress, TaskStatus.cancelled];
case TaskStatus.inProgress:
return [TaskStatus.todo, TaskStatus.review, TaskStatus.blocked];
case TaskStatus.review:
return [TaskStatus.inProgress, TaskStatus.done, TaskStatus.todo];
case TaskStatus.blocked:
return [TaskStatus.todo, TaskStatus.inProgress];
case TaskStatus.done:
return [TaskStatus.review]; // Can reopen for review
case TaskStatus.cancelled:
return [TaskStatus.todo]; // Can reactivate
}
}
/// Check if transition is allowed
bool canTransitionTo(TaskStatus newStatus) {
return allowedTransitions.contains(newStatus);
}
/// Transition to new status
TaskModel transitionTo(TaskStatus newStatus) {
if (!canTransitionTo(newStatus)) {
throw Exception('Invalid status transition from $status to $newStatus');
}
return copyWith(
status: newStatus,
startDate: newStatus == TaskStatus.inProgress && startDate == null
? DateTime.now()
: startDate,
completedDate: newStatus == TaskStatus.done
? DateTime.now()
: null,
updatedAt: DateTime.now(),
);
}
/// Calculate completion percentage
double get completionPercentage {
switch (status) {
case TaskStatus.todo:
return 0.0;
case TaskStatus.inProgress:
return 50.0;
case TaskStatus.review:
return 80.0;
case TaskStatus.blocked:
return 25.0;
case TaskStatus.done:
return 100.0;
case TaskStatus.cancelled:
return 0.0;
}
}
/// Check if task is overdue
bool get isOverdue {
if (status == TaskStatus.done || status == TaskStatus.cancelled) {
return false;
}
return DateTime.now().isAfter(dueDate);
}
/// Get priority in Bangla
String get priorityInBangla {
switch (priority) {
case TaskPriority.low:
return 'āĻāĻŽ';
case TaskPriority.medium:
return 'āĻŽāĻžāĻāĻžāϰāĻŋ';
case TaskPriority.high:
return 'āĻāĻā§āĻ';
case TaskPriority.urgent:
return 'āĻāϰā§āϰāĻŋ';
}
}
/// Get status in Bangla
String get statusInBangla {
switch (status) {
case TaskStatus.todo:
return 'āĻāϰāϤ⧠āĻšāĻŦā§';
case TaskStatus.inProgress:
return 'āĻāϞāĻŽāĻžāύ';
case TaskStatus.review:
return 'āĻĒāϰā§āϝāĻžāϞā§āĻāύāĻž';
case TaskStatus.blocked:
return 'āĻŦāĻžāϧāĻžāĻā§āϰāϏā§āϤ';
case TaskStatus.done:
return 'āϏāĻŽā§āĻĒāύā§āύ';
case TaskStatus.cancelled:
return 'āĻŦāĻžāϤāĻŋāϞ';
}
}
TaskModel copyWith({
String? id,
String? title,
String? description,
String? assigneeId,
String? assigneeName,
String? projectId,
TaskPriority? priority,
TaskStatus? status,
List<String>? tags,
DateTime? dueDate,
DateTime? startDate,
DateTime? completedDate,
double? estimatedHours,
double? actualHours,
DateTime? createdAt,
DateTime? updatedAt,
}) {
return TaskModel(
id: id ?? this.id,
title: title ?? this.title,
description: description ?? this.description,
assigneeId: assigneeId ?? this.assigneeId,
assigneeName: assigneeName ?? this.assigneeName,
projectId: projectId ?? this.projectId,
priority: priority ?? this.priority,
status: status ?? this.status,
tags: tags ?? this.tags,
dueDate: dueDate ?? this.dueDate,
startDate: startDate ?? this.startDate,
completedDate: completedDate ?? this.completedDate,
estimatedHours: estimatedHours ?? this.estimatedHours,
actualHours: actualHours ?? this.actualHours,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
);
}
}
đ¯ Key Model Patternsâ
- Basic CRUD Models: Simple JSON serialization
- Nested Object Models: Complex relationships
- Date/Time Models: Proper timestamp handling
- Validation Models: Built-in validation logic
- Calculation Models: Business logic calculations
- State Machine Models: Status transitions
- Localization Models: Multi-language support
- Currency Models: Financial calculations
- File/Media Models: Asset handling
- Audit Models: Change tracking
â Best Practicesâ
- Always extend Entity: Model extends corresponding Entity
- Proper JSON handling: Handle null values gracefully
- Validation logic: Include business rule validation
- Immutable models: Use copyWith for updates
- Type safety: Proper type casting from JSON
- Error handling: Graceful error handling in factories
- Documentation: Clear method documentation
- Performance: Efficient serialization/deserialization
āĻāĻ examples āĻā§āϞ⧠follow āĻāϰā§: āĻāĻĒāύāĻŋ āϝā§āĻā§āύ⧠āϧāϰāύā§āϰ Model āϤā§āϰāĻŋ āĻāϰāϤ⧠āĻĒāĻžāϰāĻŦā§āύ! đī¸