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

  • TextEditingController
  • ScrollController
  • PageController
  • TabController
  • AnimationController
  • ValueNotifier
  • FocusNode
  • 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: 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