Early in the TexOps project, we had a genuine internal debate about whether to build native or cross-platform. The client needed the app running on both iPad (for supervisors) and Samsung Galaxy Tabs (for floor workers). Going native meant two separate codebases, two separate teams, and two separate QA cycles. That wasn't feasible given the timeline.
We chose Flutter. This post is about why that was the right call for TexOps specifically, and where we paid for it.
TexOps had a lot of UI surface area. Gate pass forms, bale tracking screens, real-time admin dashboards pulling live Firebase data, role-based views that showed different information depending on whether you were a Lab Engineer or a floor supervisor. Building all of that twice in Swift and Kotlin would have consumed most of the project budget before we got to any of the interesting backend work.
Flutter's hot reload genuinely saved us during the UI iteration phase. The client's floor managers had strong opinions about the gate pass form layout, and we went through about eight revisions in two days of on-site visits. With a shared codebase, every layout change showed up on both the iPad and Galaxy Tab simultaneously. That feedback loop mattered.
Firebase worked well here too. Firestore's real-time listeners map naturally to Flutter's widget rebuild model. When a bale status update came in from the warehouse floor, the admin dashboard reflected it within about 2 seconds without any polling.
On-device fabric defect detection was the hardest part of the project, and Flutter made it harder than native would have.
We ran a YOLOv8 model converted to TFLite. On iOS, CoreML provides hardware acceleration that TFLite can sometimes access, but the integration path through Flutter is not direct. You are essentially writing a native wrapper, exposing it via Dart FFI or a platform channel, and maintaining that bridge. On native Swift, you get CoreML without any bridging layer.
The app size also jumped. Bundling the TFLite model files added 45MB to both the .ipa and .apk. We could not do on-demand download because the target facilities have unreliable connectivity. The larger initial install was a deliberate tradeoff, not an oversight.
The gate pass and bale tracking features used QR code scanning. Flutter's mobile_scanner package handled this fine. Each bale gets a QR code printed on a label; scanning it logs the bale's movement through departments and updates Firestore in real time.
This is where Firebase's offline persistence paid off. When a floor worker's tablet lost connectivity (which happened regularly), Firestore cached the writes locally and synced them automatically when the connection came back. No data loss, no error screens, no special offline handling code on our end.
// Firestore write with offline persistence enabled automatically
await FirebaseFirestore.instance
.collection('bales')
.doc(baleId)
.update({
'status': 'dispatched',
'gatePassTimestamp': FieldValue.serverTimestamp(),
'dispatchedBy': currentUser.uid,
});Firestore's offline write queue handled the rest. We did not write a single line of offline sync logic.
The Dart FFI bridge to the TFLite inference engine took longer to get right than we expected. If the defect detection feature had been the primary requirement rather than one feature among many, we would have built that specific piece natively and wrapped it in a platform view. The ML performance on native CoreML is noticeably better.
But for a project where the UI complexity is high, the team is cross-platform, and offline reliability matters more than peak ML performance, Flutter was the right call. The alternative was a native iOS team and a native Android team working in parallel, which would have tripled coordination overhead for this kind of deadline.
If you are scoping a similar project and deciding on the stack, the honest question to ask is: how much of the complexity is in the UI, and how much is in platform-specific hardware features? The higher the UI-to-hardware ratio, the stronger the Flutter case. For TexOps, that ratio was firmly in Flutter's favor.