HTTP Timeouts & Retry Logic in Flutter (Dio Retry Pattern)
Production-grade retry in Flutter: timeouts and exponential backoff — the Dio retry pattern on the http package. Which failures are safe to retry?
The happy-path HTTP tutorial ends where real apps begin. On a flaky mobile network, await http.get(url) with no timeout can hang forever — your loading spinner spins until the user force-quits the app. And a single transient 500 doesn't have to become an error screen if you retry it. This guide makes your networking production-grade: timeouts, retries with exponential backoff, and knowing exactly which failures are safe to retry. The pattern mirrors the popular Flutter Dio retry approach (interceptor-based, like dio_smart_retry ), implemented here on the official http package so you need zero extra dependencies. Related: Python requests: retries, timeouts and backoff — the same resilience patterns for Python HTTP clients. Why it matters Dart's http package sets no timeout by default . http.get() returns a Future that completes when the server answers — and if the server never answers (dead connection, captive portal, tunnel with no signal), that Future never completes. Your FutureBuilder stays on the spinner indefinitely. Worse, a raw retry loop without backoff turns one struggling server into a retry storm. You need three things: a deadline on every request, a retry policy that waits between attempts, and the discipline to retry only retryable failures. What you'll build A .timeout() on every request with a controlled fallback. A reusable ApiClient wrapper: timeout + retry with exponential backoff (1s, 2s, 4s). Clear rules for retryable vs non-retryable errors. A runnable demo that logs each attempt with a Stopwatch , so you can watch the backoff work. Cancellation of stale requests. Step 1 — Put a deadline on every request Dart futures have a built-in .timeout() . When the deadline passes, it throws TimeoutException — or runs your onTimeout fallback: import…