Modules

Modules are isolated, self-contained features with their own lifecycle, dependencies, and navigation.

Creating a Module

class UserModule extends Module {
  UserModule() : super(name: 'user');

  @override
  Widget build(BuildContext context) {
    return UserHomePage();
  }

  @override
  Future<void> onInit() async {
    // Setup services
    di.put<UserRepository>(UserRepositoryImpl());
    di.put<AuthService>(AuthServiceImpl());
    
    // Load configuration
    await loadUserPreferences();
  }

  @override
  Future<void> onDispose() async {
    // Cleanup
    await saveUserState();
    await closeConnections();
  }
}

Lifecycle States

Uninitialized → Initializing → Active → Suspended → Disposing → Disposed
                      ↓           ↓
                    Error ← ← ← ←

States:

  • Uninitialized - Created but not initialized
  • Initializing - onInit() running
  • Active - Ready and operational
  • Suspended - Temporarily inactive
  • Disposing - onDispose() running
  • Disposed - Permanently shut down
  • Error - Failed during lifecycle transition

Lifecycle Hooks

class MyModule extends Module {
  @override
  Future<void> onInit() async {
    // Called during initialization
    // Setup services, load configuration
  }

  @override
  void onActive(RouteTransitionContext ctx) {
    // Called when module becomes active
    // Refresh data, start timers
  }

  @override
  Future<void> onSuspend() async {
    // Called when suspended
    // Pause operations, save state
  }

  @override
  Future<void> onResume() async {
    // Called when resumed
    // Restart operations
  }

  @override
  Future<void> onDispose() async {
    // Called during cleanup
    // Release resources, save state
  }

  @override
  Future<void> onError(Object error, StackTrace trace) async {
    // Handle errors during lifecycle
    logger.error('Module error: $error');
  }

  @override
  Future<bool> onRecover() async {
    // Attempt recovery from error state
    return true; // Return false to prevent recovery
  }
}

Module Registration

// Automatic via CLI-generated init.dart
await mosaic.registry.initialize(appModule, [
  authModule,
  dashboardModule,
  settingsModule,
]);

Module Dependencies

class DashboardModule extends Module {
  DashboardModule() : super(name: 'dashboard');

  @override
  List<Module> get dependencies => [
    AuthModule(),      // Dashboard needs auth
    AnalyticsModule(), // Dashboard needs analytics
  ];
}

Dependencies are initialized in topological order automatically.

Module Operations

// Check state
if (module.state == ModuleLifecycleState.active) {
  // Module is ready
}

// Suspend module
await module.suspend();

// Resume module
await module.resume();

// Dispose module
await module.dispose();

// Recover from error
final recovered = await module.recover();

Internal Navigation

Each module has its own navigation stack:

// Push page
final result = await module.push<String>(EditPage());

// Pop with result
module.pop<String>('saved');

// Clear stack
module.clear();

// Stack info
final depth = module.stackDepth;
final canPop = module.canPop;

Error Handling

class ResilientModule extends Module {
  @override
  Future<void> onInit() async {
    try {
      await riskyInitialization();
    } catch (e) {
      // Handle initialization error
      logger.error('Init failed: $e');
      rethrow; // Module enters error state
    }
  }

  @override
  Future<bool> onRecover() async {
    // Try to recover
    try {
      await fallbackInitialization();
      return true; // Recovery successful
    } catch (e) {
      return false; // Recovery failed
    }
  }
}

Hot Reload (Development)

// Replace module with new implementation
await oldModule.hotReload(newModule);

Transfers state to new module without losing data.