holders 0.1.0
holders: ^0.1.0 copied to clipboard
Lightweight reactive value holders for Flutter with linked values, notifications, timing controls, and builder widgets.
holders #
Lightweight reactive values for Flutter. A holder owns or adapts a value, notifies callbacks when it changes, and can rebuild widgets without requiring a larger state-management framework.
Features #
- Store values with
ValueHolder. - Derive and update values through
LinkHolder. - Adapt
ValueNotifierinstances withNotifierHolder. - Adapt callback-based state with
GetterHolder. - Ignore equivalent values with a custom comparator.
- Debounce notifications with
stressand throttle them withcooldown. - Rebuild widgets with
HolderBuilderorHoldersBuilder.
Installation #
Add holders to your pubspec.yaml:
dependencies:
holders: ^0.1.0
Then import it:
import 'package:holders/holders.dart';
Usage #
Create a holder and subscribe to changes:
final counter = ValueHolder<int>(
0,
isEqual: nativeIsEqual,
);
void logValue(int value) => print('Counter: $value');
counter.hold(logValue);
counter.value = 1; // Prints: Counter: 1
counter.value = 1; // Ignored because the value is equal.
counter.unhold(logValue);
Create a two-way derived value with link:
final temperature = ValueHolder<double>(20);
final fahrenheit = temperature.link<double>(
read: (celsius) => celsius * 9 / 5 + 32,
write: (fahrenheit) => (fahrenheit - 32) * 5 / 9,
isEqual: nativeIsEqual,
);
fahrenheit.value = 86;
print(temperature.value); // 30.0
Rebuild a widget when a holder changes:
HolderBuilder(
holder: counter,
builder: (context) => Text('${counter.value}'),
)
Create a Flutter listenable when an API requires one, and dispose it when its owner is disposed:
final listenable = counter.buildListener();
// Use listenable with a ValueListenableBuilder.
listenable.dispose();
Holder values update immediately. stress waits until changes stop before
notifying callbacks, emitting only the latest value. cooldown emits the first
notification immediately and then, at most once per interval, emits the latest
value received during that interval. When both are set, stress runs first and
cooldown starts after the changes settle:
final query = ValueHolder<String>(
'',
isEqual: nativeIsEqual,
stress: const Duration(milliseconds: 300),
);
Notifications can be disabled until a particular value is observed or a condition is met. These methods also support nullable values:
final selection = ValueHolder<int?>(0);
selection.turnOffUntilValue(null);
selection.value = null; // Turns on and emits null.
selection.turnOffUntil(
(value) => value != null && value.isEven,
excludeMatchingCall: true,
);
selection.value = 2; // Turns on without emitting 2.
selection.dispose();
Lifecycle #
Call dispose() when a holder is no longer needed. This cancels its timers,
removes callbacks, and disconnects linked or notifier-backed holders from their
source. Disposal is permanent: attempts to access the value or reactivate the
holder throw StateError. Status and configuration reads, removing a callback
with unhold(), and calling dispose() again remain safe.
Linked holders and read-only listeners are disposed automatically with their
source. NotifierHolder does not own the supplied ValueNotifier; dispose that
notifier separately when appropriate.
Listenables and notifiers created with buildListener() or buildNotifier()
are owned by their caller and must be disposed. Disposing the holder disconnects
them but does not dispose them.