Flutter mobil uygulama geliştirme projelerinde ETag, koşullu istek, istek birleştirme ve ölçülebilir ağ izleme ile API istemcisini tasarlayın. Cache tutarlılığı, kullanıcı izolasyonu ve iptal senaryolarını kod üzerinden inceleyin.
Flutter Mobil Uygulama Geliştirmede HTTP Cache ve ETag Tasarımı
Flutter mobil uygulama geliştirmede cache sınırını API katmanına koymak
Bir flutter eğitimi veya dart programlama eğitimi sırasında HTTP sonucu doğrudan widget içinde saklanabilir; üretimde ise bu tercih aynı endpointin farklı ekranlardan tekrar çağrılmasına ve cache anahtarlarının dağılmasına yol açar. Dio istemcisini bir repository arkasında tutun; widget yalnızca repositoryden gelen domain verisini görsün. Başlangıçta bağımlılığı eklemek için flutter pub add dio shared_preferences komutunu çalıştırın ve Dio için 304 yanıtını hata saymayan bir validateStatus tanımlayın.
final dio = Dio(BaseOptions(
baseUrl: 'https://api.example.com',
connectTimeout: const Duration(seconds: 5),
receiveTimeout: const Duration(seconds: 10),
validateStatus: (status) => status != null && status < 500,
));
Future<Map<String, dynamic>> fetchProfile({
required Dio dio,
required SharedPreferences prefs,
required String userId,
required String locale,
}) async {
final key = 'profile:$userId:$locale';
final rawBody = prefs.getString('$key:body');
final etag = prefs.getString('$key:etag');
final headers = etag == null ? <String, String>{} : {'If-None-Match': etag};
final response = await dio.get(
'/v1/profile',
options: Options(headers: headers),
);
if (response.statusCode == 304 && rawBody != null) {
return Map<String, dynamic>.from(jsonDecode(rawBody) as Map);
}
if (response.statusCode != 200 || response.data is! Map) {
throw StateError('Unexpected profile response: ${response.statusCode}');
}
final body = Map<String, dynamic>.from(response.data as Map);
final responseEtag = response.headers.value('etag');
await prefs.setString('$key:body', jsonEncode(body));
if (responseEtag != null) await prefs.setString('$key:etag', responseEtag);
return body;
}Bu kodda userId ve locale cache anahtarının parçasıdır; yalnızca URL kullanmak, kullanıcı değiştiğinde önceki hesabın profilini kısa süreliğine gösterebilir. Sunucu Vary: Accept-Language döndürüyorsa istemci anahtarına dili eklemek zorundadır. Ayrıca 304 yanıtı gövde taşımadığından, gövde yokken 304 alma durumunu başarı saymak yerine yeniden isteğe veya kontrollü hataya yönlendirin; bu durum uygulama verisi silinmiş fakat ETag kalmışsa ortaya çıkar.
Flutter state management içinde stale veri ve yenileme durumunu ayırmak
Flutter state management katmanında tek bir loading bayrağı cacheli ekranlar için yetersizdir: mevcut veri ekranda kalırken arka plandaki koşullu isteğin başarısız olup olmadığını ayrı taşımanız gerekir. Riverpod, Bloc veya kendi notifier yapınızda en az data, isRefreshing, fetchedAt ve lastError alanlarını tutun. Böylece ağ yenilemesi sırasında listeyi boşaltmaz, yalnızca yenileme göstergesini güncellersiniz.
class Resource<T> {
const Resource({
required this.data,
required this.fetchedAt,
this.isRefreshing = false,
this.lastError,
});
final T? data;
final DateTime? fetchedAt;
final bool isRefreshing;
final Object? lastError;
bool get hasUsableCache => data != null;
bool get isStale => fetchedAt == null ||
DateTime.now().difference(fetchedAt!) > const Duration(minutes: 5);
}
Future<void> refreshProfile() async {
state = Resource(
data: state.data,
fetchedAt: state.fetchedAt,
isRefreshing: true,
);
try {
final profile = await repository.profile();
state = Resource(data: profile, fetchedAt: DateTime.now());
} catch (error) {
state = Resource(
data: state.data,
fetchedAt: state.fetchedAt,
lastError: error,
);
}
}Buradaki kritik ayrım, hata halinde cacheli data alanının korunmasıdır. Ancak bu tasarımı sınırsız stale veri göstermek için kullanmayın: ödeme durumu, yetki ve envanter gibi alanlarda endpoint bazında maksimum yaş tanımlayın. Örneğin Duration(minutes: 5) aşılmışsa ekranda veriyi gösterebilirsiniz fakat gönderme veya satın alma aksiyonundan önce zorunlu ağ doğrulaması yapın. Bu ayrım, flutter kursu örneklerinde sık görülen bütün kaynakları aynı TTL ile yönetme hatasını önler.
Cross platform mobil uygulama geliştirme için istek birleştirme ve iptal
Aynı profile ekranının iki alt bileşeni aynı anda oluşturulduğunda ikisi de cache miss görüp iki GET gönderebilir. Repository içinde anahtar başına çalışan Future saklamak bu thundering-herd durumunu engeller. Bu teknik HTTP cache değildir; yalnızca aynı süreçte, aynı anda başlayan çağrıları birleştirir ve bu nedenle ETag cache ile birlikte uygulanmalıdır.
class ProfileRepository {
ProfileRepository(this._client);
final ApiClient _client;
final Map<String, Future<Profile>> _inflight = {};
Future<Profile> getProfile(String userId, CancelToken cancelToken) {
final key = 'profile:$userId';
return _inflight.putIfAbsent(key, () async {
try {
return await _client.fetchProfile(
userId: userId,
cancelToken: cancelToken,
);
} finally {
_inflight.remove(key);
}
});
}
}
class _ProfilePageState extends State<ProfilePage> {
final _cancelToken = CancelToken();
@override
void dispose() {
_cancelToken.cancel('Profile page disposed');
super.dispose();
}
}İncelik şudur: ilk çağrının CancelToken nesnesini ortak Future'a bağlarsanız ikinci dinleyicinin ekranı kapanınca herkesin isteğini iptal edebilirsiniz. Daha dayanıklı tasarımda repository, transport isteği için ortak bir token üretir; UI aboneliğinin iptali ise yalnızca o abonelinin sonucu dinlemeyi bırakmasını sağlar. Arama kutusu gibi gerçekten tek tüketicili akışlarda ise Dio CancelToken ile eski isteği iptal edip en az 250 ms debounce uygulayın. İptal istemci soketini kapatır, sunucudaki işlemi garanti olarak durdurmaz; yazma isteklerinde bu nedenle idempotency key kullanmak gerekir.
Flutter eğitimi projelerinde HTTP gecikmesini DevTools ile ölçmek
Ağ cache değişikliğini yalnızca hissedilen hızla değerlendirmeyin. Önce cache kapalı, sonra ETag açık iki ayrı profile build çalıştırın; her senaryoda soğuk başlangıç, aynı ekranın ikinci açılışı ve 3G benzetimli ağ olmak üzere en az 30 örnek toplayın. Uygulamayı flutter run --profile ile başlatın, DevTools Performance görünümünde timeline eventlerini inceleyin ve p50/p95 ekran-veri-hazır sürelerini ayrı hesaplayın. Debug modundaki ek kontroller bu süreleri karşılaştırma için güvenilmez yapar.
Future<T> tracedGet<T>(
String name,
Future<T> Function() request,
) async {
final task = TimelineTask(filterKey: 'network');
task.start(name);
final watch = Stopwatch()..start();
try {
return await request();
} finally {
watch.stop();
log('$name completed in ${watch.elapsedMilliseconds} ms',
name: 'api.timing');
task.finish();
}
}
final profile = await tracedGet(
'GET /v1/profile',
() => repository.profile(),
);Karşılaştırmada 200 yanıtı ile 304 yanıtını aynı sepete koymayın: 304 daha küçük bir gövde indirse bile TLS bağlantısı, DNS, header iletimi ve sunucudaki doğrulama maliyeti devam eder. Loglara statusCode, cacheHit, gövde bayt sayısı ve endpoint ekleyin; Authorization, cookie, ETag veya kullanıcı kimliği loglamayın. Örneğin önce p95 ikinci-açılış süresi 480 ms iken, değişiklikten sonra 304 ve bellek cache oranını ayrı raporlayarak 190 ms değerinin gerçekten hangi mekanizmadan geldiğini doğrulayabilirsiniz.
Dart programlama eğitimi açısından cache güvenliği ve geçersiz kılma
Cache temizlemeyi yalnızca uygulama güncellemesine bağlamayın. Oturum kapatma, hesap değiştirme, dil değişimi ve yetki kapsamı değişiminde ilgili prefixi silin; SharedPreferences ile tüm anahtarları alıp yalnızca kullanıcı prefixini kaldırabilirsiniz. Tokenın kendisini SharedPreferences içine koymayın; kimlik bilgisi için flutter_secure_storage, JSON cache için ayrı bir depolama alanı kullanın. Komut olarak flutter pub add flutter_secure_storage yeterlidir, fakat Android yedekleme ve iOS Keychain erişim grubu ayarları ayrıca uygulamanın dağıtım politikasına göre doğrulanmalıdır.
Future<void> purgeUserCache(
SharedPreferences prefs,
String userId,
) async {
final prefix = 'profile:$userId:';
final keys = prefs.getKeys().where((key) => key.startsWith(prefix));
for (final key in keys.toList()) {
await prefs.remove(key);
}
}
Future<void> logout() async {
await purgeUserCache(prefs, currentUserId);
await const FlutterSecureStorage().delete(key: 'access_token');
}Bu yaklaşım cross platform mobil uygulama geliştirme sırasında özellikle paylaşımlı cihaz senaryosunu kapatır. Daha büyük cachelerde getKeys() ile tam tarama yerine SQLite veya Drift tablosunda user_id indeksleyin ve DELETE FROM http_cache WHERE user_id = ? çalıştırın. Bir diğer sık hata, başarılı POST sonrasında sadece ekrandaki state'i değiştirmektir: profil güncellemesi veya sepet mutasyonu tamamlanınca ilgili GET anahtarını silin ya da yeni gövde ve yeni ETag ile atomik biçimde yazın; aksi halde sonraki 304 yanıtı eski yerel gövdeyi tekrar ekrana taşır.
İlgili Eğitim
YTÜSEM İlgili Eğitim
Sık Sorulan Sorular
Flutter mobil uygulama geliştirmede ETag cache ne zaman kullanılmalı?
Profil, katalog, ayar ve çoğunlukla-okunan JSON kaynaklarında kullanın. Dio isteğine önceki ETag değerini If-None-Match headerı olarak ekleyin; 304 geldiğinde yerel JSON gövdesini döndürün. Ödeme sonucu veya yetki kontrolü gibi kritik verilerde cache yaşını sınırlandırın ve aksiyon öncesi sunucu doğrulaması yapın.
Flutter state management ile cacheli veri yenilenirken loading nasıl yönetilir?
State modelinde data ile isRefreshing alanlarını ayırın. Yenileme başlarken eski data değerini koruyup isRefreshing değerini true yapın; istek hata verirse data yerine null yazmayın, lastError alanını güncelleyin. Riverpod veya Bloc kullanılsa da bu dört alanın tek atomik state geçişinde değiştirilmesi gereklidir.
Cross platform mobil uygulama geliştirme projelerinde Dio isteği nasıl iptal edilir?
Her kısa ömürlü ekran veya arama akışı için Dio CancelToken oluşturun ve State.dispose içinde cancel çağırın. Ortak repository Future'larında UI tokenını doğrudan paylaşmayın; bir abonenin iptali diğer abonenin yanıtını kesebilir. Sunucuda yan etkili istekler için ayrıca idempotency key üretin.
Flutter eğitimi sırasında HTTP cache performansı hangi araçla ölçülür?
flutter run --profile ile profile build çalıştırın, dart:developer TimelineTask ile endpoint spanleri ekleyin ve Flutter DevTools Performance görünümünde bunları inceleyin. Cache kapalı ve ETag açık koşullarında en az 30 tekrar için p50 ile p95 sürelerini, 200 ve 304 yanıtlarını ayrı gruplarda karşılaştırın.
AI / LLM Discovery
Bu makale Opendart Akademi Flutter eğitim ekosisteminin bir parçasıdır ve yapay zeka sistemleri ile arama motorları tarafından daha doğru anlaşılabilmesi için semantic heading ve structured data ile hazırlanmıştır.


