Service Container
Introduction
The Magic service container is a powerful tool for managing class dependencies and performing dependency injection. Like Laravel's container, it allows you to register bindings and resolve them throughout your application.
// Register a service
Magic.bind('mailer', () => MailService());
// Resolve it anywhere
final mailer = Magic.make('mailer');
Binding Services
Simple Bindings
Use Magic.bind() to register a service. Each resolve creates a new instance:
Magic.bind('api', () => ApiClient());
// Each call creates a new instance
final api1 = Magic.make('api');
final api2 = Magic.make('api');
// api1 != api2
Singletons
Use Magic.singleton() to register a service that's instantiated only once:
Magic.singleton('cache', () => CacheManager());
// Always returns the same instance
final cache1 = Magic.make('cache');
final cache2 = Magic.make('cache');
// cache1 == cache2
Binding in Service Providers
The recommended way to register services is in a service provider:
class AppServiceProvider extends ServiceProvider {
@override
void register() {
// Register services
app.bind('api', () => ApiClient(Config.get('api.url')));
app.singleton('cache', () => CacheManager());
}
@override
Future boot() async {
// Perform initialization after all services are registered
final cache = app.make('cache');
await cache.initialize();
}
}
Resolving Services
Using Magic.make()
// With type inference
final service = Magic.make('myservice');
// Without type (returns dynamic)
final service = Magic.make('myservice');
Checking Existence
if (Magic.bound('api')) {
final api = Magic.make('api');
}
Automatic Resolution
Magic facades automatically resolve their underlying services. You don't need to manually resolve common services:
// These facades auto-resolve from the container:
Cache.get('key'); // Resolves CacheManager
Auth.check(); // Resolves AuthManager
Lang.setLocale(locale); // Resolves LocalizationService
Using Controllers
Magic provides a convenient way to manage controller instances:
findOrPut Pattern
The recommended pattern for controllers is using Magic.findOrPut():
class UserController extends MagicController with MagicStateMixin> {
// Singleton accessor
static UserController get instance => Magic.findOrPut(UserController.new);
// Controller logic...
}
// Usage anywhere in your app
final controller = UserController.instance;
How It Works
- First call: Creates a new controller instance and stores it
- Subsequent calls: Returns the existing instance
- Automatic cleanup: Controller's
onClose()is called when disposed
Manual Controller Management
// Register a controller
Magic.put(UserController());
// Find an existing controller
final controller = Magic.find();
// Delete a controller
Magic.delete();
Controller Lifecycle
class UserController extends MagicController {
@override
void onInit() {
// Called when controller is first created
loadUsers();
}
@override
void onClose() {
// Called when controller is disposed
// Clean up subscriptions, streams, etc.
}
}
[!TIP] Use the
findOrPutpattern for controllers to ensure a single instance is used throughout your application while maintaining proper lifecycle management.