crystalline 0.7.0 copy "crystalline: ^0.7.0" to clipboard
crystalline: ^0.7.0 copied to clipboard

Crystal clear state management

Crystalline #

CI Build Checks Pub Version Pub Popularity Pub Points Pub Likes GitHub Repo stars GitHub contributors Pub Publisher GitHub

Note: This library is currently in early development. Documentation is not yet ready, and the API is subject to changes as I continue to refine the core concepts.

The Core Idea #

Crystalline is a state management solution built on a simple premise: Everything that exists is data, and everything that is done is a manipulation of that data.

While many state management libraries focus on complex flows or boilerplate-heavy patterns, Crystalline treats state as a living piece of data that inherently reflects its own lifecycle. Whether data is being read, updated, created, or deleted, those transitions shouldn't just be "side effects"—they should be first-class properties of the state itself.

In Crystalline, state is not just a value; it's a Data object that knows its current operation, its failures, and its history. This makes the relationship between data manipulation and the UI observer transparent and effortless.

Quick Example #

Here is how you can manage an asynchronous operation like fetching a user profile.

1. Define and Manipulate Data #

Instead of manually managing loading booleans and error strings, the Data object tracks the state of the operation for you.

// Define a piece of state for a UserProfile
final userProfile = Data<UserProfile>();

// Perform an async operation
Future<void> fetchUserProfile() async {
  // Set operation to 'read' to indicate loading
  userProfile.operation = Operation.read;

  try {
    final profile = await api.getUserProfile();
    // Setting the value automatically updates observers
    userProfile.value = profile;
  } catch (e) {
    userProfile.failure = Failure(e.toString());
  } finally {
    // Reset operation to 'none' when finished
    userProfile.operation = Operation.none;
  }
}

2. Observe with DataBuilder #

DataBuilder gives you full control over how to render the state based on its current properties.

DataBuilder(
  data: userProfile,
  builder: (context, data) {
    if (data.isReading) return CircularProgressIndicator();
    if (data.hasFailure) return Text('Error: ${data.failure}');
    if (data.hasValue) return Text('Welcome, ${data.value.name}');

    return Text('No profile loaded');
  },
)

3. Simplify with WhenDataBuilder #

For a more declarative approach, you can use WhenDataBuilder to handle different states of your data explicitly.

WhenDataBuilder(
  data: userProfile,
  onRead: (context, data) => CircularProgressIndicator(),
  onFailure: (context, data) => Text('Error: ${data.failure}'),
  onValue: (context, data) => Text('Welcome, ${data.value.name}'),
  onNoValue: (context, data) => Text('No profile loaded'),
)

Features & Roadmap #

  • Everything is Data: Crystalline provides specialized data classes like Data, ListData, CollectionData, and OperationData to handle different state shapes.
  • Built-in Builders: Reactive widgets like DataBuilder, StoreBuilder, and WhenDataBuilder make it easy to consume state changes without manual listeners.
  • Store System: A structured Store class to organize multiple states, with upcoming support for Code Generation to automatically produce custom data classes and builders.
  • Semantic Operations: State naturally tracks whether it is isReading, isUpdating, or has a failure, allowing you to build robust UIs that respond to every stage of a data's lifecycle.

Crystalline exists to make state management feel like what it actually is: simple data manipulation.

4
likes
140
points
105
downloads

Publisher

verified publishereasazade.com

Weekly Downloads

Crystal clear state management

Repository (GitHub)
View/report issues

Documentation

API reference

License

MIT (license)

Dependencies

collection, meta

More

Packages that depend on crystalline