LogoSignals.dart
Copy Markdown
rodydavis/signals.dart 999999

Model

Creates a new model constructor with an instanced factory.

createModel#

Creates a new model constructor with an instanced factory.

A SignalModel is a highly powerful architectural primitive designed to manage cohesive packages of related state, business logic, actions, and side effects.

Under the hood, SignalModel automatically tracks, scopes, and manages the lifecycle of any Effects instantiated during its factory execution. When the model is disposed (by calling model.dispose()), all nested/captured effects are clean up automatically, ensuring complete prevention of memory leaks.

Furthermore, if the factory returns a standard Dart Map, and wrapInAction is enabled (default), all nested function properties are automatically wrapped in batched action transactions to optimize updates.

The simplest and most built-in way to define a compile-safe model is to return a Dart record from your factory. Records provide immediate type safety, autocomplete, and compile-time verification without any wrapper boilerplates.

import 'package:signals/signals.dart';

// Define the reactive model constructor returning a Record
final counterModel = createModel(() {
  final count = signal(0);

  // Captured nested side-effect (e.g. logging or syncing to local storage)
  effect(() {
    print('Nested logger: count is ${count.value}');
  });

  return (
    count: count,
    increment: () => count.value++,
  );
});

void main() {
  // Instantiate the model
  final model = counterModel();

  // Access properties type-safely via .value
  print(model.value.count.value); // Prints: 0 (and registers effect print: Nested logger: count is 0)
  model.value.increment();        // Prints: Nested logger: count is 1

  // Dispose when done to clean up all captured nested effects
  model.dispose();
}

2. Object-Oriented Style: Type-Safe Wrappers using Dart 3+ Extension Types#

While records are great for lightweight structures, you can wrap the returned Map-based SignalModel in a standard Dart 3 extension type when you prefer a class-like API (e.g. implementing getters/setters or hiding subscript lookups).

import 'package:signals/signals.dart';

// 1. Define the reactive model constructor returning a Map
final counterModel = createModel(() {
  final count = signal(0);

  // Captured nested side-effect (e.g. logging or syncing to local storage)
  effect(() {
    print('Nested logger: count is ${count.value}');
  });

  return <String, dynamic>{
    'count': count,
    'increment': () => count.value++,
  };
});

// 2. Create a premium, compile-safe extension type wrapper
extension type TypeSafeCounter(SignalModel<Map<String, dynamic>> _model) {
  int get count => (_model['count'] as Signal<int>).value;
  set count(int val) => (_model['count'] as Signal<int>).value = val;

  void increment() => (_model['increment'] as Function)();
  void dispose() => _model.dispose();
}

void main() {
  // 3. Instantiate and wrap the model
  final counter = TypeSafeCounter(counterModel());

  // Now you have a beautifully autocomplete-friendly, compile-safe API!
  print(counter.count); // Prints: 0 (and registers effect print: Nested logger: count is 0)
  counter.increment();  // Prints: Nested logger: count is 1

  // Dispose when done to clean up all captured nested effects
  counter.dispose();
}
Favor using Dart 3 records or extension types when defining models. They cost zero runtime overhead while granting complete compile-safe parameters and autocomplete functionality.

SignalModel#

A premium wrapper for cohesive state packages constructed with createModel.

It holds the instanced model value and all the Effects that were captured during its construction. Disposing the SignalModel automatically disposes of all nested/captured effects, completely avoiding memory leaks.

Premium Pattern: Dart 3+ Extension Type Wrappers#

To avoid unchecked subscript access like model['count'].value, wrap your model in an extension type:

extension type TypeSafeCounter(SignalModel<Map<String, dynamic>> _model) {
  int get count => (_model['count'] as Signal<int>).value;
  set count(int val) => (_model['count'] as Signal<int>).value = val;
  void increment() => (_model['increment'] as Function)();
  void dispose() => _model.dispose();
}

Constructors#

View Constructors
SignalModel(this.value, this._effects, {this.options = const SignalModelOptions()})

Creates a new model instance.

Properties#

View Properties
T value

The instanced model value.

SignalModelOptions options

Options used to configure this model.

Methods#

View Methods
dynamic [](Object? key)

Access properties dynamically if the underlying value is a Map.

void []=(dynamic key, dynamic val)

Set properties dynamically if the underlying value is a Map.

T call()

Returns the value of this model. Alias for [.value]

void dispose()

Disposes of all captured effects.


SignalModelOptions#

Options for configuring a SignalModel.

Provides configuration for debug labeling (name) and whether to automatically wrap Map functions in transaction-safe, batched actions (wrapInAction).

Example Usage#

import 'package:preact_signals/preact_signals.dart';

final options = const SignalModelOptions(
  name: 'user-profile-model',
  wrapInAction: true,
);

Constructors#

View Constructors
SignalModelOptions({this.name, this.wrapInAction = true})

Creates a new instance of SignalModelOptions.

Properties#

View Properties
String? name

The name or debug label of the model.

bool wrapInAction

Whether to automatically wrap returned Map functions in actions. Defaults to true.

Methods#

View Methods
SignalModelOptions copyWith({String? name, bool? wrapInAction})

Copy options with new values.

bool ==(Object other)
int hashCode

SignalModelConstructor#

A constructor for models that manages nested effects.

The model constructor starts capturing effects when called, storing them inside the returned SignalModel.

Example Usage#

import 'package:preact_signals/preact_signals.dart';

final myModel = SignalModelConstructor(() => 'data');
final model = myModel();
print(model.value); // Prints: data

Constructors#

View Constructors
SignalModelConstructor(this._factory, {this.options = const SignalModelOptions()})

Creates a new instance of SignalModelConstructor.

Properties#

View Properties
SignalModelOptions options

Options used to configure this constructor.

Methods#

View Methods
SignalModel call()

Instantiates a new SignalModel instance.