import 'dart:async'; typedef _InitCallback = void Function(); /// A [StreamController] boilerplate that prevents memory leaks. abstract class SafeCtrl { factory SafeCtrl() => _SingleSafeCtrl(); factory SafeCtrl.broadcast() => _BroadcastSafeCtrl(); Stream get stream; void add(T event); void addError(error, [StackTrace? stackTrace]); Future close(); void whenInitialized(void Function() callback); } class _SingleSafeCtrl implements SafeCtrl { late StreamController _stream; bool _hasListener = false, _initialized = false; _InitCallback? _initializer; _SingleSafeCtrl() { _stream = StreamController(onListen: () { _hasListener = true; if (!_initialized && _initializer != null) { _initializer!(); _initialized = true; } }, onPause: () { _hasListener = false; }, onResume: () { _hasListener = true; }, onCancel: () { _hasListener = false; }); } @override Stream get stream => _stream.stream; @override void add(T event) { if (_hasListener) _stream.add(event); } @override void addError(error, [StackTrace? stackTrace]) { if (_hasListener) _stream.addError(error as Object, stackTrace); } @override Future close() { return _stream.close(); } @override void whenInitialized(void Function() callback) { if (!_initialized) { if (!_hasListener) { _initializer = callback; } else { _initializer!(); _initialized = true; } } } } class _BroadcastSafeCtrl implements SafeCtrl { late StreamController _stream; int _listeners = 0; bool _initialized = false; _InitCallback? _initializer; _BroadcastSafeCtrl() { _stream = StreamController.broadcast(onListen: () { _listeners++; if (!_initialized && _initializer != null) { _initializer!(); _initialized = true; } }, onCancel: () { _listeners--; }); } @override Stream get stream => _stream.stream; @override void add(T event) { if (_listeners > 0) _stream.add(event); } @override void addError(error, [StackTrace? stackTrace]) { if (_listeners > 0) _stream.addError(error as Object, stackTrace); } @override Future close() { return _stream.close(); } @override void whenInitialized(void Function() callback) { if (!_initialized) { if (_listeners <= 0) { _initializer = callback; } else { _initializer!(); _initialized = true; } } } }