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?
Expo 50 app with expo-router, Tamagui, NativeWind, Zustand, and Reanimated 3
Full-stack React Native + Next.js travel app with Hono API, Prisma, MSW mock layer, and 68 commits in a week
Reward app with 5-network ad mediation