
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 valuesReactive collectionsDevTools integrationAsync reactive helpersStream bindingsForm utilitiesPersistent storage helpersFlutter Web optimizationsIsolate task poolingParallel isolate execution helpersCancelable 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
Libraries
- reactive_flutter
- A lightweight auto-tracking reactive state management library for Flutter.