reactive_notifier 2.2.1 reactive_notifier: ^2.2.1 copied to clipboard
A Dart library for managing reactive state efficiently, supporting multiples related state.
ReactiveNotifier #
A powerful, elegant, and type-safe state management solution for Flutter that seamlessly integrates with MVVM pattern while maintaining complete independence from BuildContext. Perfect for applications of any size.
Note: Are you migrating from
reactive_notify
? The API remains unchanged - just update your dependency toreactive_notifier
.
Features #
- π Simple and intuitive API
- ποΈ Perfect for MVVM architecture
- π Independent from BuildContext
- π― Type-safe state management
- π‘ Built-in Async and Stream support
- π Smart related states system
- π οΈ Repository/Service layer integration
- β‘ High performance with minimal rebuilds
- π Powerful debugging tools
- π Detailed error reporting
Installation #
Add this to your package's pubspec.yaml
file:
dependencies:
reactive_notifier: ^2.2.1
Quick Start #
Basic Usage #
// Define states globally or in a mixin
final counterState = ReactiveNotifier<int>(() => 0);
// Using a mixin (recommended for organization)
mixin AppStateMixin {
static final counterState = ReactiveNotifier<int>(() => 0);
static final userState = ReactiveNotifier<UserState>(() => UserState());
}
// Use in widgets - No BuildContext needed for state management!
class CounterWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ReactiveBuilder<int>(
valueListenable: AppStateMixin.counterState,
builder: (context, value, keep) {
return Column(
children: [
Text('Count: $value'),
keep(const CounterButtons()), // Static content preserved
],
);
},
);
}
}
State Management Patterns #
Global State Declaration #
// β
Correct: Global state declaration
final userState = ReactiveNotifier<UserState>(() => UserState());
// β
Correct: Mixin with static states
mixin AuthStateMixin {
static final authState = ReactiveNotifier<AuthState>(() => AuthState());
static final sessionState = ReactiveNotifier<SessionState>(() => SessionState());
}
// β Incorrect: Never create inside widgets
class WrongWidget extends StatelessWidget {
final state = ReactiveNotifier<int>(() => 0); // Don't do this!
}
MVVM Integration #
ReactiveNotifier is built with MVVM in mind:
// 1. Repository Layer
class UserRepository implements RepositoryImpl<User> {
final ApiNotifier apiNotifier;
UserRepository(this.apiNotifier);
Future<User> getUser() async => // Implementation
}
// 2. Service Layer (Alternative to Repository)
class UserService implements ServiceImpl<User> {
Future<User> getUser() async => // Implementation
}
// 3. ViewModel
class UserViewModel extends ViewModelImpl<UserState> {
UserViewModel(UserRepository repository)
: super(repository, UserState(), 'user-vm', 'UserScreen');
@override
void init() {
// Automatically called on initialization
loadUser();
}
Future<void> loadUser() async {
try {
final user = await repository.getUser();
setState(UserState(name: user.name, isLoggedIn: true));
} catch (e) {
// Error handling
}
}
}
// Without Repository If you need to handle other types of logic or use external Notifiers too.
class SimpleViewModel extends ViewModelStateImpl<UserState> {
SimpleViewModel(): super(UserState());
void updateUser(String name) {
updateState(UserState(name: name));
}
}
// 4. Create ViewModel Notifier
final userNotifier = ReactiveNotifier<UserViewModel>(() {
final repository = UserRepository(apiNotifier);
return UserViewModel(repository);
});
// 5. Use in View
class UserScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ReactiveBuilder<UserViewModel>(
valueListenable: userNotifier,
builder: (_, viewModel, keep) {
return Column(
children: [
Text('Welcome ${viewModel.state.name}'),
keep(const UserActions()),
],
);
},
);
}
}
Related States System #
Correct Pattern #
// 1. Define individual states
final userState = ReactiveNotifier<UserState>(() => UserState());
final cartState = ReactiveNotifier<CartState>(() => CartState());
final settingsState = ReactiveNotifier<SettingsState>(() => SettingsState());
// 2. Create relationships correctly
final appState = ReactiveNotifier<AppState>(
() => AppState(),
related: [userState, cartState, settingsState]
);
// 3. Use in widgets - Updates automatically when any related state changes
class AppDashboard extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ReactiveBuilder<AppState>(
valueListenable: appState,
builder: (context, state, keep) {
// Access related states directly
final user = appState.from<UserState>();
final cart = appState.from<CartState>(cartState.keyNotifier);
// or use userState, cartState directly, [ Text('Welcome ${userState.name}')]
return Column(
children: [
Text('Welcome ${user.name}'),
Text('Cart Items: ${cart.items.length}'),
if (user.isLoggedIn) keep(const UserProfile())
],
);
},
);
}
}
What to Avoid #
// β NEVER: Nested related states
final cartState = ReactiveNotifier<CartState>(
() => CartState(),
related: [userState] // β Don't do this
);
// β NEVER: Chain of related states
final orderState = ReactiveNotifier<OrderState>(
() => OrderState(),
related: [cartState] // β Avoid relation chains
);
// β
CORRECT: Flat structure with single parent
final appState = ReactiveNotifier<AppState>(
() => AppState(),
related: [userState, cartState, orderState]
);
Async & Stream Support #
Async Operations #
class ProductViewModel extends AsyncViewModelImpl<List<Product>> {
@override
Future<List<Product>> fetchData() async {
return await repository.getProducts();
}
}
class ProductsScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ReactiveAsyncBuilder<List<Product>>(
viewModel: productViewModel,
buildSuccess: (products) => ProductGrid(products),
buildLoading: () => const LoadingSpinner(),
buildError: (error, stack) => ErrorWidget(error),
buildInitial: () => const InitialView(),
);
}
}
Stream Handling #
final messagesStream = ReactiveNotifier<Stream<Message>>(
() => messageRepository.getMessageStream()
);
class ChatScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ReactiveStreamBuilder<Message>(
streamNotifier: messagesStream,
buildData: (message) => MessageBubble(message),
buildLoading: () => const LoadingIndicator(),
buildError: (error) => ErrorMessage(error),
buildEmpty: () => const NoMessages(),
buildDone: () => const StreamComplete(),
);
}
}
Debugging System #
ReactiveNotifier includes a comprehensive debugging system with detailed error messages:
Creation Tracking #
π¦ Creating ReactiveNotifier<UserState>
π With related types: CartState, OrderState
Invalid Structure Detection #
β οΈ Invalid Reference Structure Detected!
ββββββββββββββββββββββββββββββ
Current Notifier: CartState
Key: cart_key
Problem: Attempting to create a notifier with an existing key
Solution: Ensure unique keys for each notifier
Location: package:my_app/cart/cart_state.dart:42
Performance Monitoring #
β οΈ Notification Overflow Detected!
ββββββββββββββββββββββββββββββ
Notifier: CartState
50 notifications in 500ms
β Problem: Excessive updates detected
β
Solution: Review update logic and consider debouncing
And more...
Best Practices #
State Declaration #
- Declare ReactiveNotifier instances globally or as static mixin members
- Never create instances inside widgets
- Use mixins for better organization of related states
Performance Optimization #
- Use
keep
for static content - Maintain flat state hierarchy
- Use keyNotifier for specific state access
- Avoid unnecessary rebuilds
Architecture Guidelines #
- Follow MVVM pattern
- Utilize Repository/Service patterns
- Let ViewModels initialize automatically
- Keep state updates context-independent
Related States #
- Maintain flat relationships
- Avoid circular dependencies
- Use type-safe access
- Keep state updates predictable
Coming Soon: Real-Time State Inspector π #
We're developing a powerful visual debugging interface that will revolutionize how you debug and monitor ReactiveNotifier states:
Features in Development #
- π Real-time state visualization
- π Live update tracking
- π Performance metrics
- πΈοΈ Interactive dependency graph
- β±οΈ Update timeline
- π Deep state inspection
- π± DevTools integration
This tool will help you:
- Understand state flow in real-time
- Identify performance bottlenecks
- Debug complex state relationships
- Monitor rebuild patterns
- Optimize your application
- Develop more efficiently
Examples #
Check out our example app for more comprehensive examples and use cases.
Contributing #
We love contributions! Please read our Contributing Guide first.
- Fork it
- Create your feature branch (
git checkout -b feature/amazing
) - Commit your changes (
git commit -am 'Add amazing feature'
) - Push to the branch (
git push origin feature/amazing
) - Create a new Pull Request
Support #
- π Star the repo to show support
- π Create an issue for bugs
- π‘ Submit feature requests through issues
- π Contribute to the documentation
License #
This project is licensed under the MIT License - see the LICENSE file for details.
Made with β€οΈ by JhonaCodes