đź’ˇ Examples
🛍️ E-commerce

🛍️ E-commerce App Example

Exemplo completo de uma aplicação de e-commerce usando GoRouter Modular com arquitetura modular.

🏗️ Estrutura da Aplicação

e_commerce_app/
├── lib/
│   ├── main.dart
│   ├── app_module.dart
│   └── modules/
│       ├── auth/
│       │   ├── auth_module.dart
│       │   └── pages/
│       ├── products/
│       │   ├── product_module.dart
│       │   └── pages/
│       ├── cart/
│       │   ├── cart_module.dart
│       │   └── pages/
│       └── checkout/
│           ├── checkout_module.dart
│           └── pages/

🚀 Configuração Principal

// main.dart
import 'package:flutter/material.dart';
import 'package:go_router_modular/go_router_modular.dart';
import 'app_module.dart';
 
void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  
  await GoRouterModular.configure(
    appModule: AppModule(),
    initialRoute: '/home',
    debugLogEventBus: true,
  );
  
  runApp(ECommerceApp());
}
 
class ECommerceApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      title: 'E-commerce Modular',
      routerConfig: GoRouterModular.routerConfig,
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
    );
  }
}

📦 Módulo Principal

// app_module.dart
import 'package:go_router_modular/go_router_modular.dart';
import 'modules/auth/auth_module.dart';
import 'modules/products/product_module.dart';
import 'modules/cart/cart_module.dart';
import 'modules/checkout/checkout_module.dart';
 
class AppModule extends Module {
  @override
  List<ModularRoute> get routes => [
    // Rota inicial
    ChildRoute('/', child: (context, state) => SplashPage()),
    ChildRoute('/home', child: (context, state) => HomePage()),
    
    // MĂłdulos de funcionalidades
    ModuleRoute('/auth', module: AuthModule()),
    ModuleRoute('/products', module: ProductModule()),
    ModuleRoute('/cart', module: CartModule()),
    ModuleRoute('/checkout', module: CheckoutModule()),
  ];
 
  @override
  void binds(Injector i) {
    // Serviços globais
    i.addSingleton<ApiService>(() => ApiService());
    i.addSingleton<AuthService>(() => AuthService());
    i.addSingleton<CartService>(() => CartService());
    i.addSingleton<NavigationService>(() => NavigationService());
  }
}

đź›’ MĂłdulo de Produtos

// modules/products/product_module.dart
import 'package:go_router_modular/go_router_modular.dart';
 
class ProductModule extends EventModule {
  @override
  List<ModularRoute> get routes => [
    ChildRoute('/', child: (context, state) => ProductListPage()),
    ChildRoute('/:id', child: (context, state) => ProductDetailPage(
      productId: state.pathParameters['id']!,
    )),
    ChildRoute('/category/:category', child: (context, state) => CategoryPage(
      category: state.pathParameters['category']!,
    )),
  ];
 
  @override
  void binds(Injector i) {
    i.addSingleton<ProductRepository>(() => ProductRepository());
    i.addFactory<ProductBloc>(() => ProductBloc());
  }
 
  @override
  void listen() {
    // Escuta eventos de outros mĂłdulos
    on<UserLoginEvent>((event, context) {
      _loadUserRecommendations(event.userId);
    });
 
    on<AddToCartEvent>((event, context) {
      _updateProductMetrics(event.productId);
    });
  }
 
  void _loadUserRecommendations(String userId) {
    final productRepo = Modular.get<ProductRepository>();
    productRepo.loadRecommendations(userId);
  }
 
  void _updateProductMetrics(String productId) {
    // Atualizar métricas do produto
    ModularEvent.fire(ProductViewedEvent(
      productId: productId,
      timestamp: DateTime.now(),
    ));
  }
}

🛍️ Módulo do Carrinho

// modules/cart/cart_module.dart
import 'package:go_router_modular/go_router_modular.dart';
 
class CartModule extends EventModule {
  @override
  List<ModularRoute> get routes => [
    ChildRoute('/', child: (context, state) => CartPage()),
    ChildRoute('/summary', child: (context, state) => CartSummaryPage()),
  ];
 
  @override
  void binds(Injector i) {
    i.addFactory<CartBloc>(() => CartBloc());
  }
 
  @override
  void listen() {
    on<AddToCartEvent>((event, context) {
      _addItemToCart(event.productId, event.quantity);
    });
 
    on<RemoveFromCartEvent>((event, context) {
      _removeItemFromCart(event.itemId);
    });
 
    on<UserLogoutEvent>((event, context) {
      _clearCart();
    });
  }
 
  void _addItemToCart(String productId, int quantity) {
    final cartService = Modular.get<CartService>();
    cartService.addItem(productId, quantity);
    
    // Notificar outros mĂłdulos
    ModularEvent.fire(CartUpdatedEvent(
      itemCount: cartService.itemCount,
      total: cartService.total,
    ));
  }
 
  void _removeItemFromCart(String itemId) {
    final cartService = Modular.get<CartService>();
    cartService.removeItem(itemId);
    
    ModularEvent.fire(CartUpdatedEvent(
      itemCount: cartService.itemCount,
      total: cartService.total,
    ));
  }
 
  void _clearCart() {
    final cartService = Modular.get<CartService>();
    cartService.clear();
    
    ModularEvent.fire(CartClearedEvent());
  }
}

đź’ł MĂłdulo de Checkout

// modules/checkout/checkout_module.dart
import 'package:go_router_modular/go_router_modular.dart';
 
class CheckoutModule extends EventModule {
  @override
  List<ModularRoute> get routes => [
    ChildRoute('/', child: (context, state) => CheckoutPage()),
    ChildRoute('/payment', child: (context, state) => PaymentPage()),
    ChildRoute('/confirmation', child: (context, state) => OrderConfirmationPage()),
  ];
 
  @override
  void binds(Injector i) {
    i.addSingleton<PaymentService>(() => PaymentService());
    i.addSingleton<OrderService>(() => OrderService());
    i.addFactory<CheckoutBloc>(() => CheckoutBloc());
  }
 
  @override
  void listen() {
    on<ProcessPaymentEvent>((event, context) {
      _processPayment(event.paymentData);
    });
 
    on<OrderCreatedEvent>((event, context) {
      _handleOrderCreated(event.orderId);
    });
  }
 
  Future<void> _processPayment(PaymentData paymentData) async {
    try {
      final paymentService = Modular.get<PaymentService>();
      final result = await paymentService.processPayment(paymentData);
      
      if (result.success) {
        ModularEvent.fire(PaymentSuccessEvent(
          transactionId: result.transactionId,
          amount: paymentData.amount,
        ));
        
        // Criar pedido
        await _createOrder(paymentData);
      } else {
        ModularEvent.fire(PaymentFailedEvent(
          error: result.error,
          amount: paymentData.amount,
        ));
      }
    } catch (error) {
      ModularEvent.fire(PaymentErrorEvent(
        error: error.toString(),
      ));
    }
  }
 
  Future<void> _createOrder(PaymentData paymentData) async {
    final orderService = Modular.get<OrderService>();
    final cartService = Modular.get<CartService>();
    
    final order = await orderService.createOrder(
      items: cartService.items,
      paymentData: paymentData,
    );
    
    ModularEvent.fire(OrderCreatedEvent(
      orderId: order.id,
      total: order.total,
    ));
    
    // Limpar carrinho apĂłs pedido criado
    cartService.clear();
  }
 
  void _handleOrderCreated(String orderId) {
    // Navegar para confirmação
    Modular.to.pushNamed('/checkout/confirmation', arguments: orderId);
    
    // Notificar outros mĂłdulos
    ModularEvent.fire(CartClearedEvent());
  }
}

🎭 Eventos da Aplicação

// events/app_events.dart
 
// Eventos de Autenticação
class UserLoginEvent {
  final String userId;
  final String email;
  final DateTime loginTime;
  
  UserLoginEvent({
    required this.userId,
    required this.email,
    required this.loginTime,
  });
}
 
class UserLogoutEvent {
  final String userId;
  final DateTime logoutTime;
  
  UserLogoutEvent({
    required this.userId,
    required this.logoutTime,
  });
}
 
// Eventos de Carrinho
class AddToCartEvent {
  final String productId;
  final int quantity;
  
  AddToCartEvent({
    required this.productId,
    required this.quantity,
  });
}
 
class RemoveFromCartEvent {
  final String itemId;
  
  RemoveFromCartEvent({required this.itemId});
}
 
class CartUpdatedEvent {
  final int itemCount;
  final double total;
  
  CartUpdatedEvent({
    required this.itemCount,
    required this.total,
  });
}
 
class CartClearedEvent {}
 
// Eventos de Produtos
class ProductViewedEvent {
  final String productId;
  final DateTime timestamp;
  
  ProductViewedEvent({
    required this.productId,
    required this.timestamp,
  });
}
 
// Eventos de Pagamento
class ProcessPaymentEvent {
  final PaymentData paymentData;
  
  ProcessPaymentEvent({required this.paymentData});
}
 
class PaymentSuccessEvent {
  final String transactionId;
  final double amount;
  
  PaymentSuccessEvent({
    required this.transactionId,
    required this.amount,
  });
}
 
class PaymentFailedEvent {
  final String error;
  final double amount;
  
  PaymentFailedEvent({
    required this.error,
    required this.amount,
  });
}
 
class PaymentErrorEvent {
  final String error;
  
  PaymentErrorEvent({required this.error});
}
 
// Eventos de Pedidos
class OrderCreatedEvent {
  final String orderId;
  final double total;
  
  OrderCreatedEvent({
    required this.orderId,
    required this.total,
  });
}

🏠 Home Page com Navegação

// pages/home_page.dart
import 'package:flutter/material.dart';
import 'package:go_router_modular/go_router_modular.dart';
 
class HomePage extends StatefulWidget {
  @override
  State<HomePage> createState() => _HomePageState();
}
 
class _HomePageState extends State<HomePage> {
  int _cartItemCount = 0;
 
  @override
  void initState() {
    super.initState();
    _listenToCartUpdates();
  }
 
  void _listenToCartUpdates() {
    // Escutar atualizações do carrinho via EventModule
    final homeModule = Modular.get<HomeModule>();
    homeModule.on<CartUpdatedEvent>((event, context) {
      setState(() {
        _cartItemCount = event.itemCount;
      });
    });
  }
 
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('E-commerce App'),
        actions: [
          IconButton(
            icon: Stack(
              children: [
                Icon(Icons.shopping_cart),
                if (_cartItemCount > 0)
                  Positioned(
                    right: 0,
                    top: 0,
                    child: Container(
                      padding: EdgeInsets.all(2),
                      decoration: BoxDecoration(
                        color: Colors.red,
                        borderRadius: BorderRadius.circular(10),
                      ),
                      constraints: BoxConstraints(
                        minWidth: 16,
                        minHeight: 16,
                      ),
                      child: Text(
                        '$_cartItemCount',
                        style: TextStyle(
                          color: Colors.white,
                          fontSize: 12,
                        ),
                        textAlign: TextAlign.center,
                      ),
                    ),
                  ),
              ],
            ),
            onPressed: () => context.go('/cart'),
          ),
        ],
      ),
      body: GridView.builder(
        padding: EdgeInsets.all(16),
        gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
          crossAxisCount: 2,
          crossAxisSpacing: 16,
          mainAxisSpacing: 16,
          childAspectRatio: 0.8,
        ),
        itemCount: categories.length,
        itemBuilder: (context, index) {
          final category = categories[index];
          return CategoryCard(
            category: category,
            onTap: () => context.go('/products/category/${category.id}'),
          );
        },
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => context.go('/products'),
        child: Icon(Icons.search),
        tooltip: 'Ver todos os produtos',
      ),
    );
  }
}
 
class CategoryCard extends StatelessWidget {
  final Category category;
  final VoidCallback onTap;
  
  const CategoryCard({
    required this.category,
    required this.onTap,
    Key? key,
  }) : super(key: key);
 
  @override
  Widget build(BuildContext context) {
    return Card(
      elevation: 4,
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(12),
      ),
      child: InkWell(
        onTap: onTap,
        borderRadius: BorderRadius.circular(12),
        child: Column(
          children: [
            Expanded(
              child: Container(
                decoration: BoxDecoration(
                  borderRadius: BorderRadius.vertical(top: Radius.circular(12)),
                  image: DecorationImage(
                    image: NetworkImage(category.imageUrl),
                    fit: BoxFit.cover,
                  ),
                ),
              ),
            ),
            Padding(
              padding: EdgeInsets.all(8),
              child: Text(
                category.name,
                style: Theme.of(context).textTheme.titleMedium,
                textAlign: TextAlign.center,
              ),
            ),
          ],
        ),
      ),
    );
  }
}

🔄 Fluxo de Navegação

  1. Splash Screen → Home Page
  2. Home Page → Product Categories → Product List → Product Detail
  3. Product Detail → Add to Cart → Cart Page
  4. Cart Page → Checkout → Payment → Order Confirmation

âś… BenefĂ­cios desta Arquitetura

  • âś… Modularidade: Cada funcionalidade em seu prĂłprio mĂłdulo
  • âś… Comunicação: EventModule permite comunicação entre mĂłdulos
  • âś… Escalabilidade: Fácil adicionar novas funcionalidades
  • âś… Testabilidade: Cada mĂłdulo pode ser testado independentemente
  • âś… Manutenibilidade: CĂłdigo organizado e bem estruturado
  • âś… Performance: Lazy loading de mĂłdulos conforme necessário

đź§Ş Testes

// test/modules/cart/cart_module_test.dart
import 'package:flutter_test/flutter_test.dart';
import 'package:go_router_modular/go_router_modular.dart';
 
void main() {
  group('CartModule Tests', () {
    late CartModule cartModule;
 
    setUp(() {
      cartModule = CartModule();
      // Setup de dependĂŞncias para teste
    });
 
    test('should add item to cart when AddToCartEvent is fired', () {
      // Arrange
      final event = AddToCartEvent(productId: 'product123', quantity: 2);
      
      // Act
      ModularEvent.fire(event);
      
      // Assert
      // Verificar se o item foi adicionado ao carrinho
    });
 
    test('should update cart count when item is added', () {
      // Teste de atualização do contador
    });
  });
}

Este exemplo demonstra uma aplicação completa de e-commerce usando GoRouter Modular, mostrando como organizar código em módulos, comunicação via eventos e navegação type-safe.