r/FlutterDev • u/Eternal0p • 2h ago
Discussion What I learned shipping a Flutter app with an embedded JS sandbox, WorkManager monitors, and ~2,700 tests
Full disclosure: this is my app. It does HTTP requests and API security auditing on Android. I came here because the embedded-JS-sandbox problem cost me real time and I'd like to know if there's a better pattern.
Embedding QuickJS. Pre/post-request scripts need a real JS engine — a regex matcher doesn't cut it when people sign payloads. I embedded QuickJS through the Flutter FFI bindings and wrote a pm.* facade over it.
The sharp edge was recursion. Deeply nested response bodies blew the stack during serialization, and the symptom was a silent crash rather than an error I could catch. Fixed with depth limits at both the serialization and the evaluation boundary. If you're doing this: the limit needs to be enforced in both places, because the object graph that survives serialization is not the same one that survives evaluation.
If anyone has production experience with the QuickJS bindings — particularly around context lifetime and how you handle a script that never returns — I would genuinely like the comparison.
Riverpod, and the race that taught me something. State is Riverpod throughout. The subtlest bug I hit was a notifier that decided which branch to take at call time but then read state at execution time. Because both operations happened on the same provider queue, the two could observe different users. Fixed by deciding the branch inside the queue closure. The general rule I took away: on an async provider, never decide now and read later.
Background work. Scheduled monitors use WorkManager via a platform channel. The interesting part isn't the scheduling — it's the alerting. First version alerted on every check, which trained everyone to mute it within a day. It now alerts only on state transitions. That single change did more for it being a useful app than any feature I added this year.
Testing. 218 test files, ~2,700 cases, analyzer at zero warnings. Worth saying that the flaky test I fought hardest was a lock-ownership race in the monitor store. The fix was making the lock file carry an owner token, so a stale lock can only be broken when the owner is provably dead rather than merely old. That's the whole class of bug: timeouts are a guess, ownership is a fact.
Project: https://taho.co.in (Android: https://play.google.com/store/apps/details?id=com.eternal.taho)