Zenify
Complete state management for Flutterβhierarchical dependency injection, reactive programming, and intelligent async state. Zero boilerplate, automatic cleanup.
// Hierarchical DI with automatic cleanup
scope.put<UserService>(UserService());
final service = scope.find<UserService>()!; // Access from child scopes
// Reactive state that just works
final count = 0.obs();
Obx(() => Text('$count')) // Auto-rebuilds
// Smart async with caching
final userQuery = ZenQuery<User>(
queryKey: 'user:123',
fetcher: (_) => api.getUser(123),
); // Caching, deduplication, refetchingβall handled
π― Why Zenify?
Building async-heavy Flutter apps? You're probably fighting:
- π Manual cache management - Writing the same cache logic over and over
- π Duplicate API calls - Multiple widgets fetching the same data
- ποΈ Memory leaks - Forgetting to dispose controllers and subscriptions
- π¦ Boilerplate overload - Hundreds of lines for simple async state
Zenify solves all of this.
β‘ What Makes Zenify Different
ποΈ Hierarchical Scoped Architecture
Riverpod-inspired scoping with automatic cleanup. Dependencies flow naturally from parent to child, and scopes dispose themselves automatically when no longer needed. Simple API: Zen.put(), Zen.find(), Zen.delete().
π― Zero Boilerplate Reactivity
GetX-like reactive system with .obs() and Obx(). Write less, accomplish more, keep your code clean. Built on Flutter's ValueNotifier for optimal performance.
π₯ React Query Style
A native-inspired implementation of TanStack Query patterns: automatic caching, smart refetching, request deduplication, and stale-while-revalidateβbuilt on top of the reactive system.
ποΈ Understanding Scopes (The Foundation)
Zenify organizes dependencies into three hierarchical levels with automatic lifecycle management:
The Three Scope Levels
π RootScope (Global - App Lifetime)
- Services like
AuthService,CartService,ThemeService - Lives for entire app session
- Access anywhere via Zen.find
π¦ Module Scope (Feature - Feature Lifetime)
- Controllers shared across feature pages
- Auto-dispose when leaving feature
- Example: HR feature with
CompanyControllerβDepartmentControllerβEmployeeController
π Page Scope (Page - Page Lifetime)
- Page-specific controllers
- Auto-dispose when page pops
- Example:
LoginController,ProfileFormController
When to Use What
| Scope | Use For | Example | Lifetime |
|---|---|---|---|
| RootScope | Needed across entire app | Zen.find<T>() |
App session |
| Module Scope | Needed across a feature | Module registration | Feature navigation |
| Page Scope | Needed on one page | createController |
Single page |
The scope hierarchy automatically manages lifecycle - when you exit a feature, all its controllers clean up automatically. No memory leaks, no manual disposal.
Learn more about hierarchical scopes β
π Quick Start (30 seconds)
1. Install
dependencies:
zenify: ^1.3.5
2. Initialize
void main() {
Zen.init();
runApp(MyApp());
}
3. Create a Controller
class CounterController extends ZenController {
final count = 0.obs();
void increment() => count.value++;
}
4. Build UI
class CounterPage extends ZenView<CounterController> {
// Correct syntax: getter returning a lambda
@override
CounterController Function()? get createController => () => CounterController();
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Obx(() => Text('Count: ${controller.count.value}')),
ElevatedButton(
onPressed: controller.increment,
child: Text('Increment'),
),
],
),
),
);
}
}
That's it! Fully reactive with automatic cleanup. No manual disposal, no memory leaks.
Note:
createControlleris optional! If your controller is already registered in a module or globally, you can omit it and ZenView will find the controller automatically.
π₯ Core Features
1. Hierarchical DI with Auto-Cleanup
Organize dependencies naturally with feature-based modules and parent-child scopes. When you navigate away, everything cleans up automatically.
// App-level services (persistent)
class AppModule extends ZenModule {
@override
void register(ZenScope scope) {
scope.put<AuthService>(AuthService(), isPermanent: true);
scope.put<DatabaseService>(DatabaseService(), isPermanent: true);
}
}
// Feature-level controllers (auto-disposed)
class UserModule extends ZenModule {
@override
void register(ZenScope scope) {
// Access parent services via Zen.find()
final db = scope.find<DatabaseService>()!;
// Register feature-specific dependencies
scope.putLazy<UserRepository>(() => UserRepository(db));
scope.putLazy<UserController>(() => UserController());
}
}
// Use with any router - it's just a widget!
ZenRoute(
moduleBuilder: () => UserModule(),
page: UserPage(),
scopeName: 'UserScope',
)
Core API:
Zen.put<T>()- Register dependenciesZen.find<T>()- Retrieve dependenciesZen.delete<T>()- Remove dependencies
What you get:
- ποΈ Natural dependency flow (parent β child)
- π Automatic disposal (no memory leaks)
- π¦ Clean module organization
- π§ͺ Easy testing (swap modules)
Works with: GoRouter, AutoRoute, Navigator 2.0, any router you like.
See Hierarchical Scopes Guide β
2. Zero-Boilerplate Reactivity
GetX-inspired reactive system built on Flutter's ValueNotifier. Simple, fast, no magic.
class TodoController extends ZenController {
// Reactive primitives
final todos = <Todo>[].obs();
final filter = Filter.all.obs();
// Computed values (auto-update)
List<Todo> get filteredTodos {
switch (filter.value) {
case Filter.active: return todos.where((t) => !t.done).toList();
case Filter.completed: return todos.where((t) => t.done).toList();
default: return todos.toList();
}
}
// Actions
void addTodo(String title) => todos.add(Todo(title));
void toggleTodo(Todo todo) => todo.done = !todo.done;
}
// In UI - automatic rebuilds
Obx(() => Text('${controller.todos.length} todos'))
Obx(() => ListView.builder(
itemCount: controller.filteredTodos.length,
itemBuilder: (context, i) => TodoItem(controller.filteredTodos[i]),
))
What you get:
- β‘ Minimal rebuilds (only affected widgets)
- π― Simple API (
.obs(),Obx(), done) - π Type-safe (compile-time checks)
- ποΈ Zero overhead (built on ValueNotifier)
3. Smart Async State (ZenQuery)
React Query patterns built on the reactive system.
// Define once
final userQuery = ZenQuery<User>(
queryKey: 'user:123',
fetcher: (_) => api.getUser(123),
config: ZenQueryConfig(
staleTime: Duration(minutes: 5),
cacheTime: Duration(hours: 1),
),
);
// Use anywhere - automatic caching, deduplication, refetching
ZenQueryBuilder<User>(
query: userQuery,
builder: (context, user) => UserProfile(user),
loading: () => CircularProgressIndicator(),
error: (error, retry) => ErrorView(error, onRetry: retry),
);
What you get for free:
- β Automatic caching with configurable staleness
- β Smart deduplication (same key = one request)
- β Background refetch on focus/reconnect
- β Stale-while-revalidate (show cached, fetch fresh)
- β Request cancellation (no wasted bandwidth)
- β Optimistic updates with rollback
- β Infinite scroll pagination
- β Real-time streams support
Perfect for: REST APIs, GraphQL, Firebase, any async data source.
π‘ Common Patterns
Global Services with .to Pattern
Access services from anywhere without context or injection:
class CartService extends ZenService {
static CartService get to => Zen.find<CartService>();
final items = <CartItem>[].obs();
void addToCart(Product product) {
items.add(CartItem.fromProduct(product));
}
@override
void onClose() {
// Cleanup happens automatically
super.onClose();
}
}
// Register once
void main() {
Zen.init();
Zen.put<CartService>(CartService(), isPermanent: true);
runApp(MyApp());
}
// Use anywhere - widgets, controllers, helpers
CartService.to.addToCart(product);
Infinite Scroll Pagination
final postsQuery = ZenInfiniteQuery<PostPage>(
queryKey: ['posts'],
infiniteFetcher: (cursor, token) => api.getPosts(cursor: cursor),
);
// Auto-load next page when reaching end
if (index == postsQuery.data.length - 1) postsQuery.fetchNextPage();
Optimistic Updates
final mutation = ZenMutation<User, UpdateArgs>(
onMutate: (args) => userQuery.data.value = args.toUser(), // Instant UI
onError: (err, args, old) => userQuery.data.value = old, // Rollback
);
Real-Time Streams
final chatQuery = ZenStreamQuery<List<Message>>(
queryKey: 'chat',
streamFn: () => chatService.messagesStream,
);
See complete patterns with detailed examples β
π οΈ Advanced Features
- Effects - Automatic loading/error/success state management (guide)
- Computed Values - Auto-updating derived state with dependency tracking
- Global Modules - Register app-wide dependencies at startup
- Performance Control - Choose between reactive (
.obs()+Obx) or manual (update()+ZenBuilder) - Workers - Debounce, throttle, and interval-based reactive handlers
- Devtools - Built-in inspector overlay for debugging scopes and queries
π Learning Path
New to Zenify? Start here:
- 5 minutes: Counter Example - Basic reactivity
- 10 minutes: Todo Example - CRUD with effects
- 15 minutes: ZenQuery Guide - Async state management
- 20 minutes: E-commerce Example - Real-world patterns
Building something complex?
- Hierarchical Scopes Guide - Advanced DI patterns
- State Management Patterns - Architectural patterns
- Testing Guide - Unit, widget, and integration tests
π± Widget Quick Reference
Choose the right widget for your use case:
| Widget | Use When | Rebuilds On |
|---|---|---|
| ZenView | Building pages with controllers | Automatic lifecycle |
| ZenRoute | Need module/scope per route | Route navigation |
| Obx | Need reactive updates | Reactive value changes |
| ZenBuilder | Need manual control | controller.update() call |
| ZenQueryBuilder | Fetching API data | Query state changes |
| ZenStreamQueryBuilder | Real-time data streams | Stream events |
| ZenEffectBuilder | Async operations | Effect state changes |
| ZenConsumer | Accessing dependencies | Manual (no auto-rebuild) |
90% of the time, you'll use:
ZenViewfor pagesObxfor reactive UIZenQueryBuilderfor API calls
π§ Configuration
void main() {
Zen.init();
// Optional: Configure logging and performance tracking
ZenConfig.configure(level: ZenLogLevel.info, performanceTracking: true);
// Optional: Set query defaults
ZenQueryConfig.defaults = ZenQueryConfig(
staleTime: Duration(minutes: 5),
cacheTime: Duration(hours: 1),
);
runApp(MyApp());
}
π§ͺ Testing
Built for testing from the ground up with mocking support:
void main() {
setUp(() => Zen.testMode().clearQueryCache());
tearDown(() => Zen.reset());
test('counter increments', () {
final controller = CounterController();
controller.increment();
expect(controller.count.value, 1);
});
test('mock dependencies', () {
Zen.testMode().mock<ApiClient>(FakeApiClient());
// Test code uses mock automatically
});
}
See complete testing guide β
π Complete Documentation
Core Guides
- Reactive Core Guide - Reactive values, collections, computed properties
- ZenQuery Guide - Async state, caching, mutations
- Effects Guide - Async operations with state management
- Hierarchical Scopes - Advanced DI patterns
- State Management Patterns - Architectural patterns
- Testing Guide - Testing strategies and utilities
Examples
- Counter - Simple reactive state
- Todo App - CRUD operations
- E-commerce - Real-world patterns
- Hierarchical Scopes Demo - Advanced DI
- ZenQuery Demo - Async state management
- Showcase - All features
π Inspired By
Zenify stands on the shoulders of giants:
- GetX by Jonny Borges - For intuitive reactive patterns
- Riverpod by Remi Rousselet - For hierarchical scoping
- React Query by Tanner Linsley - For smart async state
π¬ Community & Support
- π Found a bug? Report it
- π‘ Have an idea? Discuss it
- π Need help? Check our documentation
π License
MIT License - see LICENSE file
π Ready to Get Started?
# Add to pubspec.yaml
flutter pub add zenify
# Try the examples
cd example/counter && flutter run
Choose your path:
- π New to Zenify? β 5-minute Counter Tutorial
- π₯ Want async superpowers? β ZenQuery Guide
- ποΈ Building something complex? β Hierarchical Scopes Guide
- π§ͺ Setting up tests? β Testing Guide
Experience the zen of Flutter development. β¨
Libraries
- controllers/controllers
- controllers/zen_controller
- controllers/zen_controller_scope
- controllers/zen_route_observer
- controllers/zen_service
- core/core
- core/zen_config
- core/zen_environment
- core/zen_log_level
- core/zen_logger
- core/zen_metrics
- core/zen_module
- core/zen_scope
- debug/debug
- debug/zen_debug
- debug/zen_hierarchy_debug
- debug/zen_system_stats
- devtools/devtools
- Development tools and debugging utilities for Zenify
- devtools/inspector/widgets/debug_panel
- devtools/inspector/widgets/dependency_list_view
- devtools/inspector/widgets/query_cache_view
- devtools/inspector/widgets/scope_tree_view
- devtools/inspector/widgets/stats_view
- devtools/inspector/zen_inspector_overlay
- di/di
- di/internal/zen_container
- di/zen_dependency_analyzer
- di/zen_di
- di/zen_lifecycle
- di/zen_reactive
- di/zen_refs
- effects/effects
- effects/zen_effects
- mixins/mixins
- mixins/zen_ticker_provider
- query/core/query_key
- query/core/zen_cancel_token
- query/core/zen_query_cache
- query/core/zen_query_config
- query/core/zen_storage
- query/extensions/zen_scope_query_extension
- query/logic/zen_infinite_query
- query/logic/zen_mutation
- query/logic/zen_query
- query/logic/zen_stream_query
- query/query
- Query system for advanced async state management
- reactive/async/rx_future
- reactive/computed/rx_computed
- reactive/core/reactive_base
- reactive/core/rx_error_handling
- reactive/core/rx_tracking
- reactive/core/rx_value
- reactive/extensions/rx_list_extensions
- reactive/extensions/rx_map_extensions
- reactive/extensions/rx_set_extensions
- reactive/extensions/rx_type_extensions
- reactive/reactive
- reactive/testing/rx_testing
- reactive/utils/rx_logger
- reactive/utils/rx_timing
- reactive/utils/rx_transformations
- testing/testing
- testing/zen_test_mode
- testing/zen_test_utilities
- utils/utils
- utils/zen_scope_inspector
- widgets/builders/zen_builder
- widgets/builders/zen_effect_builder
- widgets/builders/zen_query_builder
- widgets/builders/zen_stream_query_builder
- widgets/components/rx_widgets
- widgets/components/zen_route
- widgets/components/zen_view
- widgets/scope/zen_consumer
- widgets/scope/zen_scope_widget
- widgets/widgets
- workers/workers
- workers/zen_workers
- zenify
- Zenify - Modern Flutter state management