🛣️ Routes / Modules
⏳ Loader System

🎯 Loader System

The Loader System provides automatic and manual loading indicators for better user experience during module loading and async operations.

🚀 Automatic Loader

The loader automatically appears during:

  • Module loading and initialization
  • Dependency injection
  • Route transitions
  • Async navigation

Default Behavior

class AppWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ModularApp.router(
      title: 'My App',
      theme: ThemeData(primarySwatch: Colors.blue),
      // Automatic loader is enabled by default
    );
  }
}

🔧 Manual Control

Show/Hide Loader

// Show loader
ModularLoader.show();
 
// Hide loader
ModularLoader.hide();
 
// Show with custom message
ModularLoader.show(message: 'Loading data...');
 
// Hide with delay
ModularLoader.hide(delay: Duration(milliseconds: 500));

Usage in Controllers

class UserController {
  Future<void> loadUserData() async {
    ModularLoader.show(message: 'Loading user data...');
    
    try {
      await userService.fetchUser();
    } finally {
      ModularLoader.hide();
    }
  }
}

🎨 Custom Loader

Create Custom Loader

class MyLoader extends CustomModularLoader {
  @override
  Color get backgroundColor => Colors.black87;
 
  @override
  Widget get child => Column(
    mainAxisSize: MainAxisSize.min,
    children: [
      CircularProgressIndicator(color: Colors.blue),
      SizedBox(height: 16),
      Text('Loading...', style: TextStyle(color: Colors.white)),
    ],
  );
}

Use Custom Loader

class AppWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ModularApp.router(
      customModularLoader: MyLoader(),
      title: 'My App',
    );
  }
}

Advanced Custom Loader

class AnimatedLoader extends CustomModularLoader {
  @override
  Color get backgroundColor => Colors.black.withOpacity(0.8);
 
  @override
  Widget get child => Container(
    padding: EdgeInsets.all(24),
    decoration: BoxDecoration(
      color: Colors.white,
      borderRadius: BorderRadius.circular(12),
      boxShadow: [
        BoxShadow(
          color: Colors.black26,
          blurRadius: 10,
          offset: Offset(0, 4),
        ),
      ],
    ),
    child: Column(
      mainAxisSize: MainAxisSize.min,
      children: [
        SizedBox(
          width: 40,
          height: 40,
          child: CircularProgressIndicator(
            strokeWidth: 3,
            valueColor: AlwaysStoppedAnimation<Color>(Colors.blue),
          ),
        ),
        SizedBox(height: 16),
        Text(
          'Loading...',
          style: TextStyle(
            fontSize: 16,
            fontWeight: FontWeight.w500,
            color: Colors.grey[800],
          ),
        ),
      ],
    ),
  );
}

🔄 Async Navigation

Basic Async Navigation

ElevatedButton(
  onPressed: () async {
    setState(() => isLoading = true);
    
    try {
      await context.goAsync('/heavy-page');
    } finally {
      setState(() => isLoading = false);
    }
  },
  child: Text('Navigate'),
);

Pre-loading Data

ElevatedButton(
  onPressed: () async {
    ModularLoader.show(message: 'Preparing data...');
    
    try {
      // Pre-load data before navigation
      await userService.loadUserData();
      await productService.loadProducts();
      
      context.go('/dashboard');
    } finally {
      ModularLoader.hide();
    }
  },
  child: Text('Load Dashboard'),
);

Conditional Loading

ElevatedButton(
  onPressed: () async {
    if (needsDataRefresh) {
      ModularLoader.show(message: 'Refreshing data...');
      await refreshData();
      ModularLoader.hide();
    }
    
    context.go('/next-page');
  },
  child: Text('Continue'),
);

🎯 Advanced Patterns

Loader with Progress

class ProgressLoader extends CustomModularLoader {
  final double progress;
  
  ProgressLoader(this.progress);
  
  @override
  Widget get child => Column(
    mainAxisSize: MainAxisSize.min,
    children: [
      CircularProgressIndicator(value: progress),
      SizedBox(height: 16),
      Text('${(progress * 100).toInt()}% Complete'),
    ],
  );
}
 
// Usage
ModularLoader.show(customLoader: ProgressLoader(0.5));

Loader with Actions

class ActionLoader extends CustomModularLoader {
  final VoidCallback? onCancel;
  
  ActionLoader({this.onCancel});
  
  @override
  Widget get child => Column(
    mainAxisSize: MainAxisSize.min,
    children: [
      CircularProgressIndicator(),
      SizedBox(height: 16),
      Text('Processing...'),
      if (onCancel != null) ...[
        SizedBox(height: 16),
        TextButton(
          onPressed: onCancel,
          child: Text('Cancel'),
        ),
      ],
    ],
  );
}
 
// Usage
ModularLoader.show(
  customLoader: ActionLoader(
    onCancel: () {
      ModularLoader.hide();
      // Cancel operation
    },
  ),
);

Module-Specific Loaders

class AuthModule extends Module {
  @override
  void initState(Injector i) {
    // Show custom loader for auth module
    ModularLoader.show(
      customLoader: AuthLoader(),
      message: 'Initializing authentication...',
    );
  }
  
  @override
  void dispose() {
    ModularLoader.hide();
  }
}
 
class AuthLoader extends CustomModularLoader {
  @override
  Widget get child => Column(
    children: [
      Icon(Icons.security, color: Colors.blue, size: 48),
      SizedBox(height: 16),
      Text('Authenticating...'),
    ],
  );
}

🛡️ Best Practices

1. Always Hide Loader

// ✅ Good - Always hide loader
try {
  ModularLoader.show();
  await heavyOperation();
} finally {
  ModularLoader.hide();
}
 
// ❌ Avoid - Might not hide on error
ModularLoader.show();
await heavyOperation();
ModularLoader.hide(); // Won't execute if error occurs

2. Use Descriptive Messages

// ✅ Good - Clear message
ModularLoader.show(message: 'Loading user profile...');
 
// ❌ Avoid - Generic message
ModularLoader.show(message: 'Loading...');

3. Consider User Experience

// ✅ Good - Show loader only for long operations
if (operationDuration > Duration(milliseconds: 500)) {
  ModularLoader.show();
}
 
// ❌ Avoid - Show loader for quick operations
ModularLoader.show(); // For operations < 100ms

📚 Related Topics