
The AutoSignal system is a powerful Dart based tool for automatic component connection through signal-slot architecture. It allows objects to communicate with each other without direct references, making code more modular and maintainable.
contactAdded, contactsLoaded, databaseError_onContactAdded, _onContactsLoaded, _onDatabaseErrorclass MyClass extends AutoSignalObject {
MyClass() : super() {
// Register in namespace (optional)
AutoDiscoveryRegistry().registerToNamespace(this, 'myNamespace');
// Register signals and slots
_setupSignalsAndSlots();
}
}
void _setupSignalsAndSlots() {
// Register signals with the data types they will emit
registerSignal<Contact>('contactAdded', Contact);
registerSignal<List<Contact>>('contactsLoaded', List<Contact>);
registerSignal<String>('errorOccurred', String);
registerSignal<bool>('operationCompleted', bool);
}
void _setupSignalsAndSlots() {
// ... signals ...
// Register slots with handler functions
registerSlot<Contact>('contactAdded', Contact, _onContactAdded);
registerSlot<List<Contact>>('contactsLoaded', List<Contact>, _onContactsLoaded);
registerSlot<String>('errorOccurred', String, _onErrorOccurred, priority: 1);
}
// Handler functions
void _onContactAdded(Contact contact) {
print('New contact added: ${contact.name}');
// Processing logic...
}
void _onContactsLoaded(List<Contact> contacts) {
print('Loaded ${contacts.length} contacts');
// Update UI or state...
}
void _onErrorOccurred(String error) {
print('Error: $error');
// Show error to user...
}
void someOperation() {
try {
// Perform operation...
Contact newContact = createContact();
// Emit signal with data
emitSignal<Contact>('contactAdded', newContact);
} catch (e) {
// Emit error signal
emitSignal<String>('errorOccurred', e.toString());
}
}
// In DatabaseManager
Future<Contact> createContact(Contact contact) async {
// ... database operation ...
final newContact = contact.copyWith(id: id);
// Automatically notify all listeners
emitSignal<Contact>('contactAdded', newContact);
return newContact;
}
// In ContactManager
void _onContactAdded(Contact contact) {
_contacts.add(contact);
_contacts.sort((a, b) => a.name.compareTo(b.name));
// Forward signal up to UI
emitSignal<List<Contact>>('contactsChanged', _contacts);
}
Signal Name = Slot Name
// Signal: 'contactsLoaded'
// Slot: 'contactsLoaded' ✅
Convention with _on prefix
// Signal: 'contactsLoaded'
// Slot: '_onContactsLoaded' ✅
Data Type Matching
// Signal: registerSignal<Contact>('contactAdded', Contact)
// Slot: registerSlot<Contact>('contactAdded', Contact, handler) ✅
// In main function or during initialization
void initializeApp() {
// Create objects
final databaseManager = DatabaseManager();
final contactManager = ContactManager();
final uiController = UIController();
// Connect all objects in namespace
AutoDiscoveryRegistry().connectNamespace('contacts');
}
class DatabaseManager extends AutoSignalObject {
DatabaseManager() : super() {
// Register in namespace
AutoDiscoveryRegistry().registerToNamespace(this, 'contacts');
}
}
class ContactManager extends AutoSignalObject {
ContactManager() : super() {
// Same namespace
AutoDiscoveryRegistry().registerToNamespace(this, 'contacts');
}
}
// Connect all objects in namespace
AutoDiscoveryRegistry().connectNamespace('contacts');
Slots with higher priority execute first.
// Critical operations - high priority
registerSlot<String>('errorOccurred', String, _handleCriticalError, priority: 10);
// Regular operations - medium priority
registerSlot<Contact>('contactAdded', Contact, _updateUI, priority: 5);
// Logging - low priority
registerSlot<Contact>('contactAdded', Contact, _logOperation, priority: 1);
class MyClass extends AutoSignalObject {
@override
void dispose() {
// AutoSignalObject automatically:
// - Clears all signal handlers
// - Unregisters object from registry
// - Breaks all connections
super.dispose(); // IMPORTANT: Always call super.dispose()
}
}
class _MyWidgetState extends State<MyWidget> {
late MyClass _myObject;
@override
void initState() {
super.initState();
_myObject = MyClass();
}
@override
void dispose() {
_myObject.dispose(); // Clean up AutoSignal object
super.dispose();
}
}
class StatusManager extends AutoSignalObject {
StatusManager() : super() {
registerSignal<String>('statusChanged', String);
}
void updateStatus(String newStatus) {
emitSignal<String>('statusChanged', newStatus);
}
}
class UIController extends AutoSignalObject {
UIController() : super() {
registerSlot<String>('statusChanged', String, _onStatusChanged);
}
void _onStatusChanged(String status) {
// Update UI
statusBar.text = status;
}
}
class DataLoader extends AutoSignalObject {
DataLoader() : super() {
registerSignal<bool>('loadingStarted', bool);
registerSignal<List<Data>>('dataLoaded', List<Data>);
registerSignal<String>('loadingError', String);
}
Future<void> loadData() async {
emitSignal<bool>('loadingStarted', true);
try {
final data = await fetchFromAPI();
emitSignal<List<Data>>('dataLoaded', data);
} catch (e) {
emitSignal<String>('loadingError', e.toString());
}
}
}
class ProgressIndicator extends AutoSignalObject {
ProgressIndicator() : super() {
registerSlot<bool>('loadingStarted', bool, _onLoadingStarted);
registerSlot<List<Data>>('dataLoaded', List<Data>, _onDataLoaded);
}
void _onLoadingStarted(bool isLoading) {
showSpinner(isLoading);
}
void _onDataLoaded(List<Data> data) {
hideSpinner();
}
}
class ErrorManager extends AutoSignalObject {
ErrorManager() : super() {
registerSlot<String>('databaseError', String, _onDatabaseError, priority: 10);
registerSlot<String>('networkError', String, _onNetworkError, priority: 9);
registerSlot<String>('generalError', String, _onGeneralError, priority: 1);
registerSignal<ErrorInfo>('errorProcessed', ErrorInfo);
}
void _onDatabaseError(String error) {
final errorInfo = ErrorInfo(
type: ErrorType.database,
message: error,
severity: ErrorSeverity.critical
);
logError(errorInfo);
emitSignal<ErrorInfo>('errorProcessed', errorInfo);
}
}
void printDebugInfo() {
// Registry information
AutoDiscoveryRegistry().printDebugInfo();
// Object information
print('Signals: ${getSignals().keys}');
print('Slots: ${getSlots().keys}');
}
void emitSignal<T>(String name, T value) {
print('Signal emitted: $name = $value');
super.emitSignal<T>(name, value);
}
contactAdded, dataLoaded)_on prefix (_onContactAdded, _onDataLoaded)dynamic except in special casesdispose()isDisposed before operationsThe AutoSignal system provides a powerful and flexible way to create loosely-coupled architectures. It is especially useful for:
Following this guide, you will be able to create robust and maintainable applications with clean architecture and automatic dependency management.
For updated sources, contact me by e-mail.
fulltext