A designer once handed me an 18 MB JSON export we had to parse, normalize, and diff against our local copy before we could render a preview. On a mid-range Android phone that parse froze the UI for about 900ms. That's not a spinner pause. That's a "did the app just die?" pause, the kind where the user taps three more times and now you have a rage-tap in your logs.
The fix everyone reaches for is "throw it on an isolate," and that's usually correct. The problem is that almost every Dart isolate tutorial stops exactly where it gets interesting, right before message-passing costs bite you and you end up slower than when you started. This is the post I wish I'd had: not "isolates are like threads" hand-waving, but a working model of what an isolate is, what crossing between them actually costs, and how to decide whether spawning one helps or hurts. I've shipped this in production at Shpper and gotten it wrong enough times to have opinions worth the read.
The single-thread myth: what actually blocks a Flutter frame
Dart runs your code on a single thread of execution per isolate. That's the part people repeat. The part they skip: "single-threaded" doesn't mean "does one thing." The event loop juggles thousands of async operations happily, because await yields control back to the loop while I/O happens elsewhere (the OS, the network stack, the disk). Async is about waiting efficiently.






