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 an exhaustive list of Flutter and Dart controllers, natively saving you from writing disposal boilerplate.

ChangeNotifier / ValueNotifier Family

(Most UI Controllers)

  • TextEditingController
  • ScrollController, PageController, TabController
  • AnimationController
  • FocusNode, FocusScopeNode
  • SearchController
  • Any custom class extending ChangeNotifier or ValueNotifier

Streams & Sinks

(Async Data)

  • StreamSubscription β†’ .cancel()
  • StreamController, BroadcastStreamController β†’ .close()
  • IOSink, EventSink, WebSocket β†’ .close()

UI & Flutter Core

  • OverlayEntry β†’ Natively calls .remove() if mounted, then .dispose()
  • Timer / RestartableTimer β†’ .cancel()
  • Ticker β†’ .stop(), .dispose()

Custom Disposable Interface

abstract class Disposable {
  bool get isDisposed;
  void dispose();
  void markDisposed();
}

Advanced Duck Typing (The "Senior Dev" Checklist)

If your object isn't in the standard Flutter list above, AutoDisposeMixin will dynamically detect and safely call its cleanup method. This automatically handles thousands of 3rd party plugins (like VideoPlayerController, GetX Worker, etc).

The mixin implicitly supports anything that has:

  • .dispose() (e.g. TapGestureRecognizer, Plugin controllers)
  • .cancel() (e.g. Worker, EventChannel subscriptions)
  • .close() (e.g. ReceivePort, PersistentBottomSheetController)
  • .kill() (e.g. Isolate)
class FakeSocketClient {
  void close() {
    print('Socket closed');
  }
}

late final socket =
    registerForDispose(FakeSocketClient());

Manual Cleanup

registerDisposeCallback(() {
  // custom cleanup
});

🎞 AnimationController & Ticker Support

βœ… Correct Mixin Order (IMPORTANT)

When using tickers:

class _MyWidgetState extends State<MyWidget>
    with SingleTickerProviderStateMixin, AutoDisposeMixin {

Rule: AutoDisposeMixin must 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 Stopwatch internally
  • Disabled in release mode by default

⚠️ Best Practices

βœ… DO

  • Use late final with registerForDispose
  • 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.


Libraries

auto_dispose_mixin