Runtime JSON Model Generator for Flutter — Automatically adapt to evolving backend JSON structures without manual model updates.
Backend APIs evolve constantly—new fields get added, old ones removed, types change. Traditional model classes break easily, requiring constant maintenance. Dynamic Backend Schema Mapper solves this by:
- ✅ Zero Model Classes — No manual model updates needed
- ✅ Type-Safe Access —
getString(),getInt(),getBool()with defaults - ✅ Auto-Adaptation — Handles backend changes automatically
- ✅ Schema Detection — Get notified when backend structure changes
- ✅ Deep Nesting — Fully supports nested objects and lists
- ✅ Crash Prevention — Default values prevent null reference errors
Demonstrating real-time JSON parsing with type-safe getters and default values
Automatic detection and notification of backend schema changes
Add to your pubspec.yaml:
dependencies:
dynamic_schema_mapper: ^0.1.0Then run:
flutter pub getimport 'package:dynamic_schema_mapper/dynamic_schema_mapper.dart';
// Parse any JSON response
final jsonResponse = {
'id': 123,
'name': 'John Doe',
'age': 30,
'premium': true,
'balance': 1250.50,
};
final schema = DynamicSchema.parse(jsonResponse);
// Type-safe access with automatic defaults
final name = schema.getString('name'); // "John Doe"
final age = schema.getInt('age'); // 30
final premium = schema.getBool('premium'); // true
final balance = schema.getDouble('balance'); // 1250.50
// Non-existent keys return safe defaults
final phone = schema.getString('phone', defaultValue: 'N/A'); // "N/A"final response = {
'user': {
'name': 'Alice',
'address': {
'city': 'Springfield',
'zipcode': 12345,
}
}
};
final schema = DynamicSchema.parse(response);
// Navigate nested structures
final user = schema.getNested('user');
final address = user?.getNested('address');
print(address?.getString('city')); // "Springfield"
print(address?.getInt('zipcode')); // 12345
// Or use dot notation
print(schema.getValueAtPath('user.address.city')); // "Springfield"final response = {
'products': [
{'id': 1, 'name': 'Laptop', 'price': 999.99},
{'id': 2, 'name': 'Mouse', 'price': 29.99},
]
};
final schema = DynamicSchema.parse(response);
final products = schema.getList('products');
for (final product in products) {
print('${product.getString('name')}: \$${product.getDouble('price')}');
}
// Output:
// Laptop: $999.99
// Mouse: $29.99Get notified automatically when your backend structure changes:
// Enable change detection
DynamicSchema.enableSchemaDetection((changes) {
print('⚠️ Backend schema changed!');
for (final change in changes) {
print('• $change');
}
});
// First API call
DynamicSchema.parse({'id': 1, 'name': 'Product A', 'price': 99.99});
// Backend evolves - new fields added
DynamicSchema.parse({
'id': 2,
'name': 'Product B',
'price': 149.99,
'category': 'Electronics', // NEW!
'inStock': true, // NEW!
});
// Console output:
// ⚠️ Backend schema changed!
// • Fields Added: category
// • Fields Added: inStockAll getters include default values to prevent crashes:
schema.getString('key', defaultValue: 'default');
schema.getInt('key', defaultValue: 0);
schema.getDouble('key', defaultValue: 0.0);
schema.getBool('key', defaultValue: false);// Get all keys
final keys = schema.keys; // ['id', 'name', 'age']
// Check if key exists
if (schema.hasKey('email')) {
print(schema.getString('email'));
}
// Get all paths (including nested)
final paths = schema.getAllPaths();
// ['user', 'user.name', 'user.address', 'user.address.city']
// Get schema structure (types only)
final structure = schema.getSchemaStructure();// Pretty print
print(schema.toJsonString(pretty: true));
// Compact
final json = schema.toJsonString();Cache schemas locally for offline use or comparison:
import 'package:dynamic_schema_mapper/cache_manager.dart';
final cache = SchemaCacheManager(
namespace: 'my_app',
cacheDuration: Duration(hours: 24),
);
// Save schema
await cache.saveSchema('users', schema.getSchemaStructure());
// Load cached schema
final cachedSchema = await cache.loadSchema('users');
// Check if valid cache exists
if (await cache.hasValidCache('users')) {
print('Using cached schema');
}| Home Screen | Basic JSON Parsing | Nested Objects | Lists And Arrays | Dashboard |
dynamic_schema_mapper/
├─ lib/
│ ├─ dynamic_schema_mapper.dart # Main API
│ └─ src/
│ ├─ schema_node.dart # Core data structure
│ ├─ schema_parser.dart # JSON → SchemaNode
│ ├─ schema_diff.dart # Change detection
│ └─ cache_manager.dart # Optional caching
- SchemaNode: Represents JSON values (primitives, objects, lists)
- SchemaParser: Converts JSON to SchemaNode tree
- SchemaDiff: Detects changes between schemas
- CacheManager: Optional local schema storage
// Complex e-commerce order
final order = DynamicSchema.parse(orderApiResponse);
// Customer info
final customer = order.getNested('customer');
print('Customer: ${customer?.getString('name')}');
print('Loyalty: ${customer?.getString('loyaltyTier')}');
// Order items
final items = order.getList('items');
for (final item in items) {
final name = item.getString('name');
final qty = item.getInt('quantity');
final price = item.getDouble('unitPrice');
print('$name: $qty × \$$price');
}
// Shipping address
final address = order.getNested('shipping')?.getNested('address');
print('Ship to: ${address?.getString('city')}, ${address?.getString('state')}');
// Payment
final payment = order.getNested('payment');
final paid = payment?.getBool('paid') ?? false;
print('Payment status: ${paid ? 'PAID' : 'PENDING'}');final oldSchema = DynamicSchema.parse(oldApiResponse);
final newSchema = DynamicSchema.parse(newApiResponse);
final changes = DynamicSchema.compareSchemas(oldSchema, newSchema);
for (final change in changes) {
print(change);
}// Print entire schema structure
schema.printTree();
// Output:
// object {
// name:
// string: John Doe
// age:
// integer: 30
// address:
// object {
// city:
// string: Springfield
// }
// }- Fast Parsing: Minimal overhead compared to manual models
- Lazy Evaluation: Only processes accessed fields
- Memory Efficient: Shared references, no duplication
Performance varies based on JSON size and structure. The package is optimized for real-world use cases where flexibility is more important than raw speed.
The package is optimized for real-world scenarios where API flexibility and zero maintenance are priorities. Performance characteristics scale well with JSON size, with lazy evaluation ensuring only accessed fields are processed.
- No Static Types: Fields are accessed dynamically at runtime
- No Code Generation: Everything happens at runtime
Contributions are welcome! If you find a bug or have a feature request, please open an issue on GitHub.
MIT License - see LICENSE file.
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Email: gargesamarth@gmail.com
If this package helps your project, give it a ⭐ on GitHub!
Made with ❤️ for the Flutter community

