Current Logo

Jaspr Current

A simple, lightweight state management library for Jaspr

Features

  • Typed reactive properties for primitives, nullable values, lists, and maps.
  • View-model-driven components with CurrentComponent and CurrentState.
  • Application-wide shared state with Current.
  • Built-in busy state, change notifications, and event listeners for async flows.

Note

This library is a port of the battle-tested Flutter Current state management library. The Jaspr implementation has not yet reached 1.0, but the core patterns and APIs are stable. The Jaspr specific components still need some time in the oven, and I certainly welcome contributions and feedback.

Getting Started

In your Jaspr project, add the dependency to your pubspec.yaml.

dependencies:
  jaspr_current: ^0.1.1

Quick Start

A small counter is still the fastest way to see the core pattern: keep state in a CurrentViewModel, list reactive properties in currentProps, and render that view model through a CurrentComponent.

counter_view_model.dart

import 'package:jaspr_current/jaspr_current.dart';

class CounterViewModel extends CurrentViewModel {
  final count = CurrentProperty.integer();

  void incrementCounter() {
    count.value += 1;
  }

  void decrementCounter() {
    count.value -= 1;
  }

  @override
  Iterable<CurrentProperty> get currentProps => [count];
}

counter_page.dart

import 'package:jaspr_current/jaspr_current.dart';
import 'package:jaspr/jaspr.dart';

class CounterPage extends CurrentComponent<CounterViewModel> {
  const CounterPage({super.key, required super.viewModel});

  @override
  CurrentState<CounterPage, CounterViewModel> createCurrent() {
    return _CounterPageState(viewModel);
  }
}

class _CounterPageState extends CurrentState<CounterPage, CounterViewModel> {
  _CounterPageState(super.viewModel);

  @override
  Component build(BuildContext context) {
    return div([
      div(classes: 'counter', [
        button(
          onClick: viewModel.decrementCounter,
          [.text('-')],
        ),
        span([.text('${viewModel.count}')]),
        button(
          onClick: viewModel.incrementCounter,
          [.text('+')],
        ),
      ]),
    ]);
  }
}

main.dart

import 'package:jaspr/jaspr.dart';

@client
class App extends StatelessComponent {
  App({super.key});

  @override
  Component build(BuildContext context) {
    return div(classes: 'main', [
        const Header(),
        Router(
          routes: [
            Route(
              path: '/',
              title: 'Counter',
              builder: (context, state) => const CounterPage(viewModel: CounterViewModel()),
            ),
          ],
        ),
      ]);
  }
}

This keeps business logic out of the component tree without introducing a second state object. When any property in currentProps changes, the matching CurrentState rebuilds automatically.

Contributing

This is an open source project, and contributions are welcome. Please feel free to create a new issue if you encounter any problems, or submit a pull request. For community contribution guidelines, please review the Code of Conduct.

If submitting a pull request, please ensure the following standards are met:

  1. Code files must be well formatted with dart format ..
  2. Tests must pass with dart test. New test cases to validate your changes are highly recommended.
  3. Implementations must not add unnecessary project dependencies.
  4. Project must contain zero warnings. Running dart analyze must return zero issues.
  5. Keep docstrings and README guidance up to date when public APIs change.

Additional information

This package has ZERO third-party package dependencies. The current_core package dependency is the dart-only (No Jaspr) core of the library also maintained by me. This allows me to iterate quickly on the core reactive features for both the Flutter and Jaspr implementations of Current.

You can find the full API documentation here.

Libraries

jaspr_current
Support for doing something awesome.