auto_dispose_guard 1.0.0 copy "auto_dispose_guard: ^1.0.0" to clipboard
auto_dispose_guard: ^1.0.0 copied to clipboard

High-performance Flutter package that automatically manages disposal of all disposable resources — zero boilerplate, zero memory leaks. Supports TextEditingController, AnimationController, StreamContr [...]

auto_dispose_guard #

Zero-boilerplate, production-grade resource lifecycle management for Flutter.

Stop writing dispose() overrides. Stop tracking every controller manually.
Let the guard handle it — automatically, safely, with zero overhead in release builds.


The Problem #

Every Flutter developer has written this:

class _MyScreenState extends State<MyScreen> {
  late final _name      = TextEditingController();
  late final _email     = TextEditingController();
  late final _animation = AnimationController(vsync: this, ...);
  late final _focus     = FocusNode();
  late final _stream    = StreamController<int>();
  late final _sub       = _stream.stream.listen(...);

  @override
  void dispose() {
    _name.dispose();       // forget one → memory leak
    _email.dispose();
    _animation.dispose();
    _focus.dispose();
    _stream.close();       // close, not dispose!
    _sub.cancel();         // cancel, not dispose!
    super.dispose();
  }
}

Forgetting a single call causes memory leaks, frame drops, and "used after dispose" crashes that are notoriously hard to track down.


The Solution #

class _MyScreenState extends State<MyScreen>
    with AutoDisposeMixin, SingleTickerProviderStateMixin {

  late final _name      = register(TextEditingController());
  late final _email     = register(TextEditingController());
  late final _animation = register(AnimationController(vsync: this, ...));
  late final _focus     = register(FocusNode());
  late final _stream    = register(StreamController<int>());
  // ignore: unused_field
  late final _sub       = register(_stream.stream.listen(...));

  // ✅ No @override dispose() needed. Ever.
}

Architecture #

┌──────────────────────────────────────────────────────────────────┐
│                       auto_dispose_guard                         │
├──────────────────────────────────────────────────────────────────┤
│                                                                  │
│   ┌────────────────────┐     ┌────────────────────────────────┐  │
│   │  AutoDisposeMixin  │     │       AutoDisposeScope         │  │
│   │  (State mixin)     │     │  (InheritedWidget scope)       │  │
│   └─────────┬──────────┘     └────────────────┬───────────────┘  │
│             │                                 │                  │
│             └─────────────────┬───────────────┘                  │
│                               ▼                                  │
│                  ┌────────────────────────┐                      │
│                  │    LifecycleManager     │                      │
│                  └────────────┬───────────┘                      │
│                               │                                  │
│                               ▼                                  │
│                  ┌────────────────────────┐                      │
│                  │     DisposeRegistry     │  O(1) identity map   │
│                  │    (LinkedHashMap)      │  LIFO disposal       │
│                  └────────────┬───────────┘                      │
│                               │                                  │
│                               ▼                                  │
│                  ┌────────────────────────┐                      │
│                  │      DisposeEngine      │                      │
│                  │  Auto-detects method:  │                      │
│                  │  dispose / close /     │                      │
│                  │  cancel  (no mirrors)  │                      │
│                  └────────────────────────┘                      │
└──────────────────────────────────────────────────────────────────┘

Installation #

dependencies:
  auto_dispose_guard: ^1.0.0
import 'package:auto_dispose_guard/auto_dispose_guard.dart';

Usage #

Add the mixin to any State class and call register() on every resource. No dispose() override needed.

class _ProfileScreenState extends State<ProfileScreen>
    with AutoDisposeMixin, SingleTickerProviderStateMixin {

  late final _name   = register(TextEditingController());
  late final _email  = register(TextEditingController());
  late final _focus  = register(FocusNode());
  late final _anim   = register(
    AnimationController(vsync: this, duration: kThemeAnimationDuration),
  );
  late final _stream = register(StreamController<String>.broadcast());

  @override
  Widget build(BuildContext context) => ...;
}

Mixin order with TickerProviders:
Place AutoDisposeMixin after ticker mixins so Flutter disposes the ticker before the AnimationController:

with SingleTickerProviderStateMixin, AutoDisposeMixin  // ✅

Method 2 — AutoDisposeScope #

Wrap a route or subtree in AutoDisposeScope. Resources registered from any descendant are disposed when the scope is removed from the tree.

// In your route builder:
MaterialPageRoute<void>(
  builder: (_) => const AutoDisposeScope(
    debugLabel: 'ProfileScreen',
    child: ProfileScreen(),
  ),
)
// From any descendant — safe to call from initState:
AutoDispose.of(context).register(myController);

Method 3 — .autoDispose() extension #

Fluent inline registration inside initState or didChangeDependencies when a scope is present in the tree:

@override
void initState() {
  super.initState();
  _name   = TextEditingController().autoDispose(context);
  _stream = StreamController<int>().autoDispose(context);
  _focus  = FocusNode().autoDispose(context);
}

With a custom teardown:

_myService = MyService().autoDispose(
  context,
  onDispose: () => _myService.shutdown(),
);

Auto-Detection Table #

No reflection. Detection is pure is type checking — zero runtime cost.

Type Detected method Common examples
Disposable (your interface) dispose() Custom BLoC, services
ChangeNotifier dispose() TextEditingController, AnimationController, ScrollController, FocusNode, PageController, TabController
StreamController close() StreamController<T>
StreamSubscription cancel() .stream.listen(...)
Timer cancel() Timer.periodic(...)
Closeable (your interface) close() Custom streams
Cancellable (your interface) cancel() Custom tasks
Explicit disposeCallback custom Anything else

Custom Types #

Option A — implement a marker interface (zero-config) #

class AnalyticsService implements Disposable {
  @override
  void dispose() { /* flush & teardown */ }
}

// Then register normally:
late final _analytics = register(AnalyticsService());

Option B — provide an explicit callback #

late final _db = register(
  Database.open(path),
  onDispose: () => _db.close(),
);

Debug Intelligence #

All output is in debug mode only (kDebugMode). Zero bytes of log code execute in release builds.

Normal disposal:

✅ AutoDisposeGuard: Auto-disposed [TextEditingController]
✅ AutoDisposeGuard: Auto-disposed [AnimationController]
✅ AutoDisposeGuard: Auto-disposed [_BroadcastStreamController<String>]
[AutoDisposeGuard] Scope "ProfileScreen" released 3 resource(s)

Unknown type warning:

⚠️  AutoDisposeGuard Warning:
   Controller : SomeRandomClass
   Issue      : No disposal method detected (dispose / close / cancel)
   Action     : Object skipped. Implement Disposable, Closeable, Cancellable,
                or pass a disposeCallback.

Disposal error (fail-safe):

❌ AutoDisposeGuard Error:
   Resource : AnimationController
   Error    : Null check operator used on a null value
   Trace    : ...

Disposal continues for all remaining resources — one failure never blocks the rest.


Performance #

Metric Value
Registration O(1) — identity hash map (LinkedHashMap)
Lookup O(1)
Disposal O(n) — unavoidable minimum
Widget rebuilds triggered ZeroupdateShouldNotify always returns false
Listener overhead Zero — no streams, no ChangeNotifier internally
Release-build log overhead Zero — all guarded by kDebugMode
Memory per tracked resource ~56 bytes (TrackedResource object)

API Reference #

AutoDisposeMixin #

mixin AutoDisposeMixin<T extends StatefulWidget> on State<T>
Member Description
R register<R>(R resource, {void Function()? onDispose}) Registers resource; returns it for inline late final assignment
DisposeRegistry disposeRegistry Direct registry access for advanced use

AutoDisposeScope #

class AutoDisposeScope extends StatefulWidget
Member Description
AutoDisposeScope.of(context) Returns nearest DisposeRegistry; throws if none
AutoDisposeScope.maybeOf(context) Returns nearest DisposeRegistry or null
debugLabel Optional label for debug output

AutoDispose #

Convenience façade — identical behaviour to AutoDisposeScope:

AutoDispose.of(context).register(controller);
AutoDispose.of(context).register(obj, disposeCallback: obj.teardown);

DisposeRegistry #

Member Description
register(object, {disposeCallback}) Register a resource
unregister(object) Remove without disposing
disposeResource(object) Dispose and remove immediately
disposeAll() Dispose all (LIFO); called automatically
isRegistered(object) Query registration state
resourceCount Number of tracked resources
isDisposed Whether disposeAll() has been called

Marker interfaces #

abstract interface class Disposable { void dispose(); }
abstract interface class Closeable  { void close();   }
abstract interface class Cancellable { void cancel(); }

Before vs After #

Before (6 resources, 6 manual calls, 1 typo away from a leak):

@override
void dispose() {
  _nameController.dispose();
  _emailController.dispose();
  _animation.dispose();
  _focusNode.dispose();
  _counterStream.close();  // different method name!
  _subscription.cancel();  // yet another!
  super.dispose();
}

After (6 resources, 0 manual calls):

// Nothing. The mixin handles everything.

License #

MIT

1
likes
0
points
222
downloads

Publisher

unverified uploader

Weekly Downloads

High-performance Flutter package that automatically manages disposal of all disposable resources — zero boilerplate, zero memory leaks. Supports TextEditingController, AnimationController, StreamController, FocusNode, custom types, and anything with dispose/close/cancel.

Repository (GitHub)
View/report issues

Topics

#dispose #memory-management #lifecycle #performance #controllers

License

unknown (license)

Dependencies

flutter

More

Packages that depend on auto_dispose_guard