super_qubit 0.1.2
super_qubit: ^0.1.2 copied to clipboard
Hierarchical state management for Flutter. Manage multiple states with event propagation control, cross-communication, and parent-child event handling patterns.
SuperQubit #
A hierarchical state management library for Flutter with SuperQubit and Qubit support.
Features #
✅ Minimal Dependencies - Uses only Flutter SDK and the nested package
✅ Hierarchical State Management - One SuperQubit manages multiple Qubits
✅ Flexible Event Handling - Both parent and child can handle events
✅ Cross-Qubit Communication - Easy communication between Qubits via dispatch and listenTo
✅ Event Propagation Control - Use flags to control when parent/child handlers execute
✅ Type-Safe - Full generic type support
✅ Stream-Based - Native Dart streams for state changes
Why This Exists #
As Flutter applications grow, managing multiple related pieces of state becomes complex. A single feature often requires multiple states working together - for example, a shopping cart needs loading state, items state, and filter state all coordinated under one domain.
Traditional approaches require you to:
- Scatter related states across multiple providers in the widget tree
- Manually coordinate state updates between separate managers
- Write boilerplate to enable communication between sibling state managers
- Nest multiple providers creating deep widget hierarchies
SuperQubit solves these challenges by introducing a hierarchical layer above individual state managers (Qubits). It allows you to:
- Group multiple states under a single feature domain (one SuperQubit manages many Qubits)
- Coordinate automatically with built-in communication via
dispatchandlistenTo - Handle events flexibly at both parent and child levels with propagation control
- Use one provider for an entire feature instead of multiple nested providers
This makes it ideal for complex features like shopping carts, multi-step forms, or dashboards where multiple states need to work together seamlessly as a cohesive unit.
Installation #
Add to your pubspec.yaml:
dependencies:
super_qubit: ^0.1.0
Then run:
flutter pub get
Quick Start #
1. Define States and Events #
// States
class LoadState {
final bool isLoading;
const LoadState({this.isLoading = false});
}
class CartItemsState {
final List<Item> items;
const CartItemsState({this.items = const []});
}
// Events
class LoadEvent {}
class AddItemEvent {
final Item item;
AddItemEvent(this.item);
}
2. Create Qubits #
import 'package:super_qubit/super_qubit.dart';
// Define Event base class
abstract class LoadEventBase {}
class LoadEvent extends LoadEventBase {}
abstract class CartItemsEventBase {}
class AddItemEvent extends CartItemsEventBase {
final Item item;
AddItemEvent(this.item);
}
class LoadQubit extends Qubit<LoadEventBase, LoadState> {
LoadQubit() : super(const LoadState()) {
on<LoadEvent>((event, emit) async {
emit(const LoadState(isLoading: true));
// Fetch data...
emit(const LoadState(isLoading: false));
});
}
}
class CartItemsQubit extends Qubit<CartItemsEventBase, CartItemsState> {
CartItemsQubit() : super(const CartItemsState()) {
on<AddItemEvent>((event, emit) {
final newItems = [...state.items, event.item];
emit(CartItemsState(items: newItems));
});
}
}
3. Create SuperQubit #
class CartSuperQubit extends SuperQubit {
@override
void init() {
// Cross-Qubit communication
listenTo<CartItemsQubit>((state) {
if (state.items.isEmpty) {
dispatch<LoadQubit, LoadEvent>(LoadEvent());
}
});
// Parent-level event handler
on<CartItemsQubit, AddItemEvent>((event, emit) async {
// Handle at parent level (e.g., analytics)
// Child handler will also execute if not using ignoreWhenParentDefines
});
}
// Convenience getters
LoadQubit get load => getQubit<LoadQubit>();
CartItemsQubit get items => getQubit<CartItemsQubit>();
}
4. Provide to Widget Tree #
void main() {
runApp(
SuperQubitProvider<CartSuperQubit>(
superQubit: CartSuperQubit(),
qubits: [
LoadQubit(),
CartItemsQubit(),
],
child: MyApp(),
),
);
}
5. Use in Widgets #
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
// Option 1: Using context extensions
final cart = context.readSuper<CartSuperQubit>();
return StreamBuilder<CartItemsState>(
stream: cart.items.stream,
builder: (context, snapshot) {
final state = snapshot.data ?? const CartItemsState();
return Column(
children: [
Text('Items: ${state.items.length}'),
ElevatedButton(
onPressed: () {
cart.items.add(AddItemEvent(Item('New Item')));
},
child: const Text('Add Item'),
),
],
);
},
);
}
}
// Option 2: Using QubitBuilder (recommended)
class MyWidgetWithBuilder extends StatelessWidget {
@override
Widget build(BuildContext context) {
return QubitBuilder<CartItemsQubit, CartItemsState>(
superQubitType: CartSuperQubit,
builder: (context, state) {
return Column(
children: [
Text('Items: ${state.items.length}'),
ElevatedButton(
onPressed: () {
context
.read<CartSuperQubit, CartItemsQubit>()
.add(AddItemEvent(Item('New Item')));
},
child: const Text('Add Item'),
),
],
);
},
);
}
}
Core Concepts #
Event Propagation Flags #
Control when parent and child handlers execute:
ignoreWhenParentDefines()
Use in child Qubit to skip execution if parent has a handler:
class LoadQubit extends Qubit<LoadEventBase, LoadState> {
LoadQubit() : super(const LoadState()) {
on<LoadEvent>((event, emit) {
// Only runs if parent doesn't handle it
}, ignoreWhenParentDefines());
}
}
ignoreWhenChildDefines<T>()
Use in parent SuperQubit to skip execution if child has a handler:
class CartSuperQubit extends SuperQubit {
@override
void init() {
on<LoadQubit, LoadEvent>((event, emit) async {
// Only runs if LoadQubit doesn't handle it
}, ignoreWhenChildDefines<LoadQubit>());
}
}
Default Behavior (No Flag)
Both parent and child execute in sequence:
- Child handler executes first
- Parent handler executes after
Cross-Qubit Communication #
dispatch<T, E>(event)
Send an event to a specific child Qubit:
dispatch<LoadQubit, LoadEvent>(LoadEvent());
listenTo<T>(callback)
Listen to state changes from a child Qubit:
listenTo<CartItemsQubit>((state) {
if (state.items.isEmpty) {
dispatch<LoadQubit, LoadEvent>(LoadEvent());
}
});
Accessing Child Qubits #
// Get Qubit instance
final loadQubit = superQubit.getQubit<LoadQubit>();
// Get current state
final loadState = superQubit.getState<LoadQubit, LoadState>();
// Or use convenience getters
LoadQubit get load => getQubit<LoadQubit>();
Example App #
See the example/ directory for a complete shopping cart application demonstrating:
- ✅ Multiple Qubits (Load, CartItems, CartFilter)
- ✅ SuperQubit orchestration
- ✅ Cross-Qubit communication
- ✅ Parent and child event handlers
- ✅ Event propagation flags
- ✅ UI integration with StreamBuilder
Run the example:
cd example
flutter pub get
flutter run -d chrome # or macos, ios, android
API Reference #
Qubit<Event, State> #
Base class for state management.
Type Parameters:
Event- Base type for events this Qubit handlesState- Type of state this Qubit manages
Methods:
on<E extends Event>(handler, [config])- Register event handleradd(event)- Add event to be processedclose()- Close and clean up
Properties:
state- Current statestream- Stream of state changesisClosed- Whether Qubit is closed
SuperQubit #
Container for managing multiple child Qubits.
Methods:
registerQubits(qubits)- Register child Qubitson<ChildQubit, Event>(handler, [config])- Register parent event handlerdispatch<T, E>(event)- Dispatch event to child QubitlistenTo<T>(callback)- Listen to child state changesgetQubit<T>()- Get child Qubit by typegetState<T, S>()- Get child Qubit stateclose()- Close SuperQubit and all children
SuperQubitProvider #
Widget that provides SuperQubit to descendants.
Parameters:
superQubit- SuperQubit instancequbits- List of child Qubitschild- Widget tree
Static Methods:
SuperQubitProvider.of<T>(context)- Get SuperQubit from context
MultiSuperQubitProvider #
Widget that provides multiple SuperQubits to descendants.
Convenience widget built on the nested package that nests multiple SuperQubitProviders cleanly without deep indentation.
Example:
MultiSuperQubitProvider(
providers: [
SuperQubitProvider<CartSuperQubit>(
superQubit: CartSuperQubit(),
qubits: [LoadQubit(), CartItemsQubit()],
),
SuperQubitProvider<UserSuperQubit>(
superQubit: UserSuperQubit(),
qubits: [AuthQubit(), ProfileQubit()],
),
SuperQubitProvider<SettingsSuperQubit>(
superQubit: SettingsSuperQubit(),
qubits: [ThemeQubit()],
),
],
child: MyApp(),
)
Parameters:
providers- List of SuperQubitProvider instanceschild- Widget tree
Context Extensions #
For SuperQubit access:
context.readSuper<T>()- Get SuperQubit without listeningcontext.watchSuper<T>()- Get SuperQubit and listen
For child Qubit access:
context.read<T, Q>()- Get child Qubit without listeningcontext.watch<T, Q>()- Get child Qubit and listencontext.watchState<T, Q, S>()- Watch specific child Qubit statecontext.select<T, Q, S, R>(selector)- Select specific value from state
License #
MIT License
Contributing #
Contributions welcome! Please open an issue or PR.