zenify 1.9.0 copy "zenify: ^1.9.0" to clipboard
zenify: ^1.9.0 copied to clipboard

Powerful asynchronous state management, automatic caching, hierarchical DI, and zero-boilerplate reactivity. A holistic architecture for modern Flutter apps.

Zenify #

pub package likes pub points license: MIT

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();
ZenObserver(() => 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 #

Reactive system with .obs() and ZenObserver() (or Obx() for GetX users). Write less, accomplish more, keep your code clean. Built on Flutter's ValueNotifier for optimal performance.

React Query Style Async State #

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.

Offline-First Resilience #

Don't let network issues break your app. Zenify includes robust persistence, an offline mutation queue, and optimistic updates out of the box with minimal configuration.


πŸ—οΈ 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<CartService>() or the .to pattern: CartService.to.addItem()

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.9.0

2. Initialize #

void main() async {
  await 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> {
  @override
  CounterController Function()? get createController => () => CounterController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ZenObserver(() => 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: createController is optional! If your controller is already registered in a module or globally, you can omit it and ZenView will find the controller automatically.

See complete example β†’


πŸ”₯ 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 dependencies
  • Zen.find<T>() β€” Retrieve dependencies
  • Zen.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
ZenObserver(() => Text('${controller.todos.length} todos'))
ZenObserver(() => ListView.builder(
  itemCount: controller.filteredTodos.length,
  itemBuilder: (context, i) => TodoItem(controller.filteredTodos[i]),
))

What you get:

  • Minimal rebuilds (only affected widgets)
  • Simple API (.obs(), ZenObserver(), done)
  • Type-safe (compile-time checks)
  • Zero overhead (built on ValueNotifier)

See Reactive Core Guide β†’

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
  • βœ… Tag & wildcard group invalidation (tags: ['user'], invalidateQueriesByTag, invalidateQueriesByPattern)

Perfect for: REST APIs, GraphQL, Firebase, any async data source.

See ZenQuery Guide β†’

4. Offline Synchronization Engine #

Turn your app into an offline-capable powerhouse with minimal configuration.

// Auto-persist data to disk
final postsQuery = ZenQuery<List<Post>>(
  queryKey: 'posts',
  fetcher: (_) => api.getPosts(),
  config: ZenQueryConfig(
    persist: true,
    networkMode: NetworkMode.offlineFirst,
  ),
);

// Queue mutations when offline
final createPost = ZenMutation<Post, Post>(
  mutationKey: 'create_post', // Enables queuing
  mutationFn: (post) => api.createPost(post),
);

Key capabilities:

  • Storage agnostic β€” Works with Hive, SharedPreferences, SQLite, or any backend via ZenStorage
  • Mutation queue β€” Actions are queued and auto-replayed when online
  • Optimistic updates β€” Update UI immediately, sync later
  • Network modes β€” Control exactly how queries behave offline

See Offline Guide β†’


πŸ’‘ 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 #

Mutations provide automatic loading/error states, optimistic UI updates, offline queueing, and cache synchronization. Learn why mutations are better than direct API calls β†’

// Easy way: Use helpers (recommended)
final createPost = ZenMutation.listPut<Post>(
  queryKey: 'posts',
  mutationFn: (post) => api.createPost(post),
  onError: (err, post) => logger.error('Create failed', err), // Rollback automatic!
);

// Advanced: Manual control for complex scenarios
final mutation = ZenMutation<User, UpdateArgs>(
  onMutate: (args) => userQuery.data.value = args.toUser(),
  onError: (err, args, old) => userQuery.data.value = old, // Manual 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() + ZenObserver) or manual (update() + ZenBuilder)
  • Workers β€” Debounce, throttle, and interval-based reactive handlers
  • DevTools β€” Built-in inspector for debugging scopes and queries

See detailed examples β†’


πŸŽ“ Learning Path #

New to Zenify? Start here:

  1. 5 minutes: Counter Example β€” Basic reactivity
  2. 10 minutes: Todo Example β€” CRUD with effects
  3. 15 minutes: ZenQuery Guide β€” Async state management
  4. 20 minutes: E-commerce Example β€” Real-world patterns
  5. 30 minutes: Offline Demo β€” Full offline-first app with persistence, mutation queue & SharedPreferences

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
ZenObserver 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:

  • ZenView for pages
  • ZenObserver for reactive UI
  • ZenQueryBuilder for API calls

πŸ”§ Configuration #

void main() {
  Zen.init();

  // Optional: Configure logging and performance tracking
  ZenConfig.configure(level: ZenLogLevel.info, performanceTracking: true);

  // Optional: Set global query defaults
  Zen.queryCache.setDefaultConfig(ZenQueryConfig(
    staleTime: Duration(minutes: 5),
    cacheTime: Duration(hours: 1),
  ));

  runApp(MyApp());
}

πŸ§ͺ Testing #

Built for testing from the ground up:

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
  });

  test('query with in-memory storage', () async {
    // InMemoryStorage is built-in β€” no external dependencies needed
    Zen.queryCache.setStorage(InMemoryStorage());
    final q = ZenQuery<String>(
      queryKey: 'test',
      fetcher: (_) async => 'hello',
      config: ZenQueryConfig(persist: true, toJson: (s) => {'v': s}, fromJson: (j) => j['v']),
    );
    await q.fetch();
    expect(q.data.value, 'hello');
  });
}

See complete testing guide β†’


πŸ” Flutter DevTools Extension #

Zenify has a separate DevTools extension package for real-time inspection and debugging.

Quick Setup #

  1. Add the DevTools extension (as a dev dependency):
dev_dependencies:
  zenify_devtools_extension: ^1.0.0
  1. Register service extensions in your app:
import 'package:zenify/devtools/devtools.dart';

void main() {
  ZenServiceExtensions.registerExtensions();
  runApp(MyApp());
}
  1. Enable the extension in devtools_options.yaml:
extensions:
  - zenify_devtools_extension: true

Features #

3-Tab Inspector:

  1. Scope Inspector β€” Visualize your entire DI hierarchy with hierarchical tree view, dependency breakdown, and parent-child relationships
  2. Query Cache Viewer β€” Monitor all cached queries; search, filter, refetch, and invalidate with visual status indicators
  3. Metrics Dashboard β€” Live scope and query metrics to identify bottlenecks and detect memory leaks

Learn more β†’


πŸ“š Complete Documentation #

Core Guides #

  • Reactive Core Guide β€” Reactive values, collections, computed properties
  • ZenQuery Guide β€” Async state, caching, mutations
  • Offline-First Guide β€” Persistence & synchronization
  • 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
  • Offline Demo β€” Full offline-first app: persistence, mutation queue, SharedPreferences adapter, and optimistic updates
  • 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 #


πŸ“„ License #

MIT License β€” see LICENSE file


πŸš€ Ready to Get Started? #

flutter pub add zenify

Choose your path:

  • New to Zenify? β†’ 5-minute Counter Tutorial
  • Want async superpowers? β†’ ZenQuery Guide
  • Need offline support? β†’ Offline Guide
  • Building something complex? β†’ Hierarchical Scopes Guide
  • Setting up tests? β†’ Testing Guide

Experience the zen of Flutter development.

9
likes
0
points
469
downloads

Documentation

Documentation

Publisher

unverified uploader

Weekly Downloads

Powerful asynchronous state management, automatic caching, hierarchical DI, and zero-boilerplate reactivity. A holistic architecture for modern Flutter apps.

Repository (GitHub)
View/report issues

Topics

#state-management #async-state #caching #dependency-injection #react-query

License

unknown (license)

Dependencies

flutter

More

Packages that depend on zenify