mavix 0.1.3 copy "mavix: ^0.1.3" to clipboard
mavix: ^0.1.3 copied to clipboard

A lightweight Flutter state manager with separate streams for persistent state and one-time UI activities.

Mavix #

pub package style: flutter_lints

A lightweight Flutter state manager that separates persistent UI state from one-time UI activities.

State    → MavixBuilder / MavixSelector
Activity → MavixListener

Mavix flow

Why Mavix? #

Flutter screens usually contain two different kinds of information.

State describes what the UI currently displays:

  • loading
  • data
  • selected item
  • filters
  • form values

Activity describes what the UI should perform once:

  • navigation
  • snackbar
  • dialog
  • clipboard action
  • file download
  • focus request

Instead of storing temporary flags inside state:

update(nextState);
send(activity);
  • update(...) changes persistent state.
  • send(...) emits a one-time activity.
  • Activities are not stored or replayed to new listeners.

Installation #

dependencies:
  mavix: ^0.1.2

Then run:

flutter pub get

VS Code Extension #

The Mavix extension generates files, wraps widgets and provides Dart snippets.

It supports:

  • generating Mavix, State and Activity
  • MavixProvider
  • MavixBuilder
  • MavixSelector
  • MavixListener
  • repository providers
  • multi-providers and multi-listeners
  • context snippets

Right-click a folder and select:

Mavix → New Mavix

The extension generates:

mavix/
├── login_mavix.dart
├── login_state.dart
└── login_activity.dart

Open Mavix in the VS Code Marketplace

Quick Start #

State #

class LoginState {
  const LoginState({
    this.isLoading = false,
  });

  final bool isLoading;

  LoginState copyWith({
    bool? isLoading,
  }) {
    return LoginState(
      isLoading: isLoading ?? this.isLoading,
    );
  }
}

Activity #

sealed class LoginActivity {
  const LoginActivity();
}

final class LoginCompleted extends LoginActivity {
  const LoginCompleted();
}

final class LoginFailed extends LoginActivity {
  const LoginFailed(this.message);

  final String message;
}

Mavix #

import 'package:mavix/mavix.dart';

class LoginMavix extends Mavix<LoginState, LoginActivity> {
  LoginMavix(this._repository) : super(const LoginState());

  final AuthenticationRepository _repository;

  Future<void> login() async {
    if (state.isLoading) return;

    update(state.copyWith(isLoading: true));

    try {
      await _repository.login();

      update(state.copyWith(isLoading: false));
      send(const LoginCompleted());
    } catch (error, stackTrace) {
      reportError(error, stackTrace);

      update(state.copyWith(isLoading: false));
      send(LoginFailed(error.toString()));
    }
  }
}

Provide Mavix #

MavixProvider<LoginMavix>(
  create: (_) => LoginMavix(authenticationRepository),
  child: const LoginScreen(),
);

Instances created through create are closed automatically.

Access the instance without rebuilding:

context.mavix<LoginMavix>().login();

Build From State #

Use MavixBuilder when a widget depends on the complete state:

MavixBuilder<LoginMavix, LoginState>(
  builder: (context, state) {
    return LoginForm(
      isLoading: state.isLoading,
    );
  },
);

Use MavixSelector when a widget depends on one selected value:

MavixSelector<LoginMavix, LoginState, bool>(
  selector: (state) => state.isLoading,
  builder: (context, isLoading) {
    return FilledButton(
      onPressed: isLoading
          ? null
          : context.mavix<LoginMavix>().login,
      child: Text(
        isLoading ? 'Loading...' : 'Login',
      ),
    );
  },
);

React to Activities #

MavixListener<LoginMavix, LoginActivity>(
  listener: (context, activity) {
    switch (activity) {
      case LoginCompleted():
        Navigator.of(context).pushReplacementNamed('/home');

      case LoginFailed(:final message):
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(content: Text(message)),
        );
    }
  },
  child: const LoginView(),
);

MavixListener does not rebuild its child.

Main API #

Mavix<State, Activity>

MavixProvider<M>
MavixMultiProvider

MavixBuilder<M, State>
MavixSelector<M, State, Selected>

MavixListener<M, Activity>
MavixMultiListener

MavixRepositoryProvider<R>
MavixMultiRepositoryProvider

Context access:

context.mavix<M>()
context.mavixRepository<R>()

Core methods:

update(state)
send(activity)
reportError(error, stackTrace)
close()

State Equality #

Mavix does not publish a state when:

nextState == state

Use immutable state models and implement equality manually or with packages such as equatable or freezed.

Observer #

void main() {
  MavixConfig.observer = const MavixLogObserver();

  runApp(const App());
}

Example #

A complete example application is available in example/.

License #

Mavix is available under the MIT License.

1
likes
160
points
115
downloads
screenshot

Documentation

Documentation
API reference

Publisher

unverified uploader

Weekly Downloads

A lightweight Flutter state manager with separate streams for persistent state and one-time UI activities.

Repository (GitHub)
View/report issues

Topics

#flutter #state-management #state-manager #reactive #ui

License

MIT (license)

Dependencies

flutter

More

Packages that depend on mavix