reactive_notify 2.1.3 reactive_notify: ^2.1.3 copied to clipboard
A Dart library for managing reactive state efficiently, supporting multiple independent instances with unique keys.
ReactiveNotify #
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.
π’ Important Notice: Renamed to ReactiveNotifier #
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.
State of the Libraries #
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
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
Table of Contents #
- Installation
- Quick Start
- State Management Patterns
- MVVM Integration
- Related States System
- Async & Stream Support
- Debugging System
- Best Practices
- Coming Soon
- Contributing
- License
Installation #
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
Quick Start #
Note: All examples in this documentation are compatible with both reactive_notify
and reactive_notifier
.
Basic Usage #
// 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
],
);
},
);
}
}
State Management Patterns #
Global State Declaration #
// β
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!
}
MVVM Integration #
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()),
],
);
},
);
}
}
Related States System #
Correct Pattern #
// 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())
],
);
},
);
}
}
What to Avoid #
// β 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]
);
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 = 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(),
);
}
}
Debugging System #
ReactiveNotify includes a comprehensive debugging system with detailed error messages:
Creation Tracking #
π¦ Creating ReactiveNotify<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 ReactiveNotify 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 ReactiveNotify 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
Stay tuned for this exciting addition to ReactiveNotify!
Contributing #
We welcome contributions! See our Contributing Guide for details.
Star Us! β #
If you find ReactiveNotify helpful, please star us on GitHub! It helps other developers discover this package.
License #
MIT License - see the LICENSE file for details