Introduction
Efficient network usage is essential in Flutter mobile development. Fetching identical resources repeatedly wastes bandwidth and slows your app. ETags and conditional requests let the client ask the server whether a resource changed since the last fetch. If unchanged, the server responds with 304 Not Modified and no body, saving data and CPU. This tutorial explains how ETags work and demonstrates pragmatic caching in Flutter using conditional requests with the http package.
How ETags Work
An ETag (entity tag) is an opaque identifier the server attaches to a response (in the ETag header). It typically represents a resource version (hash, timestamp, or revision id). Workflow:
Client requests resource.
Server responds with 200 OK, body, and ETag: "xyz".
Client stores response body and associated ETag locally (memory, DB, or file cache).
On subsequent requests, client sends If-None-Match: "xyz".
Server compares the provided ETag with current resource; if equal, it returns 304 Not Modified with no body; otherwise a fresh 200 OK and new ETag.
This minimizes payload when resources haven't changed. ETags are server-controlled; your app must trust them and handle their presence/absence.
Implementing Conditional Requests
Use the http package for simple requests. Persist ETags alongside cached responses (SharedPreferences, Hive, or sqlite). Below is a compact example showing how to send If-None-Match and update local cache on 200 responses.
import 'package:http/http.dart' as http;
Future<http.Response> fetchWithEtag(String url, String? etag) async {
final headers = <String, String>{};
if (etag != null) headers['If-None-Match'] = etag;
final resp = await http.get(Uri.parse(url), headers: headers);
return resp;
}Handling the response:
If statusCode == 200: store resp.body and resp.headers['etag'].
If statusCode == 304: reuse stored body; refresh any metadata timestamp.
Other codes: handle errors as usual.
Persisting the ETag and cached body might look like storing a JSON object keyed by URL.
Cache Strategy In Flutter Apps
Choose a caching strategy that fits your app's UX and data characteristics:
Short-Lived Cache: Useful for frequently updated lists. Keep cached content for a few seconds/minutes and always validate with If-None-Match.
Long-Lived Cache: For mostly static assets, trust ETag until server changes; validate less often or only on pull-to-refresh.
Stale-While-Revalidate: Immediately show cached content while performing a background conditional request; update UI if the server returns 200.
Implementing stale-while-revalidate:
Load cached data into the UI quickly.
Trigger a conditional request in the background.
If 200 arrives, update cache and UI. If 304, update cache timestamp without changing body.
A small snippet to decide whether to revalidate:
bool shouldRevalidate(DateTime lastFetched, Duration maxAge) {
return DateTime.now().difference(lastFetched) > maxAge;
}Use device storage to record lastFetched and etag per URL.
Handling Edge Cases And Invalidations
No ETag Provided: Some servers don’t send ETags. Fallback to cache-control headers (max-age) or use last-modified with If-Modified-Since. If none exist, decide based on your app's tolerance for staleness.
Weak ETags and Validators: ETags can be weak (prefixed with W/) which conservatively indicate semantic equivalence; servers should document behavior.
Authentication and Vary: ETags can be specific to authenticated sessions. Pay attention to Vary headers (e.g., Vary: Authorization) and scope caches accordingly.
Race Conditions: When updating cache after a 200, ensure writes are atomic to avoid returning partial data. Use locks or single-threaded updates per key.
Offline Behavior: Always allow reading cached data offline. When the network returns, reconcile with server using conditional requests.
Vibe Studio

Vibe Studio, powered by Steve’s advanced AI agents, is a revolutionary no-code, conversational platform that empowers users to quickly and efficiently create full-stack Flutter applications integrated seamlessly with Firebase backend services. Ideal for solo founders, startups, and agile engineering teams, Vibe Studio allows users to visually manage and deploy Flutter apps, greatly accelerating the development process. The intuitive conversational interface simplifies complex development tasks, making app creation accessible even for non-coders.
Conclusion
ETags and conditional requests are low-effort, high-impact mechanisms to reduce bandwidth and improve perceived performance in Flutter mobile development. Implementing them requires storing ETags with cached responses, sending If-None-Match headers, and handling 304 responses gracefully. Combine conditional requests with a clear caching strategy (stale-while-revalidate, short- or long-lived cache) and handle edge cases like missing ETags, Vary headers, or authentication. These practices make your Flutter app more efficient and responsive for users on limited or metered networks.