MobX + Clean Architecture + Firebase — Reactive State Done Right
MobX + Clean + Firebase: reactive observables on Clean layers with Firebase Auth. Less common than Riverpod — still a valid FlutterInit stack.
Stack Configuration — what FlutterInit generates for this guide
When to choose this stack
Choose this stack when you like MobX's observable/action model (or you're coming from React/web MobX), want Clean Architecture's feature folders, and Firebase as the backend. Be honest with your team: MobX is less common in Flutter than Riverpod or Bloc, so hiring and Stack Overflow answers are thinner — but the pattern works, and FlutterInit generates the codegen setup for you.
MobX + Clean Architecture + Firebase gives you reactive @observable state behind Clean data / domain / presentation layers, with Firebase Auth email-password wired in. It's a valid Flutter stack — just less common than Riverpod or Bloc. FlutterInit generates the stores, build_runner config, and Firebase Auth service so you're not hand-rolling codegen.
What This Stack Generates
Select MobX + Clean Architecture + Firebase and you get:
- Clean feature folders —
lib/src/features/auth/{data,domain,presentation}/ - MobX stores —
AuthStore/SessionStorewith@observable+@action, plusmobx,flutter_mobx, andbuild_runner - Firebase —
AuthServiceoversignInWithEmailAndPassword, plus Google Services setup docs - go_router — session-aware redirects
Honest caveat up front: most Flutter job posts and tutorials assume Riverpod or Bloc. Pick MobX because you want the reactive model, not because it's the default community pick.
Project Structure
On Clean / Feature-First, MobX stores land under presentation/providers/ (auth_store.dart, session_store.dart). On MVVM, they land under lib/src/ui/auth/stores/ instead.
Observables, Actions, Reactions
MobX's contract is simple:
@observable— state the UI can react to (isLoading, user, form fields)@action— the only place you mutate observables (login, logout, toggle loading)- Reactions —
Observerwidgets (viaflutter_mobx) rebuild when tracked observables change
You don't manually call notifyListeners(). Codegen wires the tracking.
part 'auth_store.g.dart';
class AuthStore = AuthStoreBase with _$AuthStore;
abstract class AuthStoreBase with Store {
final AuthRepository _repository;
AuthStoreBase({required AuthRepository repository}) : _repository = repository;
@observable
bool isLoading = false;
@action
void login({
required BuildContext context,
required String email,
required String password,
}) async {
isLoading = true;
final result = await _repository.login(email: email, password: password);
isLoading = false;
result.fold(
(failure) {
if (context.mounted) {
showToast(context, message: failure.message, status: 'error');
}
},
(_) {
if (context.mounted) context.go(AppRoutes.home);
},
);
}
}
Screens wrap reactive bits in Observer:
Observer(
builder: (_) => ElevatedButton(
onPressed: store.isLoading ? null : () => store.login(...),
child: store.isLoading
? const CircularProgressIndicator()
: const Text('Sign in'),
),
)
Codegen — build_runner Is Required
MobX stores need generated mixins (_$AuthStore). FlutterInit adds mobx, flutter_mobx, mobx_codegen, and build_runner to pubspec.yaml. After you edit a store:
dart run build_runner build --delete-conflicting-outputs
Skip this step and you'll get missing part / _$AuthStore errors. Re-run after every store change. Same story as Hive adapters or AutoRoute — this stack is codegen-heavy by design.
Domain and Data Still Stay Clean
The store depends on AuthRepository — not Firebase. Domain stays SDK-free:
abstract class AuthRepository {
Stream<AppUser?> get onAuthStateChanged;
FutureEither<AppUser> login({
required String email,
required String password,
});
FutureEither<void> logout();
}
Data implements it via AuthService → FirebaseAuth.signInWithEmailAndPassword. Drop in google-services.json / GoogleService-Info.plist per SETUP.md. That's the Firebase edge; everything above it stays testable with a fake repository.
When to Choose This vs Alternatives
Pick MobX + Clean + Firebase when:
- Your team already thinks in observables/actions (web MobX, or prior Flutter MobX apps)
- You want Clean feature folders but prefer MobX ergonomics over Riverpod providers
- You're fine running
build_runneras part of the daily loop
Skip it when:
- You want the most hireable Flutter stack — prefer Riverpod + Clean + Firebase
- You want zero codegen for state — Provider or Riverpod without code generation is lighter
- You're new to Clean Architecture itself — start with the Clean Architecture generated guide, then layer MobX once the folders make sense
MobX isn't wrong in Flutter. It's just quieter. Generate it when the reactive model is a deliberate choice, not a default.
Ready to Generate?
Open the dashboard, select Clean + MobX + Firebase + go_router, run build_runner once after unzip, and you've got reactive stores sitting on Clean layers with Firebase Auth ready for email-password.
Ready to build?
Generate this project in seconds
FlutterInit scaffolds the entire structure described in this guide — wired up, typed, and ready for flutter run.