reactive_notifier 2.2.0 reactive_notifier: ^2.2.0 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.0
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
],
);
},
);
}
}
Core Concepts #
1. State Management Patterns #
// ✅ Recommended: Global state declaration
final userState = ReactiveNotifier<UserState>(() => UserState());
// ✅ Recommended: Mixin with static states
mixin AuthStateMixin {
static final authState = ReactiveNotifier<AuthState>(() => AuthState());
static final sessionState = ReactiveNotifier<SessionState>(() => SessionState());
}
// ❌ Avoid: Never create inside widgets
class WrongWidget extends StatelessWidget {
final state = ReactiveNotifier<int>(() => 0); // Don't do this!
}
2. MVVM Integration #
// 1. Repository Layer
class UserRepository {
Future<User> getUser() async => // Implementation
}
// 2. ViewModel
class UserViewModel extends ViewModelImpl<UserState> {
UserViewModel(UserRepository repository)
: super(repository, UserState());
Future<void> loadUser() async {
try {
final user = await repository.getUser();
setState(UserState(name: user.name, isLoggedIn: true));
} catch (e) {
setError(e);
}
}
}
// 3. Create ViewModel Notifier
final userNotifier = ReactiveNotifier<UserViewModel>(
() => UserViewModel(UserRepository())
);
// 4. 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()),
],
);
},
);
}
}
3. Related States System #
// Define individual states
final userState = ReactiveNotifier<UserState>(() => UserState());
final cartState = ReactiveNotifier<CartState>(() => CartState());
// Create relationships
final appState = ReactiveNotifier<AppState>(
() => AppState(),
related: [userState, cartState]
);
// Access in widgets
class AppDashboard extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ReactiveBuilder<AppState>(
valueListenable: appState,
builder: (context, state, keep) {
final user = appState.from<UserState>();
final cart = appState.from<CartState>();
return Column(
children: [
Text('Welcome ${user.name}'),
Text('Cart Items: ${cart.items.length}'),
],
);
},
);
}
}
4. Async & Stream Support #
// Async Operations
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),
);
}
}
// Stream Handling
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),
);
}
}
Best Practices #
Performance Optimization #
- Use
keep
for static content - Maintain flat state hierarchy
- Avoid unnecessary rebuilds
- Use keyNotifier for specific state access
Architecture Guidelines #
- Follow MVVM pattern
- Use Repository/Service patterns
- Keep state updates context-independent
- Initialize ViewModels automatically
State Management #
- Declare states globally or in mixins
- Maintain flat relationships
- Avoid circular dependencies
- Use type-safe access methods
Debugging #
ReactiveNotifier includes comprehensive debugging tools:
// Enable debugging
ReactiveNotifier.debugMode = true;
// Custom debug logging
ReactiveNotifier.onDebug = (message) {
print('🔍 Debug: $message');
};
// Performance monitoring
ReactiveNotifier.onPerformanceWarning = (details) {
print('⚠️ Performance: ${details.message}');
};
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