Auto Dispose Mixin
Zero-Boilerplate Lifecycle Management for Flutter Widgets
auto_dispose_mixin automatically disposes controllers, subscriptions, notifiers, and custom resources when a StatefulWidget is removed from the widget tree β without overriding dispose().
Designed for performance, safety, and developer ergonomics.
π Why This Package Exists
Memory leaks are one of the most common performance issues in Flutter apps.
Typical problems:
- Forgetting to dispose
TextEditingController - Leaking
StreamSubscription - Missing
AnimationController.dispose() - Bloated
dispose()methods - Inconsistent cleanup across teams
β Traditional Approach
@override
void dispose() {
controller.dispose();
scrollController.dispose();
animationController.dispose();
subscription.cancel();
super.dispose();
}
β With AutoDisposeMixin
class _MyWidgetState extends State<MyWidget>
with AutoDisposeMixin {
late final controller =
registerForDispose(TextEditingController());
// No dispose() override needed π
}
β¨ Features
- β Automatic disposal of common Flutter resources
- β Works with AnimationController (ticker-safe)
- β Stream subscription cleanup
- β Custom disposable objects
- β
Duck typing (
dispose()detection) - β Manual cleanup callbacks
- β DevTools logging
- β Performance timing
- β Zero runtime overhead in release mode
- β No code generation
- β No reflection
- β No magic
π¦ Installation
Add to pubspec.yaml:
dependencies:
auto_dispose_mixin: ^1.*.*
Then run:
flutter pub get
π§© Basic Usage
Step 1: Add the mixin
class _MyPageState extends State<MyPage>
with AutoDisposeMixin {
Step 2: Register disposables
late final TextEditingController controller =
registerForDispose(TextEditingController());
late final ScrollController scrollController =
registerForDispose(ScrollController());
Thatβs it.
No dispose() override required.
π― Supported Disposable Types
AutoDisposeMixin automatically handles:
ChangeNotifier Family
TextEditingControllerScrollControllerPageControllerTabControllerAnimationControllerValueNotifierFocusNode- Any
ChangeNotifier
Streams
StreamSubscriptionβ.cancel()
Custom Disposable Interface
abstract class Disposable {
bool get isDisposed;
void dispose();
void markDisposed();
}
Duck Typing
Any object with a dispose() method:
class MyService {
void dispose() {}
}
Manual Cleanup
registerDisposeCallback(() {
// custom cleanup
});
π AnimationController & Ticker Support
β Correct Mixin Order (IMPORTANT)
When using tickers:
class _MyWidgetState extends State<MyWidget>
with SingleTickerProviderStateMixin, AutoDisposeMixin {
Rule:
AutoDisposeMixinmust be the last mixin.
Why?
- Flutter requires ticker providers to be initialized first
- AutoDisposeMixin depends on fully constructed controllers
π§ Example: AnimationController
late final AnimationController animationController =
registerForDispose(
AnimationController(
vsync: this,
duration: const Duration(seconds: 1),
),
);
The package will:
- Stop animation if running
- Dispose safely
- Track performance (optional)
π StreamSubscription Example
late final StreamSubscription<int> subscription =
registerForDispose(
Stream.periodic(const Duration(seconds: 1))
.listen(print),
);
Automatically calls .cancel() on dispose.
π§ͺ Custom Disposable Example
class FakeSocketClient {
void dispose() {
print('Socket closed');
}
}
late final socket =
registerForDispose(FakeSocketClient());
Duck typing detects .dispose() automatically.
π§Ή Manual Dispose Callback
For edge cases:
registerDisposeCallback(() {
debugPrint('Manual cleanup');
});
Executed after all registered disposables.
π Debug & DevTools Integration
Enable Debug Reporting
void main() {
AutoDisposeDebug.debugReportEnabled = true;
AutoDisposeDebug.trackPerformance = true;
runApp(MyApp());
}
What You Get
- β Per-object dispose logs
- β Non-disposable warnings
- β Total dispose time
- β DevTools timeline visibility
Example output:
Dispose Summary for _MyPageState
---------------------------------
Disposed: 6
Not Disposable: 1
Total Time: 312Β΅s
π Performance Tracking
When enabled:
- Each disposable tracks execution time
- Total dispose duration is logged
- Uses
Stopwatchinternally - Disabled in release mode by default
β οΈ Best Practices
β DO
- Use
late finalwithregisterForDispose - Keep AutoDisposeMixin last
- Enable debug mode during development
β DONβT
- Manually call
.dispose()on registered objects - Register objects after
dispose()is called - Use with non-State classes
π§ͺ Example App
To generate an example project:
flutter create example
Then import your package:
import 'package:auto_dispose_mixin/auto_dispose_mixin.dart';
A full example is included in /example.
π§© Architecture Philosophy
- No code generation
- No build_runner
- No reflection
- No runtime cost
- Flutter-native lifecycle
- Predictable behavior
This package augments Flutter β it does not fight it.
π£ Roadmap
Planned features:
- Leak detection warnings
- DevTools UI extension
- Dispose order visualization
- Zone-based lifecycle scopes
β€οΈ Contributing
Contributions welcome.
- Add new disposable resolvers
- Improve DevTools logging
- Write tests
- Improve documentation
π License
MIT License
https://github.com/ASHISH1317/auto_dispose_mixin/blob/main/LICENSE
β Final Note
If youβve ever forgotten to dispose something in Flutter this package is for you.
Simple. Safe. Fast.