mavix 0.1.2
mavix: ^0.1.2 copied to clipboard
A lightweight Flutter state manager with separate streams for persistent state and one-time UI activities.
Mavix #
A lightweight Flutter state manager that separates persistent UI state from one-time UI activities.
State → MavixBuilder / MavixSelector
Activity → MavixListener

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,StateandActivity MavixProviderMavixBuilderMavixSelectorMavixListener- 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.
