Zenify

pub package likes pub points license: MIT

A modern state management library for Flutter that brings true "zen" to your development experience. Clean, intuitive, and powerful.

Why Zenify?

Stop fighting with state management. Zenify offers an elegant solution that keeps your codebase clean and your mind at peace:

  • πŸš€ Less Boilerplate: Write less code while accomplishing more
  • πŸ—οΈ Module System: Organize dependencies into clean, reusable modules
  • πŸ”— Natural Hierarchy: Nested scopes that automatically inherit from parents
  • ⚑ Flexible Reactivity: Choose between automatic UI updates or manual control
  • πŸ”’ Strong Type Safety: Catch errors at compile-time with enhanced type constraints
  • πŸ”₯ ZenQuery System: React Query-inspired async state management with intelligent caching, automatic deduplication, background refetching, and scope-aware lifecycle management
  • ✨ Elegant Async Handling: Built-in effects system for loading, error, and success states
  • πŸ” Production-Safe Logging: Type-safe, environment-based configuration with granular log levels
  • πŸ§ͺ Testing Ready: Comprehensive testing utilities out of the box

What Makes Zenify Different?

Zenify builds on the shoulders of giants, taking inspiration from excellent libraries like GetX, Provider, and Riverpod. Our focus is on bringing hierarchical dependency injection and automatic lifecycle management to Flutter state management.

Zenify's unique strengths:

  • πŸ—οΈ Native hierarchical scopes - Dependencies flow naturally from parent to child
  • πŸ”„ Automatic cleanup - No manual disposal needed, prevents memory leaks
  • ✨ Built-in async effects - Loading/error states handled automatically
  • 🎯 Simplified API - One obvious way to do each task

πŸ”„ Familiar Patterns, Enhanced Features

If you're familiar with GetX, you'll feel right at home! Zenify draws inspiration from Jonny Borges' excellent work, preserving the reactive patterns, keeping the API very similar, while adding enhanced capabilities for complex applications.

We've also incorporated proven concepts from Riverpod's hierarchical scoping and Provider's context-based inheritance to create a comprehensive solution.

Quick Start (30 seconds)

1. Install

dependencies:
  zenify: ^1.1.2

2. Initialize

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  Zen.init();
  
  // Type-safe configuration (recommended) ✨
  if (kReleaseMode) {
    ZenConfig.applyEnvironment(ZenEnvironment.production);
  } else {
    ZenConfig.applyEnvironment(ZenEnvironment.development);
  }
  
  //OR
  
  // Fine-grained control
  ZenConfig.configure(
    level: ZenLogLevel.info,
    performanceTracking: true,
  );
  
  runApp(const MyApp());
}

3. Create Your First Controller

class CounterController extends ZenController {
  final count = 0.obs();
  
  void increment() => count.value++;
  void decrement() => count.value--;
}

4. (Optional) Register a Service

class LoggingService extends ZenService {
  @override
  void onInit() {/* setup sinks, files, etc. */}

  @override
  void onClose() {/* flush and close */}
}

// Permanent by default when using Zen.put
Zen.put<LoggingService>(LoggingService());

5. Use in Your Page

class CounterPage extends ZenView<CounterController> {
  @override
  CounterController createController() => CounterController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Counter')),
      body: Center(
        child: Column(
          children: [
            Obx(() => Text('Count: ${controller.count.value}')),
            ElevatedButton(
              onPressed: controller.increment,
              child: const Text('Increment'),
            ),
          ],
        ),
      ),
    );
  }
}

That's it! You have a fully reactive counter with automatic cleanup and type safety.

⚑ Performance Highlights

  • Minimal Rebuilds: Only affected widgets update, not entire subtrees
  • Memory Efficient: Automatic scope cleanup prevents leaks and dangling references
  • Zero Overhead: Built on Flutter's optimized ValueNotifier foundation
  • Smart Disposal: Intelligent lifecycle management with hierarchical cleanup
  • Production Tested: Real-world app migration validates performance at scale

See Performance Guide for detailed benchmarks

Production-Ready Reactive System

Beyond basic reactive state - Zenify includes a comprehensive reactive system designed for production applications:

RxFuture - Reactive Async Operations

class DataController extends ZenController {
  late final RxFuture<List<User>> usersFuture;

  @override
  void onInit() {
    super.onInit();
    usersFuture = RxFuture.fromFactory(() => userService.getUsers());
  }

  void refreshData() => usersFuture.refresh(); // Automatic loading states
}

// In UI - automatic state management
Obx(() {
  if (controller.usersFuture.isLoading) return CircularProgressIndicator();
  if (controller.usersFuture.hasError) return ErrorWidget(controller.usersFuture.errorMessage);
  if (controller.usersFuture.hasData) return UserList(users: controller.usersFuture.data!);
  return SizedBox.shrink();
})

RxComputed - Smart Dependency Tracking

class ShoppingController extends ZenController {
  final cartItems = <CartItem>[].obs();
  final taxRate = 0.08.obs();

  late final RxComputed<double> subtotal;
  late final RxComputed<double> tax;
  late final RxComputed<double> total;

  @override
  void onInit() {
    super.onInit();
    
    // These automatically update when dependencies change
    subtotal = computed(() => 
      cartItems.fold(0.0, (sum, item) => sum + (item.price * item.quantity))
    );
    tax = computed(() => subtotal.value * taxRate.value);
    total = computed(() => subtotal.value + tax.value);
  }

  void addItem(CartItem item) {
    cartItems.add(item); // All computed values automatically update!
  }
}

// In UI - automatic updates
Obx(() => Text('Subtotal: \$${controller.subtotal.value.toStringAsFixed(2)}'))
Obx(() => Text('Tax: \$${controller.tax.value.toStringAsFixed(2)}'))
Obx(() => Text('Total: \$${controller.total.value.toStringAsFixed(2)}'))

RxResult - Production Error Handling

class UserController extends ZenController {
  Future<void> saveUser(User user) async {
    final result = await RxResult.tryExecuteAsync(
      () => userService.saveUser(user),
      'save user'
    );
    
    result.onSuccess((savedUser) {
      users.add(savedUser);
      showSuccess('User saved successfully');
    });
    
    result.onFailure((error) {
      showError('Failed to save user: ${error.message}');
    });
  }

  // Safe list operations with error handling
  void updateUserSafely(int index, User newUser) {
    final result = users.trySetAt(index, newUser);
    if (result.isFailure) showError('Invalid index: $index');
  }
}

Advanced Reactive Patterns

class AdvancedController extends ZenController {
  final searchQuery = ''.obs();
  final products = <Product>[].obs();
  final isLoading = false.obs();

  @override
  void onInit() {
    super.onInit();

    // Debounced search with error handling
    searchQuery.debounce(Duration(milliseconds: 500), (query) async {
      if (query.isEmpty) return products.clear();

      isLoading.value = true;
      final result = await RxResult.tryExecuteAsync(
        () => productService.search(query)
      );
      
      result.onSuccess((results) => products.assignAll(results));
      result.onFailure((error) => showError('Search failed: ${error.message}'));
      
      isLoading.value = false;
    });
  }
}

Benefits:

  • ✨ Comprehensive error handling with graceful degradation
  • 🎯 Smart dependency tracking with automatic cleanup
  • πŸ”’ Type-safe async operations with built-in loading states
  • 🏭 Production-validated with real-world error scenarios

Handle Async Operations with Effects

class UserController extends ZenController {
  late final userEffect = createEffect<User>(name: 'user');
  
  Future<void> loadUser() async {
    await userEffect.run(() => api.getUser());
  }
}

// In your UI - automatic loading states
ZenEffectBuilder<User>(
  effect: controller.userEffect,
  onLoading: () => const CircularProgressIndicator(),
  onSuccess: (user) => UserProfile(user),
  onError: (error) => ErrorMessage(error),
)

Benefits:

  • ✨ Automatic state management - Loading, success, error handled for you
  • πŸ”„ Retry logic - Built-in error recovery and retry mechanisms
  • πŸ”’ Type safety - Full compile-time guarantees for async operations
  • πŸ§ͺ Testing friendly - Easy to mock and test different states

πŸ”₯ ZenQuery - Smart Async State Management

React Query-like functionality for Flutter with automatic caching, deduplication, and background refetching.

// Create query
final userQuery = ZenQuery<User>(
  queryKey: 'user:123',
  fetcher: () => api.getUser(123),
);

// Use in widget
ZenQueryBuilder<User>(
  query: userQuery,
  builder: (context, user) => Text(user.name),
  loading: () => CircularProgressIndicator(),
  error: (error, retry) => ErrorWidget(error: error, onRetry: retry),
);

Features:

  • βœ… Automatic caching - No more manual cache management
  • βœ… Smart deduplication - Multiple requests = single API call
  • βœ… Background refetch - Keep data fresh automatically
  • βœ… Pagination support - Built-in patterns for paginated data
  • βœ… Optimistic updates - Instant UI with error rollback
  • βœ… Retry logic - Exponential backoff built-in
  • βœ… SWR pattern - Show cached data while fetching fresh
  • βœ… Scoped lifecycle - Automatic cleanup with modules

Perfect for: REST APIs, GraphQL queries, pagination, infinite scroll, and real-time data feeds.

See ZenQuery Guide

Flexible Widget System

Choose the right widget for your needs:

ZenConsumer - Efficient Dependency Access

// Access services efficiently
ZenConsumer<CartService>(
  builder: (cartService) => cartService != null
    ? CartIcon(itemCount: cartService.itemCount)
    : const EmptyCartIcon(),
)

// Access optional dependencies gracefully
ZenConsumer<AuthService>(
  tag: 'premium',
  builder: (authService) => authService?.isAuthenticated.value == true
    ? const PremiumFeatures()
    : const UpgradePrompt(),
)

ZenBuilder - Performance Control

class PerformanceOptimizedView extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        // Only rebuilds when controller.update(['header']) is called
        ZenBuilder<DashboardController>(
          id: 'header',
          builder: (context, controller) => AppBar(
            title: Text(controller.title),
            actions: [
              IconButton(
                icon: Icon(controller.settingsIcon),
                onPressed: controller.openSettings,
              ),
            ],
          ),
        ),

        // Only rebuilds when controller.update(['content']) is called
        ZenBuilder<DashboardController>(
          id: 'content',
          builder: (context, controller) => Expanded(
            child: ListView.builder(
              itemCount: controller.items.length,
              itemBuilder: (context, index) => ItemWidget(
                item: controller.items[index]
              ),
            ),
          ),
        ),
      ],
    );
  }
}

Obx - Reactive Updates

class ReactiveWidget extends ZenView<CounterController> {
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        // Automatically rebuilds when counter.value changes
        Obx(() => Text('Count: ${controller.counter.value}')),

        // Multiple reactive values
        Obx(() => AnimatedContainer(
          duration: Duration(milliseconds: 300),
          color: controller.isActive.value ? Colors.green : Colors.red,
          child: Text('Status: ${controller.status.value}'),
        )),
      ],
    );
  }
}

Widget Comparison

Widget Purpose Rebuild Trigger Best For
ZenView Page base class Automatic lifecycle Full pages with controllers
ZenRoute Route navigation Route lifecycle Module/scope management
ZenConsumer Dependency access Manual Optional service access
ZenBuilder Manual updates controller.update() Performance optimization
Obx Reactive updates Reactive value changes Simple reactive widgets
ZenEffectBuilder Async operations Effect state changes Loading/Error/Success states
ZenControllerScope Custom lifecycle Manual scope control Explicit lifecycle management
ZenQueryBuilder Query operations Query state changes API calls with caching

Global Module Registration

Set up your entire app's dependency architecture at startup:

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  
  // Initialize Zenify
  Zen.init();
  ZenConfig.applyEnvironment(ZenEnvironment.development);
  
  // Register global modules
  await Zen.registerModules([
    // Core infrastructure
    CoreModule(),        // Database, storage, logging
    NetworkModule(),     // API clients, connectivity
    AuthModule(),        // Authentication, user management
  ]);
  
  runApp(const MyApp());
}

class CoreModule extends ZenModule {
  @override
  String get name => 'Core';

  @override
  void register(ZenScope scope) {
    // Global services available everywhere
    scope.put<DatabaseService>(DatabaseService(), isPermanent: true);
    scope.put<CacheService>(CacheService(), isPermanent: true);
    scope.put<LoggingService>(LoggingService(), isPermanent: true);
  }
}

// Access anywhere in your app
class AnyController extends ZenController {
  // These are automatically available from global registration
  final database = Zen.find<DatabaseService>();
  final cache = Zen.find<CacheService>();
  final logger = Zen.find<LoggingService>();
}

Benefits:

  • πŸ—‚οΈ Centralized setup - Configure your entire app architecture in one place
  • πŸ”₯ Hot reload friendly - Services persist across development iterations
  • πŸ§ͺ Testing support - Easy to swap modules for testing
  • πŸ“¦ Feature isolation - Keep related dependencies grouped together

Services (ZenService)

Long-lived app-wide services (e.g., auth, logging, cache) with safe lifecycle.

  • Lifecycle:
    • onInit() runs when the service first initializes
    • onClose() runs during disposal
    • isInitialized is true only after a successful onInit()
  • DI behavior:
    • Zen.put(instance): ZenService defaults to isPermanent = true and initializes via lifecycle manager
    • Zen.putLazy(factory): permanence is explicit; instance is created and initialized on first find(). Use alwaysNew: true for factory pattern (new instance each call)

Example:

class AuthService extends ZenService {
  late final StreamSubscription _tokenSub;

  @override
  void onInit() {
    _tokenSub = tokenStream.listen(_handleToken);
  }

  @override
  void onClose() {
    _tokenSub.cancel();
  }

  void _handleToken(String token) {/* ... */}
}

// Registration: permanent by default for services
Zen.put<AuthService>(AuthService());

// Lazy registration (make permanent explicitly if desired)
Zen.putLazy<AuthService>(() => AuthService(), isPermanent: true);

// Usage anywhere
final auth = Zen.find<AuthService>(); // auto-initializes if needed

Organize with Modules

Scale your app with clean dependency organization:

// Define module with controller
class UserModule extends ZenModule {
  @override
  String get name => 'User';

  @override
  void register(ZenScope scope) {
    scope.putLazy<UserService>(() => UserService());
    scope.putLazy<UserController>(() => UserController());
  }
}

// Use in routes with automatic cleanup
ZenRoute(
  moduleBuilder: () => UserModule(),
  page: UserProfilePage(), // Page extends ZenView<UserController>
  scopeName: 'UserScope'
)

// Page doesn't need createController() - gets it from module
class UserProfilePage extends ZenView<UserController> {
  // No createController override needed!
  // ZenView automatically finds UserController from the module
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('User Profile')),
      body: Obx(() => Text('User: ${controller.userName.value}')),
    );
  }
}

Benefits:

  • πŸ—‚οΈ Organized Dependencies - Group related services together
  • πŸ”„ Automatic Cleanup - Modules dispose when routes change
  • πŸ—οΈ Hierarchical Inheritance - Child modules access parent services
  • πŸ§ͺ Testing Friendly - Swap modules for testing

πŸ› οΈ Advanced Features

For complex applications:

  • πŸ—ΊοΈ Route-Based Scoping - Automatic module lifecycle with navigation using ZenRoute
  • πŸ—οΈ Hierarchical Dependency Injection - Parent-child scope inheritance with ZenScopeWidget
  • 🏷️ Tagged Dependencies - Multiple instances with smart resolution
  • πŸ“Š Performance Monitoring - Built-in metrics and leak detection
  • πŸ§ͺ Comprehensive Testing - Mocking, lifecycle, and memory leak tests
  • πŸ”„ Advanced Lifecycle Hooks - Module initialization and disposal callbacks

ZenRoute - Works with Any Router

ZenRoute works seamlessly with any Flutter routing solution - it's just a widget!

// βœ… Works with Flutter's built-in Navigator
class AppRoutes {
  static Route<dynamic> generateRoute(RouteSettings settings) {
    switch (settings.name) {
      case '/product':
        return MaterialPageRoute(
          builder: (_) => ZenRoute(
            moduleBuilder: () => ProductModule(),
            page: ProductDetailPage(),
            scopeName: 'ProductScope',
          ),
        );
    }
  }
}

// βœ… Works with GoRouter
final router = GoRouter(
  routes: [
    GoRoute(
      path: '/product/:id',
      builder: (context, state) => ZenRoute(
        moduleBuilder: () => ProductModule(),
        page: ProductDetailPage(id: state.pathParameters['id']!),
        scopeName: 'ProductScope',
      ),
    ),
  ],
);

// βœ… Works with AutoRoute
@AutoRouterConfig()
class AppRouter extends $AppRouter {
  @override
  List<AutoRoute> get routes => [
    AutoRoute(
      page: ProductRoute.page,
      path: '/product',
    ),
  ];
}

@RoutePage()
class ProductRoute extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ZenRoute(
      moduleBuilder: () => ProductModule(),
      page: ProductDetailPage(),
      scopeName: 'ProductScope',
    );
  }
}

// βœ… Works with any custom routing solution
Navigator.of(context).push(
  MaterialPageRoute(
    builder: (_) => ZenRoute(
      moduleBuilder: () => SettingsModule(),
      page: SettingsPage(),
      scopeName: 'SettingsScope',
    ),
  ),
);

Key Benefits:

  • 🎯 Router Agnostic - Works with any routing package or custom solution
  • πŸ”„ Automatic Cleanup - Modules and scopes dispose when route is popped
  • πŸ—οΈ Hierarchical - Child routes inherit from parent scopes
  • πŸ§ͺ Testable - Easy to test without full navigation stack

Pro Tip: Use ZenRoute for feature-level routes that need dependency isolation. For simple pages, ZenView alone is often sufficient!

ZenScopeWidget - Custom Scoping

// Create scopes at any widget level
showModalBottomSheet(
  context: context,
  builder: (context) => ZenScopeWidget(
    moduleBuilder: () => FilterModule(),
    scopeName: 'FilterScope',
    child: const FilterBottomSheet(),
  ),
);

πŸ“± Best Practices

🎯 Widget Selection

  1. ZenView: Use as base class for pages with controllers (⭐ recommended)
  2. ZenRoute: Use for route-based module and scope management (⭐ navigation)
  3. ZenConsumer: Use for accessing any dependency efficiently with null safety
  4. ZenBuilder: Use for manual update control with ZenControllers
  5. Obx: Use for reactive state with automatic rebuilds
  6. ZenEffectBuilder: Use for async operations with loading/error/success states
  7. ZenControllerScope: Use when you need explicit lifecycle control

πŸ—οΈ Module Organization

  1. Feature Modules: Create specific modules for each major feature/route
  2. Core Modules: Register shared services in global/parent modules
  3. Hierarchy Design: Keep scope depth reasonable (max 3-4 levels)
  4. Dependency Checking: Always verify required dependencies exist in parent scopes
  5. Lifecycle Hooks: Use onInit and onClose for resource management

⚑ Performance Optimization

  1. Use Effects: Leverage ZenEffect for async operations with built-in state management
  2. Selective Updates: Use ZenBuilder with specific IDs for fine-grained updates
  3. Lazy Loading: Use putLazy() for dependencies that aren't immediately needed
  4. Computed Values: Use computed properties for derived state instead of manual calculations
  5. Memory Management: Dispose controllers and effects properly in onClose

πŸ›‘οΈ Error Handling

  1. Use RxResult: For operations that can fail gracefully
  2. Try Methods*: Leverage try* methods for safe reactive operations
  3. Global Handlers: Configure global error handling for production
  4. Fallback Values: Provide graceful fallbacks for missing dependencies
  5. Logging: Log errors appropriately for debugging

πŸ§ͺ Testing Strategy

  1. Unit Tests: Test controllers in isolation using dependency injection
  2. Widget Tests: Use Zen.testMode() for component testing
  3. Integration Tests: Test module interactions and lifecycle
  4. Mock Dependencies: Replace services with mocks for testing
  5. Memory Tests: Verify proper cleanup and disposal

πŸ“ Code Organization

  1. Single Responsibility: Keep controllers focused on single responsibilities
  2. Feature-Based Structure: Organize by feature, not by type
  3. Hierarchical Scopes: Use scope hierarchy for logical dependency flow
  4. Clear Naming: Use descriptive names for scopes and modules
  5. Documentation: Document complex reactive logic and dependencies

🚫 Common Anti-Patterns to Avoid

  1. DON'T mix UI logic in controllers
  2. DON'T create circular dependencies between services
  3. DON'T forget to dispose resources in onClose
  4. DON'T use excessive scope nesting (>4 levels)
  5. DON'T ignore error handling in production code

πŸ“‹ Quick Checklist

Before releasing to production:

  • βœ… Controllers have single responsibilities
  • βœ… Async operations use effects or proper error handling
  • βœ… Resources are properly disposed in onClose()
  • βœ… Dependencies are organized in logical modules
  • βœ… Critical paths have comprehensive tests
  • βœ… Error states have user-friendly fallbacks
  • βœ… Performance-critical sections use targeted updates
  • βœ… Scope hierarchy is clean and purposeful

Explore Advanced Guides for production patterns and comprehensive examples

πŸ“š Complete Documentation

New to Zenify? Start with the guides that match your needs:

Core Guides

Examples & Learning

Quick References (Coming Soon)

  • Core Concepts - Controllers, reactive state, and UI widgets
  • API Reference - Complete method and class documentation
  • Migration Guide - Moving from other state management solutions
  • Testing Guide - Unit and widget testing with Zenify

πŸ”‘ Key Features at a Glance

ZenView - Direct Controller Access

class ProductPage extends ZenView<ProductController> {
  @override
  Widget build(BuildContext context) {
    // Direct access - no Zen.find() needed!
    return Text(controller.productName.value);
  }
}

Smart Effects System

// Automatic state management for async operations
late final dataEffect = createEffect<List<Item>>(name: 'data');

await dataEffect.run(() => api.fetchData());
// Loading, success, and error states handled automatically

Hierarchical Dependency Injection

// Parent scope provides shared services
ZenRoute(
  moduleBuilder: () => AppModule(),
  page: HomePage(),
  scopeName: 'AppScope',
)

// Child widgets automatically access parent dependencies
final authService = Zen.find<AuthService>(); // Available everywhere

Flexible Reactivity

// Option 1: Automatic reactive updates
Obx(() => Text('Count: ${controller.count.value}'))

// Option 2: Manual control for performance
ZenBuilder<Controller>(
  id: 'specific-section',
  builder: (context, controller) => ExpensiveWidget(controller.data),
)
// Only rebuilds when controller.update(['specific-section']) is called

πŸ™ Credits

Zenify draws inspiration from several excellent state management libraries:

  • GetX by Jonny Borges - For the intuitive reactive syntax and dependency injection approach
  • Provider by Remi Rousselet - For context-based dependency inheritance concepts
  • Riverpod by Remi Rousselet - For improved type safety and testability patterns

πŸ’¬ Community & Support

πŸ“„ License

Zenify is released under the MIT License.

πŸš€ Ready to Get Started?

Choose your path:

Questions? We're here to help!

Ready to bring zen to your Flutter development? Start exploring and experience the difference! ✨

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
core/zen_scope_manager
core/zen_scope_stack_tracker
debug/debug
debug/zen_debug
debug/zen_hierarchy_debug
debug/zen_system_stats
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/query
Query system for advanced async state management
query/zen_query
query/zen_query_builder
query/zen_query_cache
query/zen_query_config
query/zen_scope_query_extension
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/rx_widgets
widgets/widgets
widgets/zen_builder
widgets/zen_consumer
widgets/zen_effect_builder
widgets/zen_route
widgets/zen_scope_widget
widgets/zen_view
workers/workers
workers/zen_workers
zenify
Zenify - Modern Flutter state management