zenify 1.3.5
zenify: ^1.3.5 copied to clipboard
Powerful asynchronous state management, automatic caching, hierarchical DI, and zero-boilerplate reactivity. A holistic architecture for modern Flutter apps.
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. β¨