Offline-first Flutter app with GetX state management, Appwrite backend, and Lottie animations

I had been writing Flutter since the Prachyam mobile client — a production codebase with real users, real crashes, and real performance constraints. The question I wanted to answer with this project was narrower: how does Flutter's lightweight state management story (GetX) hold up for a fresh build, compared to the more structured BLoC pattern I had used in production? And how well does Appwrite work as a backend-as-a-service alternative to building a custom API?
Nine days, 19 commits, 2,799 Dart lines. Not a production ship — a proper evaluation under real conditions.
GetX combines reactive state management, dependency injection, and named routing in one package. BLoC separates those concerns deliberately: events go in, states come out, the stream is explicit. GetX is more direct: declare observable variables, react to them in the UI, inject dependencies via Get.put(). Less boilerplate, less ceremony, faster initial velocity.
The tradeoff is testability. BLoC's explicit event/state contracts make unit testing predictable — you know exactly what goes in and what should come out. GetX's reactive layer is more magic, which means tests have more to account for. For a nine-day evaluation on a non-production project, GetX's velocity advantage was worth it. For a production mobile codebase with a team, BLoC's clarity would win.
GetStorage gave the app offline-first behaviour by persisting key data locally before syncing with Appwrite. The pattern is standard: write to local storage immediately, queue the Appwrite sync, resolve conflicts on reconnect. For the use case, it worked without incident.
Appwrite provides authentication, database, and storage without a custom backend. For a solo project evaluated over nine days, the time saved by not writing an API is significant. The Flutter SDK is first-class — auth flows, collection queries, and file storage are all covered with clean abstractions.
The honest constraint: Appwrite is a managed service. It handles the infrastructure but you cannot control the data contract at the schema level the way you can with a custom Node.js API or a Go service. The query layer is capable for CRUD operations and good enough for most mobile app patterns.
The React Native counterpart — built immediately after — used a different stack and produced different conclusions. The Flutter vs React Native comparison at the end of both builds is the main artifact of the Madhur pair.
Did this resonate?