⚡ Vexo — The Ultimate Flutter State Management
Zero dependencies. Simple as GetX. Powerful as Riverpod + BLoC combined.
Table of Contents
- Why Vexo?
- The Problem With Every Other Package
- Deep Comparison: Vexo vs GetX vs Provider vs Riverpod vs BLoC
- Feature Matrix
- Side-by-Side Code Comparison
- Core Concepts
- Full API Reference
- Architecture Guide
- Migration Cheat-Sheet
- Installation
Why Vexo?
Flutter's state management ecosystem is fragmented. Every popular package solves some problems brilliantly while creating new ones. Developers are forced to choose between simplicity and power — until now.
Vexo was built by studying every weakness of every major package and eliminating them all, while keeping only the best ideas from each. The result is a single, pure-Dart package that requires zero external dependencies and gives you everything.
// This is all you need to know to start:
import 'package:vexo/vexo.dart';
class CounterController extends VexoController {
final count = VexoState(0); // reactive atom
late final doubled = count.select((v) => v * 2); // derived state — auto updates
void increment() => count.increment(); // dead simple
}
// Register once
Vexo.put(() => CounterController(), permanent: true);
// Use anywhere — no context, no ref, no boilerplate
VexoConsumer<CounterController>(
statesOf: (c) => [c.count],
builder: (ctx, c) => Text('${c.count.value}'),
)
The Problem With Every Other Package
GetX — Too Magical, Too Opinionated
GetX is beloved for its simplicity, but it comes at a steep cost:
- Global state pollution —
.obsvariables are scattered everywhere with no clear ownership - Hidden magic —
Get.find()can silently fail at runtime, no compile-time safety - Monolith dependency — GetX bundles routing, HTTP, storage, i18n — you get it all whether you want it or not
- Testing nightmare — tightly coupled to
Get, mocking is painful - No computed/derived state — you manage derived values manually
- No async state type-safety — loading/error/data states are manual booleans
- No event-driven architecture — no BLoC-style events, just direct method calls
- Lifecycle surprises —
onInit/onClosetiming issues plague production apps
Provider — Too Verbose, Too Primitive
Provider was Flutter's "official" recommendation, but developers quickly outgrow it:
- Widget tree pollution — everything must live in the widget tree via
ChangeNotifierProvider - Massive boilerplate —
MultiProviderwith 10+ providers is unreadable - No built-in reactivity — you must manually call
notifyListeners()everywhere - No derived state — computing values from multiple providers is painful
- No async support —
FutureProvider/StreamProviderare clunky and limited - Context dependency — you cannot access state outside the widget tree without workarounds
- No lifecycle hooks — controllers have no
onInit/onClose - Memory leaks — forgetting
dispose()is easy and completely silent - Not a real DI system — no dependency injection, just widget-scoped values
Riverpod — Too Complex, Too Much Ceremony
Riverpod is architecturally sound but has a brutal learning curve:
- Code generation required for real-world use (
@riverpod,build_runner) — adds build time, complexity, and tooling fragility refis everywhere — you must passrefthrough your entire call chain- Steep learning curve —
Provider,StateProvider,FutureProvider,StreamProvider,NotifierProvider,AsyncNotifierProvider... which one do you use? - Verbose providers — even a simple counter needs many lines of boilerplate
- No event-driven pattern — BLoC-style events are not supported
- No built-in forms — form validation is completely out of scope
- No worker helpers —
ever,debounce,intervalmust be implemented manually - No global middleware — no equivalent of BLoC's
BlocObserver - ProviderScope required — tight coupling to the widget tree root
flutter_bloc — Too Verbose, Too Much Boilerplate
BLoC is powerful but exhausting:
- Extreme boilerplate — every feature needs: Event class, State class, Bloc/Cubit class, BlocProvider, BlocBuilder, BlocListener — minimum 5 files for one screen
- Over-engineering — simple counters become 100-line codebases
- No built-in reactive atoms — every piece of state must be a full Bloc
- No derived state — computing values from multiple blocs is manual
- No async state typing — loading/error states must be manually added to every State class
- No form management — completely out of scope
- No worker helpers —
ever,debounce, etc. must be built from scratch - Steep learning curve —
EventTransformer,StreamTransformer, concurrency handling - Verbose widgets —
BlocConsumerwith bothlistenWhenandbuildWhenbecomes deeply nested
Deep Comparison: Vexo vs GetX vs Provider vs Riverpod vs BLoC
GetX vs Vexo
| Criterion | GetX | Vexo |
|---|---|---|
| Simplicity | Simple | Equally simple |
| Reactive state | .obs |
VexoState |
| Derived / computed state | Manual | VexoComputed / .select() |
| Type-safe DI | Runtime errors possible | Compile-time safe |
| Lifecycle hooks | onInit, onClose |
onInit, onReady, onClose |
| Worker helpers | ever, once, debounce |
All of those + throttle, interval |
| BLoC / event-driven | None | VexoBloc / VexoCubitBloc |
| Async state | Manual booleans | Typed VexoAsyncState<T> |
| Reactive forms | None | VexoForm + VexoField |
| Global middleware | None | VexoMiddleware |
| External dependencies | Huge monolith | Zero |
| Testability | Get-coupled | Pure Dart, easy to test |
| Scoped state | Limited | Full scoped DI |
| Selector rebuild optimization | None | VexoSelector |
Verdict: Vexo has every feature GetX has, plus BLoC events, typed async, computed state, forms, and middleware — with zero external dependencies and better type safety. GetX is a monolith; Vexo is surgical.
Provider vs Vexo
| Criterion | Provider | Vexo |
|---|---|---|
| Setup boilerplate | Heavy (MultiProvider, ChangeNotifier) | One line: Vexo.put(...) |
| Reactivity | Manual notifyListeners() |
Automatic |
| Derived state | Not supported | VexoComputed |
| Async state | Clunky FutureProvider | VexoAsyncState<T> |
| Access outside widget tree | Requires context | Vexo.find<T>() anywhere |
| Lifecycle management | None | onInit, onReady, onClose |
| Auto-dispose | Manual | Automatic on VexoScope close |
| BLoC-style events | None | VexoBloc |
| Worker helpers | None | VexoWorker |
| Forms | None | VexoForm |
| Global middleware | None | VexoMiddleware |
| Scoped controllers | Via widget tree only | Widget tree + global registry |
| External dependencies | Zero | Zero |
Verdict: Vexo is strictly a superset of Provider. Everything Provider does, Vexo does better with automatic reactivity, no manual notifyListeners(), and a complete feature set Provider never had.
Riverpod vs Vexo
| Criterion | Riverpod | Vexo |
|---|---|---|
| Learning curve | Steep (8+ provider types) | Gentle (one VexoState concept) |
| Code generation | Required for real apps | Never needed |
| Boilerplate | High | Minimal |
ref passing |
Must thread through call chain | Vexo.find<T>() anywhere |
| Derived state | Excellent | Equally excellent |
| Async state | AsyncValue |
VexoAsyncState (simpler API) |
| Type safety | Excellent | Excellent |
| BLoC / event-driven | None | VexoBloc |
| Worker helpers | Manual | VexoWorker |
| Reactive forms | None | VexoForm |
| Global middleware | None | VexoMiddleware |
| Scoped state | Excellent | Excellent |
ProviderScope required |
Yes, must wrap app root | No widget tree requirement |
| External dependencies | Yes (riverpod package) | Zero |
| Hot-reload friendly | Yes | Yes |
Verdict: Vexo matches Riverpod's power with a fraction of the ceremony. No code generation, no ref threading, no ProviderScope, no 8 different provider types to memorize — and Vexo adds BLoC events, forms, and workers that Riverpod simply does not have.
flutter_bloc vs Vexo
| Criterion | flutter_bloc | Vexo |
|---|---|---|
| Event-driven pattern | Excellent | VexoBloc / VexoCubitBloc |
| Typed state transitions | Excellent | VexoCubitBloc<E, S> |
| Boilerplate | Extreme | Minimal |
| Simple reactive state | Must write full Bloc | VexoState(0) |
| Derived state | None | VexoComputed |
| Async state | Manual state classes | VexoAsyncState<T> |
| Global middleware | BlocObserver |
VexoMiddleware |
| Worker helpers | None | VexoWorker |
| Reactive forms | None | VexoForm |
| Lifecycle hooks | None | onInit, onReady, onClose |
| DI / controller registry | None (use get_it separately) | Built-in |
| Files per feature | 3–5 files minimum | 1 file |
| Testing | Good (bloc_test package) | Pure Dart, no extra package needed |
| External dependencies | Yes | Zero |
Verdict: Vexo gives you everything flutter_bloc gives you — typed events, typed states, state transitions, global observer — but eliminates the extreme boilerplate. A BLoC feature that took 5 files now takes 1. Vexo also adds reactive state atoms, DI, async state, forms, and workers that BLoC never had.
Feature Matrix
| Feature | GetX | Provider | Riverpod | BLoC | Vexo |
|---|---|---|---|---|---|
| Reactive state atoms | ✅ | ❌ | ✅ | ❌ | ✅ |
| Derived / computed state | ❌ | ❌ | ✅ | ❌ | ✅ |
| Selector (granular rebuild) | ❌ | ❌ | ✅ | ✅ | ✅ |
| BLoC event-driven pattern | ❌ | ❌ | ❌ | ✅ | ✅ |
| Typed async state | ❌ | ❌ | ✅ | ⚠️ | ✅ |
| Built-in DI / registry | ✅ | ❌ | ✅ | ❌ | ✅ |
| Scoped controllers | ⚠️ | ✅ | ✅ | ⚠️ | ✅ |
| Lifecycle hooks | ✅ | ❌ | ❌ | ❌ | ✅ |
| Worker helpers (ever/debounce) | ✅ | ❌ | ❌ | ❌ | ✅ |
| Reactive forms | ❌ | ❌ | ❌ | ❌ | ✅ |
| Global middleware / observer | ❌ | ❌ | ❌ | ✅ | ✅ |
| Multi-state listener widget | ❌ | ❌ | ❌ | ❌ | ✅ |
| Access state without context | ✅ | ❌ | ❌ | ❌ | ✅ |
| Auto-dispose on scope exit | ✅ | ✅ | ✅ | ✅ | ✅ |
| Code generation required | ❌ | ❌ | ⚠️ | ❌ | ❌ |
| Zero external dependencies | ✅ | ✅ | ❌ | ❌ | ✅ |
| Debounce / throttle built-in | ✅ | ❌ | ❌ | ❌ | ✅ |
| Type-safe at compile time | ⚠️ | ⚠️ | ✅ | ✅ | ✅ |
| State interceptors | ❌ | ❌ | ❌ | ❌ | ✅ |
| Stream access on state | ✅ | ❌ | ✅ | ✅ | ✅ |
| Named scopes | ❌ | ❌ | ❌ | ❌ | ✅ |
| Total score | 9/21 | 4/21 | 12/21 | 8/21 | 21/21 |
Side-by-Side Code Comparison
Counter App
GetX
// controller
class CounterController extends GetxController {
var count = 0.obs;
void increment() => count++;
}
Get.put(CounterController());
// widget
Obx(() => Text('${Get.find<CounterController>().count}'))
Provider
// model
class Counter extends ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() { _count++; notifyListeners(); }
}
// main
ChangeNotifierProvider(create: (_) => Counter(), child: MyApp())
// widget
Consumer<Counter>(builder: (_, c, __) => Text('${c.count}'))
Riverpod
// provider (requires @riverpod annotation + build_runner)
@riverpod
class Counter extends _$Counter {
@override int build() => 0;
void increment() => state++;
}
// widget
Consumer(builder: (ctx, ref, _) {
final count = ref.watch(counterProvider);
return Text('$count');
})
flutter_bloc — minimum 3 files
// events.dart
abstract class CounterEvent {}
class IncrementEvent extends CounterEvent {}
// states.dart
class CounterState { final int count; const CounterState(this.count); }
// bloc.dart
class CounterBloc extends Bloc<CounterEvent, CounterState> {
CounterBloc() : super(CounterState(0)) {
on<IncrementEvent>((event, emit) => emit(CounterState(state.count + 1)));
}
}
// main
BlocProvider(create: (_) => CounterBloc(), child: MyApp())
// widget
BlocBuilder<CounterBloc, CounterState>(
builder: (ctx, state) => Text('${state.count}'),
)
Vexo — cleanest of all
// controller.dart
class CounterController extends VexoController {
final count = VexoState(0);
void increment() => count.increment();
}
// main.dart
Vexo.put(() => CounterController(), permanent: true);
// widget — done
VexoConsumer<CounterController>(
statesOf: (c) => [c.count],
builder: (ctx, c) => Text('${c.count.value}'),
)
Async Data Loading
GetX
class PostsController extends GetxController {
var posts = <Post>[].obs;
var isLoading = false.obs;
var error = ''.obs;
@override
void onInit() { fetchPosts(); super.onInit(); }
void fetchPosts() async {
isLoading.value = true;
error.value = '';
try {
posts.value = await api.getPosts();
} catch (e) {
error.value = e.toString();
} finally {
isLoading.value = false;
}
}
}
// Three separate Obx() widgets required for loading/error/data
Riverpod
@riverpod
Future<List<Post>> posts(PostsRef ref) => api.getPosts();
// widget
ref.watch(postsProvider).when(
loading: () => Spinner(),
error: (e, s) => Text('$e'),
data: (posts) => PostList(posts),
)
flutter_bloc — ~80 lines across 3 files
// Requires: PostEvent, PostState (Loading/Loaded/Error classes),
// PostBloc, BlocProvider, BlocBuilder with 3 state checks
// Minimum 80 lines across 3 separate files
Vexo
class PostsController extends VexoController {
final posts = VexoAsyncState<List<Post>>();
@override
void onInit() { posts.execute(api.getPosts); super.onInit(); }
}
// widget — one widget, three states handled
VexoAsyncBuilder<List<Post>>(
state: ctrl.posts,
loading: () => Spinner(),
error: (e, _) => Text('$e'),
data: (posts) => PostList(posts),
)
Event-Driven Authentication
flutter_bloc
// auth_event.dart, auth_state.dart, auth_bloc.dart — 3 files
// BlocProvider + BlocBuilder + BlocListener in widget tree
// ~120 lines total
Vexo — same power, 1 file
abstract class AuthEvent extends VexoEvent {}
class Login extends AuthEvent { final String id; Login(this.id); }
class Logout extends AuthEvent {}
abstract class AuthState {}
class LoggedIn extends AuthState { final String user; LoggedIn(this.user); }
class LoggedOut extends AuthState {}
class AuthBloc extends VexoCubitBloc<AuthEvent, AuthState> {
AuthBloc() : super(LoggedOut());
@override
void onEvent(AuthEvent event) async {
switch (event) {
case Login(:var id): emit(LoggedIn(id));
case Logout(): emit(LoggedOut());
}
}
}
// Usage
bloc.add(Login('user123'));
// Widget — one builder, full type-safety
VexoBuilder(
states: [bloc.stateAtom],
builder: (ctx) => switch (bloc.state) {
LoggedIn(:var user) => HomeScreen(user),
LoggedOut() => LoginScreen(),
_ => SplashScreen(),
},
)
Reactive Forms
- GetX — No built-in form system
- Provider — No built-in form system
- Riverpod — No built-in form system
- flutter_bloc — No built-in form system
- Vexo — Full reactive form system included
class LoginForm extends VexoForm {
final email = VexoField<String>(
initial: '',
validators: [
VexoValidators.required('Email is required'),
VexoValidators.email(),
],
);
final password = VexoField<String>(
initial: '',
validators: [VexoValidators.minLength(6)],
);
@override
List<VexoField> get fields => [email, password];
}
// Widget
VexoFieldBuilder<String>(
field: form.email,
builder: (_, field) => TextField(
onChanged: (v) => field.value = v,
decoration: InputDecoration(errorText: field.error),
),
)
// Submit with auto loading/error state
await form.submit(() => authService.login(form.email.value, form.password.value));
Global State Observer / Middleware
- GetX — Not supported
- Provider — Not supported
- Riverpod — Not supported
- flutter_bloc —
BlocObserver(covers only Blocs, not general state) - Vexo — Covers ALL controllers and ALL state
class AppMiddleware extends VexoMiddleware {
@override
void onCreate(VexoController ctrl) =>
analytics.log('created', {'type': '${ctrl.runtimeType}'});
@override
void onChange(VexoController ctrl, VexoChange change) =>
logger.d('${change.stateName}: ${change.previous} → ${change.next}');
@override
void onError(VexoController ctrl, Object error, StackTrace stack) =>
crashlytics.recordError(error, stack);
@override
void onClose(VexoController ctrl) =>
analytics.log('closed', {'type': '${ctrl.runtimeType}'});
}
// Register once at app start
VexoObserver.middleware = AppMiddleware();
Core Concepts
VexoState<T> — Reactive Atom
final count = VexoState(0);
final name = VexoState('Alice');
// Read / write
print(count.value);
count.value = 5;
count.emit(10);
count.update((v) => v * 2);
count.setSilent(99); // silent — no notification
// Stream
count.stream.listen(print);
// Integer helpers
count.increment();
count.increment(5);
count.decrement();
count.reset();
// Bool helpers
final isDark = VexoState(false);
isDark.toggle();
isDark.setTrue();
isDark.setFalse();
// List helpers
final items = VexoState<List<String>>([]);
items.add('hello');
items.remove('hello');
items.removeAt(0);
items.clear();
// Map helpers
final map = VexoState<Map<String, int>>({});
map.put('a', 1);
map.remove('a');
// Intercept changes
count.intercept((prev, next) => print('$prev → $next'));
// Derive
final doubled = count.select((v) => v * 2);
VexoComputed<T> — Derived State
final a = VexoState(10);
final b = VexoState(5);
final sum = VexoComputed(() => a.value + b.value, [a, b]);
// Chaining selectors
final name = VexoState('hello world');
final upper = name.select((v) => v.toUpperCase());
final words = upper.select((v) => v.split(' '));
VexoController — Business Logic
class UserController extends VexoController {
final user = VexoState<User?>(null);
final isLoading = VexoState(false);
final error = VexoState<String?>(null);
late final displayName = user.select((u) => u?.name ?? 'Guest');
@override
void onInit() {
ever(user, (u) => print('Changed: $u'));
once(user, (u) => print('First: $u'));
when(isLoading, (v) => !v, (_) => print('Done'));
debounce(user, (_) => cacheUser(), duration: Duration(seconds: 2));
super.onInit();
}
@override
void onReady() => fetchUser();
@override
void onClose() => print('Cleaning up');
Future<void> fetchUser() => run(
() => api.getUser(),
loading: isLoading,
errorState: error,
onSuccess: (u) => user.emit(u),
);
}
VexoAsyncState<T> — Async Data
final posts = VexoAsyncState<List<Post>>();
await posts.execute(() => api.getPosts());
// Pattern match
posts.value.when(
loading: () => 'loading',
data: (d) => 'got ${d.length} posts',
error: (e, _) => 'error: $e',
);
// Widget
VexoAsyncBuilder<List<Post>>(
state: posts,
loading: () => CircularProgressIndicator(),
error: (e, _) => Text('$e'),
data: (list) => PostList(list),
)
VexoBloc / VexoCubitBloc — Event-Driven
abstract class CounterEvent extends VexoEvent {}
class Add extends CounterEvent { final int n; Add(this.n); }
class Reset extends CounterEvent {}
class CounterBloc extends VexoCubitBloc<CounterEvent, int> {
CounterBloc() : super(0);
@override
void onEvent(CounterEvent event) {
switch (event) {
case Add(:var n): emit(state + n);
case Reset(): emit(0);
}
}
}
bloc.add(Add(5));
bloc.add(Reset());
Full API Reference
Vexo Facade
Vexo.put<T>(() => T(), lazy: true, permanent: false, scope: null)
Vexo.putInstance<T>(instance, permanent: false)
Vexo.find<T>(scope: null)
Vexo.findOrNull<T>(scope: null)
Vexo.findOrCreate<T>(() => T())
Vexo.isRegistered<T>()
Vexo.delete<T>()
Vexo.resetAll()
Vexo.state<T>(initial) // creates VexoState<T>
Vexo.computed<T>(fn, deps) // creates VexoComputed<T>
Reactive Widgets
VexoBuilder(states: [...], builder: (ctx) => Widget)
VexoConsumer<T>(statesOf: (ctrl) => [...], builder: (ctx, ctrl) => Widget)
VexoListener<T>(state: s, onChanged: (ctx, v) => ..., child: Widget)
VexoSelector<C, R>(state: (ctrl) => s, select: (v) => r, builder: (ctx, r) => Widget)
VexoMulti(states: [...], builder: (ctx) => Widget)
VexoAsyncBuilder<T>(state: s, loading: ..., error: ..., data: ...)
VexoFieldBuilder<T>(field: f, builder: (ctx, f) => Widget)
VexoListenerBuilder<T>(state: s, onChanged: fn, builder: (ctx, v) => Widget)
Scoping
VexoScope<T>(create: () => T(), child: Widget, permanent: false)
VexoMultiScope(scopes: [VexoScopeConfig(() => T())], child: Widget)
Workers
VexoWorker.ever<T>(state, callback)
VexoWorker.once<T>(state, callback)
VexoWorker.when<T>(state, condition, callback)
VexoWorker.debounce<T>(state, callback, duration: Duration(milliseconds: 300))
VexoWorker.throttle<T>(state, callback, interval: Duration(milliseconds: 500))
VexoWorker.interval(duration, callback)
// All return VexoDisposable — call .dispose() to cancel
Built-in Validators
VexoValidators.required([message])
VexoValidators.email([message])
VexoValidators.minLength(n, [message])
VexoValidators.maxLength(n, [message])
VexoValidators.pattern(regex, [message])
VexoValidators.custom<T>((v) => bool, [message])
Architecture Guide
lib/
├── main.dart ← Vexo.put registrations + VexoObserver
├── middleware/
│ └── app_middleware.dart ← VexoMiddleware subclass
├── controllers/
│ ├── auth_controller.dart ← VexoController
│ ├── theme_controller.dart ← VexoController
│ └── cart_controller.dart ← VexoController
├── blocs/
│ └── auth_bloc.dart ← VexoCubitBloc
├── forms/
│ └── checkout_form.dart ← VexoForm
└── pages/
├── home_page.dart ← VexoConsumer / VexoBuilder
└── auth_page.dart ← VexoBuilder(bloc.stateAtom)
Global singletons (auth, theme, cart):
Vexo.put(() => AuthController(), permanent: true);
Route-scoped controllers (per-screen logic):
VexoScope(create: () => CheckoutController(), child: CheckoutPage())
Named scopes (multi-tenant / multi-instance):
Vexo.put(() => ChatController(), scope: 'room_$roomId');
final ctrl = Vexo.find<ChatController>(scope: 'room_$roomId');
Migration Cheat-Sheet
From GetX
| GetX | Vexo |
|---|---|
Get.put(Ctrl()) |
Vexo.put(() => Ctrl()) |
Get.find<Ctrl>() |
Vexo.find<Ctrl>() |
0.obs / Rx<int>(0) |
VexoState(0) |
Obx(() => Text(...)) |
VexoBuilder(states:[s], builder:...) |
GetxController |
VexoController |
ever(rx, fn) |
ctrl.ever(state, fn) |
debounce(rx, fn) |
ctrl.debounce(state, fn) |
GetBuilder<T> |
VexoConsumer<T> |
From Provider
| Provider | Vexo |
|---|---|
ChangeNotifier + notifyListeners() |
VexoController (auto-notify) |
ChangeNotifierProvider(create:...) |
VexoScope(create:...) |
MultiProvider([...]) |
VexoMultiScope(scopes:[...]) |
Consumer<T> |
VexoConsumer<T> |
context.read<T>() |
Vexo.find<T>() |
context.watch<T>() |
VexoConsumer<T>(statesOf:...) |
From Riverpod
| Riverpod | Vexo |
|---|---|
StateProvider((ref) => 0) |
VexoState(0) |
NotifierProvider |
VexoController |
AsyncNotifierProvider |
VexoController + VexoAsyncState |
ref.watch(provider) |
VexoConsumer<T>(statesOf:...) |
ref.read(provider) |
Vexo.find<T>() |
AsyncValue<T> |
VexoAsyncValue<T> |
ProviderScope |
Not needed |
From flutter_bloc
| flutter_bloc | Vexo |
|---|---|
Bloc<E, S> |
VexoBloc<E> |
Cubit<S> |
VexoCubitBloc<E, S> |
emit(state) |
emit(state) — identical |
bloc.add(event) |
bloc.add(event) — identical |
BlocProvider |
VexoScope / Vexo.put |
BlocBuilder |
VexoBuilder(states:[bloc.stateAtom],...) |
BlocListener |
VexoListener(state:bloc.stateAtom,...) |
BlocConsumer |
VexoListenerBuilder |
BlocObserver |
VexoMiddleware |
MultiBlocProvider |
VexoMultiScope |
Installation
dependencies:
vexo:
path: ./vexo
import 'package:vexo/vexo.dart';
That is the only import you will ever need. No vexo_flutter, no vexo_hooks, no vexo_codegen. Just Vexo.
MIT License — Mysterious Coder
Libraries
- vexo
- Vexo — The Ultimate Flutter State Management