A powerful, elegant, and type-safe state management solution for Flutter that seamlessly integrates with the MVVM pattern while maintaining complete independence from BuildContext. Perfect for applications of any size.
We’ve renamed this package to reactive_notifier
to better align with naming conventions and improve clarity. reactive_notify
is now deprecated, and we strongly recommend transitioning to the updated package for future projects.
Package | Status | Version |
---|---|---|
reactive_notify |
Deprecated | |
reactive_notifier |
Active |
The API remains unchanged, so you can upgrade to reactive_notifier
seamlessly by simply updating your dependencies.
dependencies:
reactive_notifier: latest # See version badge above
- 🚀 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
- Quick Start
- State Management Patterns
- MVVM Integration
- Related States System
- Async & Stream Support
- Debugging System
- Best Practices
- Coming Soon
- Contributing
- License
To continue using the deprecated package:
dependencies:
reactive_notify: ^2.1.3
To switch to the updated version:
dependencies:
reactive_notifier: latest # See version badge above
Note: All examples in this documentation are compatible with both reactive_notify
and reactive_notifier
.
// Define states globally or in a mixin
final counterState = ReactiveNotify<int>(() => 0);
// Using a mixin (recommended for organization)
mixin AppStateMixin {
static final counterState = ReactiveNotify<int>(() => 0);
static final userState = ReactiveNotify<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
],
);
},
);
}
}
// ✅ Correct: Global state declaration
final userState = ReactiveNotify<UserState>(() => UserState());
// ✅ Correct: Mixin with static states
mixin AuthStateMixin {
static final authState = ReactiveNotify<AuthState>(() => AuthState());
static final sessionState = ReactiveNotify<SessionState>(() => SessionState());
}
// ❌ Incorrect: Never create inside widgets
class WrongWidget extends StatelessWidget {
final state = ReactiveNotify<int>(() => 0); // Don't do this!
}
ReactiveNotify 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
}
}
}
// 4. Create ViewModel Notifier
final userNotifier = ReactiveNotify<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()),
],
);
},
);
}
}
// 1. Define individual states
final userState = ReactiveNotify<UserState>(() => UserState());
final cartState = ReactiveNotify<CartState>(() => CartState());
final settingsState = ReactiveNotify<SettingsState>(() => SettingsState());
// 2. Create relationships correctly
final appState = ReactiveNotify<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);
return Column(
children: [
Text('Welcome ${user.name}'),
Text('Cart Items: ${cart.items.length}'),
if (user.isLoggedIn) keep(const UserProfile())
],
);
},
);
}
}
// ❌ NEVER: Nested related states
final cartState = ReactiveNotify<CartState>(
() => CartState(),
related: [userState] // ❌ Don't do this
);
// ❌ NEVER: Chain of related states
final orderState = ReactiveNotify<OrderState>(
() => OrderState(),
related: [cartState] // ❌ Avoid relation chains
);
// ✅ CORRECT: Flat structure with single parent
final appState = ReactiveNotify<AppState>(
() => AppState(),
related: [userState, cartState, orderState]
);
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(),
);
}
}
final messagesStream = ReactiveNotify<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(),
);
}
}
ReactiveNotify includes a comprehensive debugging system with detailed error messages:
📦 Creating ReactiveNotify<UserState>
🔗 With related types: CartState, OrderState
⚠️ 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
⚠️ Notification Overflow Detected!
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Notifier: CartState
50 notifications in 500ms
❌ Problem: Excessive updates detected
✅ Solution: Review update logic and consider debouncing
And more...
- Declare ReactiveNotify instances globally or as static mixin members
- Never create instances inside widgets
- Use mixins for better organization of related states
- Use
keep
for static content - Maintain flat state hierarchy
- Use keyNotifier for specific state access
- Avoid unnecessary rebuilds
- Follow MVVM pattern
- Utilize Repository/Service patterns
- Let ViewModels initialize automatically
- Keep state updates context-independent
- Maintain flat relationships
- Avoid circular dependencies
- Use type-safe access
- Keep state updates predictable
We're developing a powerful visual debugging interface that will revolutionize how you debug and monitor ReactiveNotify states:
- 📊 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
Stay tuned for this exciting addition to ReactiveNotify!
We welcome contributions! See our Contributing Guide for details.
If you find ReactiveNotify helpful, please star us on GitHub! It helps other developers discover this package.
MIT License - see the LICENSE file for details