A highly powerful Signal specifically designed for manual, imperative asynchronous state management.
Unlike declarative reactive signals like futureSignal or
streamSignal (which automatically wrap and listen
to an existing Future or Stream), AsyncSignal
gives you full manual/imperative control over pushing
async states (AsyncState.loading,
AsyncState.data, and AsyncState.error) into the reactive graph.
This is the perfect state primitive for building custom repositories, handling manual user action triggers (e.g., submitting a registration form, calling an API on button click), or bridging low-level callback-based APIs into reactive states.
1. Imperative State Mutations#
You can update the state of the signal directly using specialized mutation helpers:
setLoading()puts the signal into a cleanAsyncLoadingstate.setValue(T data)pushes a newAsyncDatastate containing the data.-
setError(Object error, [StackTrace? stackTrace])transitions the signal to anAsyncErrorstate.
final authState = asyncSignal<User>(AsyncState.loading());
Future<void> login(String email, String password) async {
try {
authState.setLoading(); // Set UI to loading state
final user = await authApi.signIn(email, password);
authState.setValue(user); // Push success data
} catch (err, stack) {
authState.setError(err, stack); // Push error state
}
}
2. Awaiting Async Completion via .future#
An outstanding capability of AsyncSignal is its built-in .future getter. Any part of your code can await
this future. It returns a standard Future that resolves when the signal next receives a data value,
or throws if the signal next receives an error state.
final loginSignal = asyncSignal<User>(AsyncState.loading());
// Task A: Start background operation
Future.delayed(Duration(seconds: 2), () {
loginSignal.setValue(User(name: 'Charlie'));
});
// Task B: Wait for the signal to resolve!
final user = await loginSignal.future; // Suspends execution until Task A completes!
print(user.name); // 'Charlie'
3. Rendering in Flutter using Watch and AsyncState Pattern matching#
In your Flutter widgets, you can seamlessly watch the signal and use Dart's native pattern matching on AsyncState to render different widgets corresponding to the current asynchronous lifecycle:
Widget build(BuildContext context) {
final state = authState.watch(context);
return state.map(
data: (user) => HomeScreen(user: user),
error: (error, stackTrace) => ErrorWidget(error),
loading: () => const CircularProgressIndicator(),
);
}
4. Bridging callback/event-driven systems via EventSink#
AsyncSignal implements Dart's standard EventSink interface. This allows it to act directly as an event sink
for streams, websockets, or callback listeners:
final messageLog = asyncSignal<String>(AsyncState.loading());
final chatStream = webSocket.stream.map((event) => event.toString());
// Automatically push all incoming messages and errors from the stream into the signal:
chatStream.listen(
(msg) => messageLog.add(msg),
onError: (err) => messageLog.addError(err),
onDone: () => messageLog.close(),
);
AsyncSignal when you need manual, callback-driven, or button-press-triggered state mutations.
For auto-triggering, declarative, or read-only asynchronous data dependencies (like pulling data when an ID changes),
favor [futureSignal](/packages/signals/async/future) or [computedAsync](/packages/signals/async/computed) instead.
Constructors#
Properties#
Methods#
View Methods
Future<T> future
The future of the signal completer
bool isCompleted
Returns true if the signal is completed an error or data
void setError(Object error, [StackTrace? stackTrace])
Set the error with optional stackTrace to AsyncError
void setValue(T value)
Set the value to AsyncData
void setLoading([AsyncState<T>? state])
Set the loading state to AsyncLoading
void reset([AsyncState<T>? value])
Reset the signal to the initial value
void init()
Initialize the signal
Future<void> reload()
Reload the future
Future<void> refresh()
Refresh the future
AsyncState<T> value
T requireValue
Returns the value of the signal
asyncSignal#
Helper function to create an AsyncSignal initialized with an AsyncState.
Example#
// Create an AsyncSignal initialized to a loading state
final counter = asyncSignal<int>(AsyncState.loading());
// Create an AsyncSignal initialized with initial data
final status = asyncSignal<String>(AsyncState.data('Active'));
AsyncSignalOptions#
Configuration options for an AsyncSignal.
Constructors#
View Constructors
AsyncSignalOptions({this.initialValue, this.dependencies = const [], this.onDone, this.cancelOnError, this.lazy = true, super.name, super.autoDispose, super.watched, super.unwatched})
Creates a new AsyncSignalOptions instance.
Properties#
View Properties
T? initialValue
The initial value of the async signal.
List<ReadonlySignal<dynamic>> dependencies
The list of dependencies to watch/listen to.
void Function()? onDone
Optional function called when a stream completes.
bool? cancelOnError
Whether to cancel the stream subscription on error.
bool lazy
Whether the execution is lazy.
Methods#
View Methods
AsyncSignalOptions<T> copyWith({T? initialValue, List<ReadonlySignal<dynamic>>? dependencies, void Function()? onDone, bool? cancelOnError, bool? lazy, bool? autoDispose, String? name, void Function()? watched, void Function()? unwatched})
Creates a copy of this options with custom overrides.