Skip to main content

flutter_microfrontend 1.0.0 copy "flutter_microfrontend: ^1.0.0" to clipboard
flutter_microfrontend: ^1.0.0 copied to clipboard

A powerful, production-ready Flutter package for building large-scale applications using micro-frontend architecture. Enables true module independence with lazy loading, dynamic module registration, s [...]

flutter_microfrontend #

pub version License: MIT Dart SDK Flutter

Build large-scale Flutter apps as independent, lazy-loadable feature modules.

flutter_microfrontend brings micro-frontend architecture principles to Flutter β€” enabling teams to split apps into fully isolated feature units that can be loaded, unloaded, and developed independently.


✨ Feature Highlights #

Feature Description
πŸ”’ True Module Isolation Each module has its own DI container, state scope, and event subscriptions
⚑ Lazy Loading 6 preload strategies β€” load modules only when needed
πŸ“‘ Typed Event Bus RxDart-powered pub/sub with type safety, debounce, throttle
πŸ”Œ Plugin Architecture Analytics, Crash Reporting, Feature Flags β€” all decoupled
πŸ—ΊοΈ Modular Routing Auto-collects routes from all modules via GoRouter
πŸ§ͺ Highly Testable Every module is independently testable with mock helpers
πŸ” Dynamic Registration Register/unregister modules at runtime
πŸ›‘οΈ Error Isolation ModuleErrorBoundary prevents cascade failures
πŸ“Š Lifecycle Events Observable stream of every module state change
πŸ” Dep Graph Analysis Visualize dependencies, detect cycles at startup

πŸ“¦ Installation #

dependencies:
  flutter_microfrontend: ^1.0.0
flutter pub get

πŸš€ Quick Start #

1. Create a Module #

import 'package:flutter_microfrontend/flutter_microfrontend.dart';
import 'package:go_router/go_router.dart';

class AuthModule extends MicroModule with RoutableModule, EventAwareModule {
  @override String get moduleId => 'auth';
  @override String get moduleName => 'Authentication';
  @override bool get isEager => true;
  @override int get loadPriority => 100;
  @override List<Type> get publishedEvents => [UserSignedInEvent];

  @override
  Future<void> onRegister(ModuleInjector injector) async {
    injector.registerLazySingleton<AuthService>(() => FirebaseAuthService());
  }

  @override
  List<RouteBase> get routes => [
    GoRoute(
      path: '/login',
      builder: (ctx, state) => LoginScreen(
        onSuccess: (userId) => emit(UserSignedInEvent(
          userId: userId, sourceModuleId: moduleId,
        )),
      ),
    ),
  ];
}

2. Bootstrap the App #

void main() {
  runApp(
    MicrofrontendApp(
      title: 'My App',
      modules: [
        // Eager β€” critical modules loaded at startup
        AuthModule(),
        HomeModule(),

        // Lazy β€” loaded on demand
        DeferredModule(
          moduleId: 'shop',
          loader: () async => ShopModule(),
          preloadStrategy: PreloadStrategy.afterAppReady,
          dependencies: ['auth'],
        ),
        DeferredModule(
          moduleId: 'settings',
          loader: () async => SettingsModule(),
          preloadStrategy: PreloadStrategy.whenIdle,
        ),
      ],
      plugins: [
        FirebaseAnalyticsPlugin(),
        SentryPlugin(dsn: 'https://...'),
      ],
      sharedServices: (injector) {
        injector.registerSingleton<HttpClient>(DioClient());
        injector.registerSingleton<LocalStorage>(HiveStorage());
      },
    ),
  );
}

🧩 Module Contracts (Mixins) #

Mixin Purpose
RoutableModule Declare GoRouter routes
EventAwareModule Publish and subscribe to typed events
ServiceModule Expose services to the global DI container
class ShopModule extends MicroModule
    with RoutableModule, EventAwareModule, ServiceModule {
  @override String get moduleId => 'shop';
  // ... implements all three contracts
}

πŸ“‘ Event System #

// Publisher (in ShopModule)
emit(OrderPlacedEvent(orderId: 'ORD-123', sourceModuleId: moduleId));

// Subscriber (in any other module, no coupling)
on<OrderPlacedEvent>().listen((event) {
  showNotification('Order ${event.orderId} placed!');
});

// Auto-cancelled listener (tied to module lifecycle)
listen<UserSignedOutEvent>((event) => clearCart());

// Advanced: filter + debounce
eventBus.subscribe<SearchQueryEvent>(
  onEvent: runSearch,
  where: (e) => e.query.length > 2,
  debounce: const Duration(milliseconds: 300),
);

⚑ Lazy Loading #

// Builder pattern
final analyticsModule = LazyModuleBuilder('analytics')
  .withName('Analytics Dashboard')
  .withLoader(() async => AnalyticsDashboardModule())
  .withDependencies(['auth'])
  .withStrategy(PreloadStrategy.whenIdle)
  .withConfig(PreloadConfig(maxRetries: 3))
  .build();

// In UI: show loading state automatically
LazyModuleWidget(
  module: shopDeferredModule,
  builder: (ctx, module) => ShopHomePage(),
  loading: const ShopSkeletonScreen(),
  error: (err, retry) => ShopErrorPage(onRetry: retry),
)

Preload Strategies #

Strategy When
onDemand Only when explicitly called
onFirstNavigation When user navigates to the module
afterAppReady Shortly after all eager modules initialize
afterDelay After a configurable delay
whenIdle When the device is idle (3s after startup)
onWifiConnection When Wi-Fi is available

πŸ”Œ Plugin System #

class FirebaseAnalyticsPlugin extends AnalyticsPlugin {
  @override String get pluginId => 'firebase_analytics';
  @override bool get initializeBeforeModules => true;

  @override
  Future<void> initialize(GlobalInjector injector, ModuleEventBus bus) async {
    await Firebase.initializeApp();
    injector.registerSingleton<FirebaseAnalytics>(FirebaseAnalytics.instance);
  }

  @override
  Future<void> trackScreen(String name, {String? moduleId, ...}) async {
    await FirebaseAnalytics.instance.setCurrentScreen(screenName: name);
  }
}

πŸ’‰ Dependency Injection #

// In onRegister β€” module-local services
injector.registerLazySingleton<CartRepository>(
  () => CartRepositoryImpl(http: injector.get<HttpClient>()),
);

// Expose a service globally (other modules can use it)
injector.exposeGlobally<AuthService>(() => FirebaseAuthService());

// In widgets β€” access from widget tree
final cartService = ScopedLocator.of(context).get<CartRepository>();
// or with extension:
final cartService = context.read<CartRepository>();

πŸ§ͺ Testing #

// Use MockModule for isolated tests
final mockAuth = MockModule(
  id: 'auth',
  moduleName: 'Auth',
  onInitCallback: () => print('Auth init!'),
);

// Use MockEventBus to verify events
final bus = MockEventBus();
bus.emit(UserSignedInEvent(userId: '1', ...));
expect(bus.eventsOfType<UserSignedInEvent>().length, equals(1));

πŸ—οΈ Architecture Overview #

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                   MicrofrontendApp                    β”‚
β”‚  (Bootstrap: registry + event bus + plugins + router) β”‚
β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜
      β”‚                      β”‚                    β”‚
 PluginRegistry        ModuleRegistry       ModuleRouter
  (Analytics,          (Lifecycle +          (All Routes
   Sentry, Flags)       DI + Events)          from all
                                              Modules)
      β”‚
      β”œβ”€β”€ GlobalInjector (shared services)
      β”‚
      β”œβ”€β”€ AuthModule ──── ModuleInjector (scoped)
      β”‚                   EventAwareModule (publishes/subscribes)
      β”‚
      β”œβ”€β”€ HomeModule ──── ModuleInjector (scoped)
      β”‚                   Subscribes to AuthModule events
      β”‚
      └── DeferredModule (ShopModule) ── Not loaded until needed
          DeferredModule (SettingsModule) ── Loads when idle

πŸ“ Package Structure #

lib/
β”œβ”€β”€ flutter_microfrontend.dart  ← Single barrel export
└── src/
    β”œβ”€β”€ core/                   ← Registry, Loader, Lifecycle, App
    β”œβ”€β”€ contracts/              ← MicroModule + all mixins
    β”œβ”€β”€ navigation/             ← ModuleRouter, RouteRegistration
    β”œβ”€β”€ events/                 ← EventBus, ModuleEvent, EventChannel
    β”œβ”€β”€ injection/              ← ModuleInjector, GlobalInjector, ScopedLocator
    β”œβ”€β”€ lazy/                   ← DeferredModule, LazyModuleBuilder, PreloadStrategy
    β”œβ”€β”€ plugins/                ← MicroPlugin, PluginRegistry, specialized contracts
    β”œβ”€β”€ state/                  ← ModuleStateScope, SharedStateBridge
    β”œβ”€β”€ widgets/                ← ModuleBoundary, LazyModuleWidget, ErrorBoundary
    └── utils/                  ← Logger, Validator, DependencyGraph

πŸ“ Design Principles #

  1. Isolation First β€” Modules cannot directly import each other
  2. Lazy by Default β€” Load only what's needed, when it's needed
  3. Explicit Contracts β€” Every module declares what it provides and needs
  4. Pluggable β€” Add/remove plugins without touching modules
  5. Observable β€” Every lifecycle transition is emitted to a stream

πŸ”— Dependencies #

Package Purpose
rxdart Reactive Event Bus (PublishSubject, BehaviorSubject, operators)
get_it Scoped DI containers
go_router Modular routing
equatable Value equality for events
logging Structured logging
meta Annotations (@mustCallSuper, etc.)

πŸ“„ License #

MIT β€” see LICENSE

0
likes
130
points
27
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

A powerful, production-ready Flutter package for building large-scale applications using micro-frontend architecture. Enables true module independence with lazy loading, dynamic module registration, scoped dependency injection, a typed event bus, and a robust plugin system.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

async, collection, equatable, flutter, get_it, go_router, logging, meta, rxdart

More

Packages that depend on flutter_microfrontend