๐ช CircusRing
CircusRing is a lightweight and flexible dependency injection container for Flutter, making object, lifecycle, and component relationship management intuitive.
โจ Features
- ๐งฉ Multiple Registration Methods:
- Singleton (immediate or lazy)
- Asynchronous singleton
- Factory mode (new instance each time)
- "
fenix
" mode to automatically recreate instances
- ๐ Dependency Management:
- Explicitly bind dependencies between components
- Prevent removal of components that are still depended upon
- Automatically clean up resources when components are removed
- ๐ Flexible Lookup:
- Lookup by type (with optional tag)
- Supports both synchronous and asynchronous lookup
- Lookup by
Tag
only is also supported
- โป๏ธ Resource Management:
- Automatically handle
Disposable
orChangeNotifier
- Supports asynchronous release via
AsyncDisposable
- Automatically handle
๐ How to Use
๐ Global Access
CircusRing is a global singleton. You can easily access it via Circus
or Ring
:
import 'package:joker_state/circus_ring.dart';
final instance = Circus.find<T>();
final instance = Ring.find<T>();
๐ฅ Register Dependencies
CircusRing
provides multiple registration methods. You can even specify an alias
, which is useful for architectural patterns.
// Simple singleton registration, returns the instance directly
// Yes, you can use it directly like this
// final repository = Circus.hire<UserRepository>();
Circus.hire(UserRepository());
// Register multiple instances of the same type with tags, suitable for multi-flavor development
Circus.hire<ApiClient>(ProductionApiClient(), tag: 'prod');
Circus.hire<ApiClient>(MockApiClient(), tag: 'test');
// Lazy singleton
Circus.hireLazily<Database>(() => Database.initialize());
// Asynchronous singleton
Circus.hireLazilyAsync<NetworkService>(() async => await NetworkService.initialize());
// Factory mode
Circus.contract<UserModel>(() => UserModel());
// "fenix" mode to automatically recreate instances
Circus.hireLazily<UserModel>(() => UserModel(), fenix: true);
// "alias" mode, pass the `Type` you want as an alias, CircusRing will handle everything for you
Circus.hire<UserRepository>(UserRepositoryImpl(), alias: UserRepository);
๐ Lookup Dependencies
CircusRing
makes it easy to find registered dependencies, all based on a Map
, so it's extremely fast!
// Get a singleton directly
final userRepo = Circus.find<UserRepository>();
// Get a singleton with a tag
final apiClient = Circus.find<ApiClient>('prod');
// Lazy singleton
final db = Circus.find<Database>();
// Asynchronous singleton
final networkService = await Circus.findAsync<NetworkService>();
// Safe lookup (returns null if not found)
final maybeRepo = Circus.tryFind<UserRepository>();
// Lookup by Tag
final client = Circus.findByTag('mockClient');
// Safe Tag lookup, returns null if not found
final maybeClient = Circus.tryFindByTag('mockClient');
๐ Bind Dependencies
CircusRing
provides the bindDependency
method to bind dependencies, ensuring that dependent objects are not accidentally removed.
// Make UserRepository depend on ApiClient
Circus.bindDependency<UserRepository, ApiClient>();
// As long as UserRepository exists, ApiClient will not be removed
๐งน Resource Cleanup
CircusRing
provides various cleanup methods, including both synchronous and asynchronous cleanup, if you want your dependencies can be automatically disposed when removed, please let your dependencies implement Disposable
or AsyncDisposable
.
// Remove standard Disposable (triggers dispose)
Circus.fire<UserRepository>();
// Asynchronous removal of AsyncDisposable (triggers async dispose)
await Circus.fireAsync<NetworkService>();
// Remove all dependencies, including asynchronous cleanup
await Circus.fireAll();
โ๏ธ Friendly Debug Features
By default, CircusRing
uses kDebugMode
to control debug message output, but you can also control it via enableLogs
.
Circus.enableLogs = true; // Enable debug messages
Circus.enableLogs = false; // Disable debug messages