reactive_flutter 1.0.5
reactive_flutter: ^1.0.5 copied to clipboard
A lightweight auto-tracking reactive state management library for Flutter.

A lightweight auto-tracking reactive state management library for Flutter.
reactive_flutter provides:
- Reactive state containers
- Automatic widget rebuild tracking
- Lightweight dependency injection
- Page-based pagination
- Cursor-based pagination
- Reactive search
- Reactive logger
- Minimal boilerplate
- Zero code generation
Wiki #
About #
reactive_flutter is designed to provide a lightweight and minimal reactive architecture for Flutter applications.
It focuses on:
- Simplicity
- Performance
- Automatic dependency tracking
- Minimal boilerplate
- Easy integration
Features #
✅ Automatic dependency tracking
✅ Lightweight and fast
✅ No `BuildContext` required for state access
✅ No manual dependency lists
✅ No code generation
✅ Page-based pagination
✅ Cursor-based pagination
✅ Reactive search with debounce
✅ Reactive logger
✅ Simple dependency injection
✅ Easy to learn and use
✅ Background isolate task execution
✅ Reactive isolate task state
✅ Heavy computation without UI freeze
✅ Generic isolate task payloads
Quick Start #
final Reactive<int> counter = Reactive<int>(0);
Watch(
builder: () {
return Text('${counter.value}');
},
)
Update state:
counter.value++;
Reactive State #
Reactive<T> is a lightweight reactive value holder.
Whenever the value changes, widgets that depend on it automatically rebuild.
Create Reactive Values #
final Reactive<int> counter = Reactive<int>(0);
final Reactive<String> title = Reactive<String>('Flutter');
final Reactive<bool> isDark = Reactive<bool>(false);
Reading Values #
Use .value to access the current value.
print(counter.value);
Updating Values #
counter.value++;
isDark.value = true;
Silent Updates #
Use setSilent() to update a value without notifying listeners.
counter.setSilent(100);
Watch Widget #
Watch automatically rebuilds whenever a reactive value used inside the builder changes.
No dependency list is required.
Basic Example #
Watch(
builder: () {
return Text('${counter.value}');
},
)
Multiple Reactive Dependencies #
Watch(
builder: () {
return Column(
children: [
Text('${counter.value}'),
Text(title.value),
Switch(
value: isDark.value,
onChanged: (value) {
isDark.value = value;
},
),
],
);
},
)
Conditional Tracking #
Dependencies are tracked automatically based on what is accessed during build.
Watch(
builder: () {
return isDark.value
? const Text('Dark Mode')
: const Text('Light Mode');
},
)
Only the active branch is subscribed.
Nested Watch Example #
Watch(
builder: () {
return Column(
children: [
Watch(
builder: () {
return Text(
counter.value.toString(),
);
},
),
Watch(
builder: () {
return Text(title.value);
},
),
],
);
},
)
Dependency Injection #
ReactiveInjector is a lightweight service locator.
Supports:
- Singleton registration
- Transient registration
- Dependency lookup
- Reset
- Unregister
- Clear all
Register Singleton #
ReactiveInjector.singleton<ApiService>(() => ApiService());
Retrieve dependency:
final ApiService api = ReactiveInjector.find<ApiService>();
Register Transient #
ReactiveInjector.transient<UserRepository>(() => UserRepository());
Check Registration #
final bool exists = ReactiveInjector.isRegistered<ApiService>();
Reset Singleton #
ReactiveInjector.reset<ApiService>();
Unregister Dependency #
ReactiveInjector.unregister<ApiService>();
Clear All Dependencies #
ReactiveInjector.clear();
Page-Based Pagination #
ReactivePagination<T> helps manage paginated APIs using page numbers.
Create Pagination Controller #
final ReactivePagination<User> pagination = ReactivePagination<User>(
perPage: 20,
fetcher: (page, perPage) async {
return api.fetchUsers(page, perPage);
},
);
Initialize Pagination #
await pagination.init();
Refresh Pagination #
await pagination.refresh();
Load More #
await pagination.fetchMore();
Watch Pagination State #
Watch(
builder: () {
if (pagination.isLoading) {
return const CircularProgressIndicator();
}
return ListView.builder(
itemCount: pagination.items.length,
itemBuilder: (context, index) {
final User user = pagination.items[index];
return ListTile(title: Text(user.name));
},
);
},
)
Scroll Pagination Example #
class UsersPage extends StatefulWidget {
const UsersPage({super.key});
@override
State<UsersPage> createState() => _UsersPageState();
}
class _UsersPageState extends State<UsersPage> {
final ScrollController controller =ScrollController();
final ReactivePagination<User> pagination = ReactivePagination<User>(
perPage: 20,
fetcher: (page, limit) async {
return api.fetchUsers(page, limit);
},
);
@override
void initState() {
super.initState();
pagination.init();
controller.addListener(() {
if (controller.position.pixels >=
controller.position.maxScrollExtent -
200) {
pagination.fetchMore();
}
});
}
@override
Widget build(BuildContext context) {
return Watch(
builder: () {
return ListView.builder(
controller: controller,
itemCount: pagination.items.length,
itemBuilder: (context, index) {
final User user = pagination.items[index];
return ListTile(title: Text(user.name));
},
);
},
);
}
}
Cursor-Based Pagination #
ReactiveCursorPagination<T, C> supports cursor-based APIs.
Examples of cursor types:
StringintDateTimeDocumentSnapshot(Firebase Firestore)- Firestore document snapshots
- Custom models
Create Cursor Pagination #
final ReactiveCursorPagination<User, String>
pagination =
ReactiveCursorPagination<User, String>(
perPage: 20,
fetcher: (perPage, cursor) async {
return api.fetchUsers(perPage, cursor);
},
);
PaginationResult #
PaginationResult<User, String>(
items: users,
nextCursor: nextCursor,
)
Cursor Pagination State #
pagination.items
pagination.cursor
pagination.isLoading
pagination.isMoreLoading
pagination.hasMore
pagination.error
pagination.totalFetched
pagination.isEmpty
Reactive Search #
ReactiveSearch<T> provides debounced reactive searching.
Features:
- Debounced queries
- Idle/loading/error states
- Stale request protection
- Automatic rebuilding
Create Search Controller #
final ReactiveSearch<User> search =
ReactiveSearch<User>(
debounceMs: 500,
minLength: 2,
fetcher: (query) async {
return api.searchUsers(query);
},
);
Debounced Search #
TextField(onChanged: search.onChanged)
Manual Search #
await search.search('flutter');
Clear Search #
search.clear();
Watch Search State #
Watch(
builder: () {
if (search.isLoading) {
return const CircularProgressIndicator();
}
if (search.isEmpty) {
return const Text('No results');
}
return ListView.builder(
itemCount: search.results.length,
itemBuilder: (context, index) {
final User user = search.results[index];
return ListTile(title: Text(user.name));
},
);
},
)
Reactive Isolate Tasks #
ReactiveIsolateTask<T> provides reactive isolate execution for heavy background operations.
Features:
- Background isolate execution
- Reactive loading state
- Reactive error handling
- Generic reusable task payloads
- Non-blocking UI updates
Create Isolate Task #
final ReactiveIsolateTask<int> task = ReactiveIsolateTask<int>();
Run Task #
await task.run<int>(
ReactiveTaskPayload(input: 1000000, allback: heavyCalculation),
);
Watch Task State #
Watch(
builder: () {
if (task.isLoading) {
return const CircularProgressIndicator();
}
if (task.error != null) {
return Text(task.error.toString());
}
return Text('Result: ${task.data}');
},
)
Handle Errors #
await task.run<String>(
ReactiveTaskPayload(
input: 'data',
callback: (value) {
throw Exception('Something failed');
},
),
);
print(task.error);
Heavy Task Example #
int heavyCalculation(int total) {
int sum = 0;
for (int i = 0; i < total; i++) {
sum += i;
}
return sum;
}
await task.run<int>(
ReactiveTaskPayload(
input: 10000000,
callback: heavyCalculation,
),
);
Reactive Logger #
ReactiveLogger is a lightweight persistent logger with reactive state support.
Features:
- File-based logs
- Auto-clear policies
- Buffered writes
- Console output
- Error & stack trace logging
- Reactive logger state
Initialize Logger #
final ReactiveLogger logger = ReactiveLogger(fileName: 'app_logs', clearPolicy: ClearPolicy.weekly);
await logger.init();
Write Logs #
logger.debug('Debug message');
logger.info('User logged in');
logger.warning('Slow API response');
logger.error('Request failed', error: exception, stack: stackTrace);
logger.fatal('Critical failure');
Read Logs #
LoggerView.open(context, logger: logger);
Clear Logs #
await logger.clearFile();
Delete Logs #
await logger.deleteFile();
Logger State #
logger.value.totalLogs
logger.value.lastCleared
logger.value.isReady
API Overview #
Reactive #
| Function | Description |
|---|---|
value |
Get or update reactive value |
setSilent() |
Update without notifying |
toString() |
Debug string |
Watch #
| Property | Description |
|---|---|
builder |
Auto-tracked widget builder |
ReactiveInjector #
| Function | Description |
|---|---|
singleton() |
Register singleton |
transient() |
Register transient |
find() |
Resolve dependency |
isRegistered() |
Check registration |
reset() |
Reset singleton |
unregister() |
Remove dependency |
clear() |
Remove all dependencies |
ReactivePagination #
| Function | Description |
|---|---|
init() |
Load first page |
refresh() |
Reload pagination |
fetchMore() |
Load next page |
ReactiveCursorPagination #
| Function | Description |
|---|---|
init() |
Load first cursor page |
refresh() |
Reload pagination |
fetchMore() |
Load next cursor page |
ReactiveSearch #
| Function | Description |
|---|---|
onChanged() |
Debounced search |
search() |
Manual search |
clear() |
Reset search |
results |
Current results |
isLoading |
Loading state |
isIdle |
Idle state |
isEmpty |
Empty state |
error |
Current error |
ReactiveIsolateTask #
| Function | Description |
|---|---|
run() |
Run isolate task safely |
runOrThrow() |
Run isolate task and throw error |
reset() |
Reset task state |
isLoading |
Current loading state |
data |
Latest task result |
error |
Latest task error |
ReactiveLogger #
| Function | Description |
|---|---|
debug() |
Write debug log |
info() |
Write info log |
warning() |
Write warning log |
error() |
Write error log |
fatal() |
Write fatal log |
clearFile() |
Clear log file |
deleteFile() |
Delete log file |
readRaw() |
Read raw log |
readLines() |
Read log lines |
Performance #
reactive_flutter is designed to stay lightweight and fast.
Why it performs well #
- No reflection
- No code generation
- Fine-grained rebuild tracking
- Minimal allocations
- Conditional dependency tracking
- Only subscribed widgets rebuild
- Background isolate execution for heavy operations
- Large file parsing without blocking UI
- Reactive async task state management
Architecture #
Reactive<T>
↓
ReactiveTracker
↓
Watch Widget
↓
Automatic Rebuild
How it works #
Watchstarts dependency tracking- Accessed reactive values register themselves
Watchsubscribes only to used reactives- Changed reactives rebuild subscribed widgets
Comparison #
| Feature | reactive_flutter | GetX | Riverpod | Provider |
|---|---|---|---|---|
| Auto tracking | ✅ | ⚠️ Partial | ❌ | ❌ |
| Code generation | ❌ | ❌ | ⚠️ Optional | ❌ |
| Boilerplate | Very Low | Low | Medium | Medium |
| Dependency injection | ✅ | ✅ | ❌ | ❌ |
| Pagination helpers | ✅ | ❌ | ❌ | ❌ |
| Reactive search | ✅ | ❌ | ❌ | ❌ |
| Reactive logger | ✅ | ❌ | ❌ | ❌ |
| Reactive isolate tasks | ✅ | ❌ | ❌ | ❌ |
| Automatic rebuild tracking | ✅ | ⚠️ Partial | ❌ | ❌ |
| Zero-config watchers | ✅ | ⚠️ | ❌ | ❌ |
| Learning curve | Easy | Easy | Medium | Easy |
| Lightweight | ✅ | ⚠️ | ⚠️ | ✅ |
| No BuildContext access | ✅ | ✅ | ✅ | ❌ |
Best Practices #
- Keep reactive values focused and small
- Prefer multiple small reactives
- Use nested
Watchwidgets for granular rebuilds - Dispose controllers when needed
- Avoid unnecessary global state
When to Use reactive_flutter #
reactive_flutter works especially well for:
- Small to medium apps
- Utility applications
- MVPs & prototypes
- Admin panels
- Feature modules
- Teams preferring lightweight architecture
Advanced Reactive Example #
final Reactive<List<String>> todos = Reactive<List<String>>([]);
void addTodo(String value) {
todos.value = [ ...todos.value, value ];
}
void removeTodo(String value) {
todos.value = todos.value.where((e) => e != value).toList();
}
Unit Testing Example #
void main() {
test(
'Reactive value updates correctly',
() {
final Reactive<int> counter = Reactive<int>(0);
counter.value = 5;
expect(counter.value, 5);
},
);
}
FAQ #
Does this use code generation? #
No.
reactive_flutter works without generators or build_runner.
Does Watch rebuild the whole app? #
No.
Only widgets subscribed to changed reactive values rebuild.
Can I use it with existing architectures? #
Yes.
Works well with:
- Clean Architecture
- MVVM
- MVC
- Feature-first architecture
Does it support async state? #
Yes.
You can store async results, pagination state, and API responses inside reactive values.
Is it production ready? #
Yes.
The library is designed to be lightweight, predictable, and production friendly.
Roadmap #
Planned future improvements:
- ❌ Computed reactive values
- ❌ Reactive collections
- ❌ DevTools integration
- ❌ Async reactive helpers
- ❌ Stream bindings
- ❌ Form utilities
- ❌ Persistent storage helpers
- ❌ Flutter Web optimizations
- ❌ Isolate task pooling
- ❌ Parallel isolate execution helpers
- ❌ Cancelable isolate tasks
Contributing #
Contributions are welcome.
Setup #
git clone <repository>
cd reactive_flutter
flutter pub get
Run Tests #
flutter test
Format Code #
dart format .
License #
MIT License © V Developer