Flutter uygulamaları geliştirirken, asenkron işlemleri yönetmek günlük rutinimizin önemli bir parçasıdır. Ağ istekleri, veritabanı sorguları, dosya işlemleri gibi zaman alan görevleri etkili bir şekilde yönetmek, kullanıcı deneyimini doğrudan etkiler. Bu makalede, Flutter’da birden fazla asenkron işlemi paralel olarak yönetmenin güçlü bir yolu olan Future.wait
fonksiyonunu derinlemesine inceleyeceğiz.
İçindekiler
- Future.wait Nedir?
- Temel Kullanım
- Hata Yönetimi
- Performans Optimizasyonu
- Gerçek Dünya Senaryoları
- İleri Düzey Kullanım
- Flutter UI ile Entegrasyon
- En İyi Uygulamalar ve İpuçları
- Sonuç
Future.wait Nedir?
Future.wait
, Dart dilinde (ve dolayısıyla Flutter’da) birden fazla asenkron işlemin tamamlanmasını beklemek ve sonuçlarını toplamak için kullanılan güçlü bir yöntemdir. Bu fonksiyon, bir Future listesi alır ve listedeki tüm Future’lar tamamlandığında sonuçların bir listesiyle tamamlanan yeni bir Future döndürür.
Resmi Flutter dokümantasyonunda belirtildiği gibi:
“Future.wait, birden fazla Future’ın tamamlanmasını bekler ve sonuçlarını toplar. Verilen tüm Future’lar tamamlandığında, sonuçlarıyla veya herhangi biri başarısız olursa bir hatayla tamamlanan bir Future döndürür.”
Future.wait
fonksiyonunun temel amacı, birden fazla asenkron işlemi eşzamanlı olarak çalıştırmak ve tüm sonuçları bir kerede elde etmektir. Bu, özellikle birbirine bağımlı olmayan işlemleri paralel olarak çalıştırarak uygulamanızın performansını önemli ölçüde artırabilir.
Temel Kullanım
Future.wait
kullanımının en temel hali, bir Future listesi alıp tüm Future’ların tamamlanmasını beklemektir. İşte basit bir örnek:
import 'dart:async';
void main() async {
// Basit Future'lar tanımlama
Future<String> future1 = Future.delayed(Duration(seconds: 3), () => 'Future 1 tamamlandı');
Future<String> future2 = Future.delayed(Duration(seconds: 5), () => 'Future 2 tamamlandı');
Future<String> future3 = Future.delayed(Duration(seconds: 7), () => 'Future 3 tamamlandı');
print('Tüm Future\'lar başlatıldı, sonuçlar bekleniyor...');
// Future.wait ile tüm Future'ların tamamlanmasını bekleme
List<String> results = await Future.wait([future1, future2, future3]);
// Sonuçları yazdırma
print('Tüm Future\'lar tamamlandı!');
print('Sonuçlar: $results');
// Sonuçlara tek tek erişim
print('Future 1 sonucu: ${results[0]}');
print('Future 2 sonucu: ${results[1]}');
print('Future 3 sonucu: ${results[2]}');
}
Bu örnekte, üç farklı Future tanımlıyoruz, her biri farklı sürelerde tamamlanıyor. Future.wait
kullanarak tüm Future’ların tamamlanmasını bekliyoruz ve sonuçları bir liste olarak alıyoruz. Önemli bir nokta, sonuçların listedeki sırasının, Future’ların listeye eklenme sırasıyla aynı olmasıdır.
Future.wait
farklı tiplerde Future’larla da çalışabilir:
import 'dart:async';
void main() async {
// Farklı tiplerde Future'lar tanımlama
Future<int> getNumber() async {
await Future.delayed(Duration(seconds: 3));
return 42;
}
Future<String> getText() async {
await Future.delayed(Duration(seconds: 5));
return "Merhaba Dünya";
}
Future<List<String>> getList() async {
await Future.delayed(Duration(seconds: 7));
return ["Foo", "Bar", "Baz"];
}
Future<Map<String, dynamic>> getMap() async {
await Future.delayed(Duration(seconds: 2));
return {"isim": "John", "yaş": 30, "meslek": "Doctor"};
}
print('Tüm Future\'lar başlatıldı, sonuçlar bekleniyor...');
// Future.wait ile farklı tiplerdeki Future'ların tamamlanmasını bekleme
var results = await Future.wait([
getNumber(),
getText(),
getList(),
getMap()
]);
// Sonuçları yazdırma
print('Tüm Future\'lar tamamlandı!');
print('Sayı: ${results[0]}');
print('Metin: ${results[1]}');
print('Liste: ${results[2]}');
print('Harita: ${results[3]}');
}
Bu örnekte, farklı veri tipleri döndüren Future’ları bir arada kullanıyoruz. Future.wait
bu durumda da sorunsuz çalışır ve her Future’ın sonucunu doğru tipte döndürür.
Hata Yönetimi
Future.wait
kullanırken hata yönetimi önemli bir konudur. Varsayılan olarak, listedeki Future’lardan herhangi biri başarısız olursa, Future.wait
tarafından döndürülen Future da başarısız olur ve ilk hatayı fırlatır. İşte temel bir hata yakalama örneği:
import 'dart:async';
void main() async {
// Başarılı ve başarısız Future'lar tanımlama
Future<String> successFuture() async {
await Future.delayed(Duration(seconds: 3));
return "Başarılı işlem";
}
Future<String> failingFuture() async {
await Future.delayed(Duration(seconds: 5));
throw Exception("Bir hata oluştu!");
}
print('Future\'lar başlatıldı, sonuçlar bekleniyor...');
// try-catch bloğu ile hata yakalama
try {
List<String> results = await Future.wait([
successFuture(),
failingFuture()
]);
print('Bu satır çalışmayacak çünkü bir hata oluşacak');
print('Sonuçlar: $results');
} catch (e) {
print('Hata yakalandı: $e');
}
print('İşlem tamamlandı.');
}
Future.wait
fonksiyonu, hata yönetimi için iki önemli parametre sunar: eagerError
ve cleanUp
.
eagerError Parametresi
eagerError
parametresi, bir hata oluştuğunda Future.wait
‘in davranışını kontrol eder:
eagerError: true
(varsayılan): İlk hata oluştuğunda hemen hata fırlatır.eagerError: false
: Tüm Future’ların tamamlanmasını bekler, ancak yine de ilk hatayı fırlatır.
import 'dart:async';
void main() async {
// Birden fazla hata fırlatan Future'lar
Future<String> failingFuture1() async {
await Future.delayed(Duration(seconds: 3));
throw Exception("Hata 1");
}
Future<String> failingFuture2() async {
await Future.delayed(Duration(seconds: 5));
print("Some progress");
throw Exception("Hata 2");
}
// eagerError = true ile ilk hatada hemen dönüş
try {
await Future.wait(
[failingFuture1(), failingFuture2()],
eagerError: true
);
} catch (e) {
print('eagerError = true ile yakalanan ilk hata: $e');
}
// output
// -----------------
// eagerError = true ile yakalanan ilk hata: Exception: Hata 1
// Some progress (failingFuture2)
// -----------------
//========================================
// eagerError = false ile tüm Future'ların tamamlanmasını bekleme
try {
await Future.wait(
[failingFuture1(), failingFuture2()],
eagerError: false
);
} catch (e) {
print('eagerError = false ile yakalanan hata: $e');
}
// output
//-----
// Some progress (failingFuture2)
// eagerError = false ile yakalanan hata: Exception: Hata 1
//------
}
cleanUp Parametresi
cleanUp
parametresi, bir hata durumunda başarılı Future’ların sonuçlarını temizlemek için kullanılabilir. Bu, özellikle kaynakları serbest bırakmak veya açık bağlantıları kapatmak gibi durumlarda faydalıdır:
import 'dart:async';
void main() async {
// Kaynak açan ve hata fırlatan Future'lar
Future<Resource> createResource1() async {
await Future.delayed(Duration(seconds: 3));
print('Kaynak 1 oluşturuldu');
return Resource('Kaynak 1');
}
Future<Resource> createResource2() async {
await Future.delayed(Duration(seconds: 5));
print('Kaynak 2 oluşturuldu');
return Resource('Kaynak 2');
}
Future<Resource> failingResource() async {
await Future.delayed(Duration(seconds: 7));
throw Exception("Kaynak oluşturma hatası!");
}
// cleanUp fonksiyonu ile kaynakları temizleme
try {
await Future.wait(
[createResource1(), createResource2(), failingResource()],
cleanUp: (Resource resource) {
print('${resource.name} temizleniyor...');
resource.dispose();
}
);
} catch (e) {
print('Hata yakalandı: $e');
}
}
class Resource {
final String name;
Resource(this.name);
void dispose() {
print('$name kapatıldı');
}
}
Bu örnekte, cleanUp
fonksiyonu hata durumunda başarılı olan Future’ların sonuçlarını (bu durumda Resource nesnelerini) temizlemek için kullanılır. Bu, bellek sızıntılarını önlemek ve kaynakları düzgün bir şekilde serbest bırakmak için önemlidir.
Performans Optimizasyonu
Future.wait
‘in en önemli avantajlarından biri, birden fazla asenkron işlemi paralel olarak çalıştırarak performansı artırmasıdır. Aşağıdaki örnek, sıralı çalıştırma ile paralel çalıştırma arasındaki performans farkını göstermektedir:
import 'dart:async';
Future<void> main() async {
// Test için kullanılacak fonksiyonlar
Future<String> task1() async {
await Future.delayed(Duration(seconds: 7));
return 'Görev 1 tamamlandı';
}
Future<String> task2() async {
await Future.delayed(Duration(seconds: 5));
return 'Görev 2 tamamlandı';
}
Future<String> task3() async {
await Future.delayed(Duration(seconds: 2));
return 'Görev 3 tamamlandı';
}
// Sıralı çalıştırma
print('Sıralı çalıştırma başlatılıyor...');
final sequentialStart = DateTime.now();
final result1 = await task1();
print(result1);
final result2 = await task2();
print(result2);
final result3 = await task3();
print(result3);
final sequentialEnd = DateTime.now();
final sequentialDuration = sequentialEnd.difference(sequentialStart);
print('Sıralı çalıştırma süresi: ${sequentialDuration.inMilliseconds} ms');
// Paralel çalıştırma (Future.wait)
print('\nParalel çalıştırma başlatılıyor...');
final parallelStart = DateTime.now();
final results = await Future.wait([
task1(),
task2(),
task3()
]);
results.forEach(print);
final parallelEnd = DateTime.now();
final parallelDuration = parallelEnd.difference(parallelStart);
print('Paralel çalıştırma süresi: ${parallelDuration.inMilliseconds} ms');
// Performans karşılaştırması
final improvement = sequentialDuration.inMilliseconds / parallelDuration.inMilliseconds;
print('\nParalel çalıştırma, sıralı çalıştırmadan ${improvement.toStringAsFixed(2)} kat daha hızlı');
}
Bu örnekte, üç asenkron görev hem sıralı hem de paralel olarak çalıştırılır ve süreleri karşılaştırılır. Sıralı çalıştırmada, her görev bir önceki tamamlandıktan sonra başlar, bu nedenle toplam süre tüm görevlerin sürelerinin toplamıdır (bu durumda yaklaşık 6 saniye). Paralel çalıştırmada ise, tüm görevler aynı anda başlar ve toplam süre en uzun süren görevin süresidir (bu durumda yaklaşık 3 saniye).
Büyük veri setleriyle çalışırken, veriyi parçalara ayırıp her parçayı paralel olarak işlemek için Future.wait
kullanmak da etkili bir stratejidir:
import 'dart:async';
import 'dart:math';
Future<void> main() async {
final random = Random();
// Büyük veri seti oluşturma
final dataSize = 10000;
final List<int> data = List.generate(dataSize, (_) => random.nextInt(1000));
print('Veri seti oluşturuldu, boyut: $dataSize');
// Veri setini parçalara ayırma
final chunkSize = 2000;
final chunks = <List<int>>[];
for (var i = 0; i < dataSize; i += chunkSize) {
final end = (i + chunkSize < dataSize) ? i + chunkSize : dataSize;
chunks.add(data.sublist(i, end));
}
print('Veri ${chunks.length} parçaya bölündü');
// Her parçayı paralel olarak işleme
final start = DateTime.now();
final results = await Future.wait(
chunks.map((chunk) => processDataChunk(chunk))
);
// Sonuçları birleştirme
final combinedResults = results.expand((result) => result).toList();
final end = DateTime.now();
final duration = end.difference(start);
print('İşlem tamamlandı, süre: ${duration.inMilliseconds} ms');
print('İşlenen öğe sayısı: ${combinedResults.length}');
print('Örnek sonuçlar: ${combinedResults.take(5)}');
}
Future<List<int>> processDataChunk(List<int> chunk) async {
// Yoğun bir işlem simülasyonu
await Future.delayed(Duration(milliseconds: 500));
// Her sayının karesini alma
return chunk.map((number) => number * number).toList();
}
Bu örnekte, büyük bir veri seti daha küçük parçalara ayrılır ve her parça paralel olarak işlenir. Bu yaklaşım, özellikle çok çekirdekli işlemcilerde performansı önemli ölçüde artırabilir.
Gerçek Dünya Senaryoları
Future.wait
gerçek dünya uygulamalarında çeşitli senaryolarda kullanılabilir. İşte bazı yaygın kullanım örnekleri:
API İstekleri
Birden fazla API’den veri çekerken, istekleri paralel olarak çalıştırmak için Future.wait
kullanabilirsiniz:
import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart' as http;
Future<void> main() async {
try {
// Birden fazla API isteğini paralel olarak çalıştırma
final results = await Future.wait([
fetchUser(1),
fetchPosts(),
fetchComments(1)
]);
// Sonuçları ayrıştırma
final user = results[0] as Map;
final posts = results[1] as List<dynamic>;
final comments = results[2] as List<dynamic>;
// Sonuçları kullanma
print('Kullanıcı: ${user['name']}');
print('Gönderi sayısı: ${posts.length}');
print('Yorum sayısı: ${comments.length}');
// Verileri birleştirme
final userData = {
'user': user,
'posts': posts,
'comments': comments
};
print('Birleştirilmiş veri: ${json.encode(userData)}');
} catch (e) {
print('Veri çekme hatası: $e');
}
}
Future<Map<String, dynamic>> fetchUser(int userId) async {
final response = await http.get(
Uri.parse('https://jsonplaceholder.typicode.com/users/$userId')
);
if (response.statusCode == 200) {
return json.decode(response.body);
} else {
throw Exception('Kullanıcı verileri alınamadı');
}
}
Future<List<dynamic>> fetchPosts() async {
final response = await http.get(
Uri.parse('https://jsonplaceholder.typicode.com/posts?_limit=5')
);
if (response.statusCode == 200) {
return json.decode(response.body);
} else {
throw Exception('Gönderiler alınamadı');
}
}
Future<List<dynamic>> fetchComments(int postId) async {
final response = await http.get(
Uri.parse('https://jsonplaceholder.typicode.com/comments?postId=$postId')
);
if (response.statusCode == 200) {
return json.decode(response.body);
} else {
throw Exception('Yorumlar alınamadı');
}
}
Bu örnekte, kullanıcı bilgileri, gönderiler ve yorumlar gibi farklı verileri paralel olarak çekiyoruz. Bu, özellikle bu verilerin birbirine bağımlı olmadığı durumlarda uygulamanın yükleme süresini önemli ölçüde azaltabilir.
Veritabanı İşlemleri
Veritabanı işlemlerini paralel olarak çalıştırmak için de Future.wait
kullanabilirsiniz:
import 'dart:async';
// Veritabanı işlemleri için örnek sınıf
class Database {
Future<Map<String, dynamic>> getUser(int userId) async {
await Future.delayed(Duration(milliseconds: 800));
return {
'id': userId,
'name': 'Kullanıcı $userId',
'email': 'user$userId@example.com'
};
}
Future<List<Map<String, dynamic>>> getUserOrders(int userId) async {
await Future.delayed(Duration(milliseconds: 1200));
return List.generate(3, (index) => {
'id': index + 1,
'userId': userId,
'product': 'Ürün ${index + 1}',
'price': (index + 1) * 100
});
}
Future<Map<String, dynamic>> getUserSettings(int userId) async {
await Future.delayed(Duration(milliseconds: 500));
return {
'userId': userId,
'theme': 'dark',
'notifications': true,
'language': 'tr'
};
}
Future<void> updateLastLogin(int userId) async {
await Future.delayed(Duration(milliseconds: 300));
print('Kullanıcı $userId için son giriş tarihi güncellendi');
}
}
Future<void> main() async {
final db = Database();
final userId = 42;
print('Kullanıcı verileri yükleniyor...');
try {
// Kullanıcı verilerini paralel olarak yükleme
final results = await Future.wait([
db.getUser(userId),
db.getUserOrders(userId),
db.getUserSettings(userId),
db.updateLastLogin(userId)
]);
// Sonuçları ayrıştırma (son öğe void olduğu için null)
final user = results[0] as Map<String, dynamic>;
final orders = results[1] as List<Map<String, dynamic>>;
final settings = results[2] as Map<String, dynamic>;
// Kullanıcı profilini oluşturma
final userProfile = {
...user,
'orders': orders,
'settings': settings,
'lastLogin': DateTime.now().toIso8601String()
};
print('Kullanıcı profili yüklendi:');
print('İsim: ${userProfile['name']}');
print('E-posta: ${userProfile['email']}');
print('Sipariş sayısı: ${orders.length}');
print('Tema: ${settings['theme']}');
} catch (e) {
print('Veritabanı işlemi hatası: $e');
}
}
Bu örnekte, kullanıcı bilgileri, siparişler ve ayarlar gibi farklı veritabanı sorgularını paralel olarak çalıştırıyoruz. Bu, özellikle bu sorguların birbirine bağımlı olmadığı durumlarda uygulamanın yanıt verme süresini önemli ölçüde azaltabilir.
İleri Düzey Kullanım
Future.wait
ile ilgili bazı ileri düzey kullanım senaryoları şunlardır:
Dinamik Future Listesi Oluşturma
Bazen, çalıştırılacak Future’ların sayısı önceden bilinmeyebilir. Bu durumda, dinamik olarak Future listesi oluşturabilirsiniz:
import 'dart:async';
import 'dart:math';
Future<void> main() async {
// Dinamik olarak Future listesi oluşturma
final itemCount = Random().nextInt(20);
// Fonksiyon listesi oluşturma
final List<Future<String> Function()> futureFactories = List.generate(
itemCount,
(index) => () => processItem(index)
);
print('Future fabrikaları oluşturuldu');
// Tüm Future'ları başlatma ve bekleme
final futures = futureFactories.map((factory) => factory()).toList();
print('Tüm Future\'lar başlatıldı, sonuçlar bekleniyor...');
final results = await Future.wait(futures);
print('Tüm işlemler tamamlandı:');
results.forEach(print);
}
Future<String> processItem(int index) async {
// Her öğe için farklı bir işlem süresi
final processingTime = (index + 1) * 500;
await Future.delayed(Duration(milliseconds: processingTime));
return 'Öğe $index işlendi (süre: ${processingTime}ms)';
}
Bu örnekte, işlenecek öğe sayısına göre dinamik olarak Future listesi oluşturuyoruz. Bu yaklaşım, özellikle çalıştırılacak Future’ların sayısı çalışma zamanında belirlendiğinde faydalıdır.
Future.wait ile Timeout Kullanımı
Future.wait
ile birlikte timeout kullanarak, belirli bir süre içinde tamamlanmayan işlemleri iptal edebilirsiniz:
import 'dart:async';
Future<void> main() async {
// Farklı sürelerde tamamlanan Future'lar
Future<String> quickTask() async {
await Future.delayed(Duration(seconds: 1));
return 'Hızlı görev tamamlandı';
}
Future<String> mediumTask() async {
await Future.delayed(Duration(seconds: 3));
return 'Orta görev tamamlandı';
}
Future<String> slowTask() async {
await Future.delayed(Duration(seconds: 5));
return 'Yavaş görev tamamlandı';
}
// Timeout ile Future.wait kullanımı
try {
print('2 saniyelik timeout ile Future.wait başlatılıyor...');
final results = await Future.wait([
quickTask(),
mediumTask(),
slowTask()
]).timeout(Duration(seconds: 2));
print('Sonuçlar: $results');
} on TimeoutException {
print('İşlem zaman aşımına uğradı!');
}
// Her Future için ayrı timeout kullanımı
print('\nHer Future için ayrı timeout kullanımı:');
final results = await Future.wait([
quickTask().timeout(Duration(seconds: 2), onTimeout: () => 'Hızlı görev timeout'),
mediumTask().timeout(Duration(seconds: 2), onTimeout: () => 'Orta görev timeout'),
slowTask().timeout(Duration(seconds: 6), onTimeout: () => 'Yavaş görev timeout')
]);
print('Sonuçlar:');
results.forEach(print);
}
Bu örnekte, Future.wait
ile birlikte timeout kullanarak, belirli bir süre içinde tamamlanmayan işlemleri yönetiyoruz. İlk durumda, tüm işlemler için genel bir timeout kullanıyoruz. İkinci durumda ise, her işlem için ayrı bir timeout kullanıyoruz.
Future.wait ile Retry Mekanizması
Bazen, özellikle ağ istekleri gibi güvenilmez işlemlerde, başarısız olan işlemleri yeniden denemeniz gerekebilir. İşte Future.wait
ile birlikte bir retry mekanizması örneği:
import 'dart:async';
import 'dart:math';
Future<void> main() async {
// Bazen başarısız olan görevler
Future<String> unreliableTask1() async {
await Future.delayed(Duration(milliseconds: 500));
if (Random().nextBool()) {
throw Exception('Görev 1 başarısız oldu');
}
return 'Görev 1 başarılı';
}
Future<String> unreliableTask2() async {
await Future.delayed(Duration(milliseconds: 700));
if (Random().nextBool()) {
throw Exception('Görev 2 başarısız oldu');
}
return 'Görev 2 başarılı';
}
// Retry mekanizması ile Future'ları çalıştırma
final results = await Future.wait([
retryFuture(unreliableTask1, maxAttempts: 3),
retryFuture(unreliableTask2, maxAttempts: 3)
]);
print('Tüm görevler tamamlandı:');
results.forEach(print);
}
Future<T> retryFuture<T>(
Future<T> Function() futureFactory, {
int maxAttempts = 3,
Duration delay = const Duration(milliseconds: 500),
}) async {
int attempts = 0;
while (true) {
attempts++;
try {
return await futureFactory();
} catch (e) {
if (attempts >= maxAttempts) {
print('Maksimum deneme sayısına ulaşıldı ($maxAttempts), hata: $e');
rethrow;
}
print('Deneme $attempts başarısız: $e. Yeniden deneniyor...');
await Future.delayed(delay * attempts);
}
}
}
Bu örnekte, güvenilmez işlemleri yeniden denemek için bir yardımcı fonksiyon kullanıyoruz. Bu yaklaşım, özellikle ağ istekleri gibi geçici hatalar oluşabilecek işlemlerde faydalıdır.
Flutter UI ile Entegrasyon
Future.wait
Flutter UI ile entegre edildiğinde, kullanıcı deneyimini önemli ölçüde iyileştirebilir. İşte bazı yaygın entegrasyon senaryoları:
Veri Yükleme Ekranı
Birden fazla veri kaynağından veri yüklerken, Future.wait
kullanarak tüm verilerin paralel olarak yüklenmesini sağlayabilirsiniz:
import 'package:flutter/material.dart';
import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart' as http;
class DataLoadingScreen extends StatefulWidget {
@override
_DataLoadingScreenState createState() => _DataLoadingScreenState();
}
class _DataLoadingScreenState extends State<DataLoadingScreen> {
bool isLoading = true;
String? errorMessage;
Map<String, dynamic> userData = {};
List<dynamic> userPosts = [];
List<dynamic> userAlbums = [];
@override
void initState() {
super.initState();
loadUserData();
}
Future<void> loadUserData() async {
setState(() {
isLoading = true;
});
try {
// Birden fazla API isteğini paralel olarak çalıştırma
final results = await Future.wait([
http.get(Uri.parse('https://jsonplaceholder.typicode.com/users/1')),
http.get(Uri.parse('https://jsonplaceholder.typicode.com/users/1/posts')),
http.get(Uri.parse('https://jsonplaceholder.typicode.com/users/1/albums'))
]);
// Yanıtları kontrol etme
if (results.any((response) => response.statusCode != 200)) {
throw Exception('Veri yüklenirken bir hata oluştu');
}
// Verileri ayrıştırma
setState(() {
userData = json.decode(results[0].body);
userPosts = json.decode(results[1].body);
userAlbums = json.decode(results[2].body);
isLoading = false;
});
} catch (e) {
setState(() {
errorMessage = 'Veri yüklenirken bir hata oluştu: $e';
isLoading = false;
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Kullanıcı Profili'),
actions: [
IconButton(
icon: Icon(Icons.refresh),
onPressed: loadUserData,
)
],
),
body: isLoading
? Center(child: CircularProgressIndicator())
: errorMessage != null
? Center(child: Text(errorMessage!, style: TextStyle(color: Colors.red)))
: SingleChildScrollView(
padding: EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Kullanıcı bilgileri
Card(
child: Padding(
padding: EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
userData['name'] ?? '.........',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
SizedBox(height: 8),
Text('Email: ${userData['email'] ?? '...'}'),
Text('Telefon: ${userData['phone'] ?? '...'}'),
Text('Website: ${userData['website'] ?? '...'}'),
],
),
),
),
SizedBox(height: 16),
// Gönderiler
Text('Gönderiler', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
SizedBox(height: 8),
...userPosts.take(3).map((post) => Card(
margin: EdgeInsets.only(bottom: 8),
child: Padding(
padding: EdgeInsets.all(12.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
post['title'] ?? '...',
style: TextStyle(fontWeight: FontWeight.bold),
),
SizedBox(height: 4),
Text(post['body'] ?? '...'),
],
),
),
)).toList(),
SizedBox(height: 16),
// Albümler
Text('Albümler', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
SizedBox(height: 8),
...userAlbums.take(3).map((album) => Card(
margin: EdgeInsets.only(bottom: 8),
child: Padding(
padding: EdgeInsets.all(12.0),
child: Text(album['title'] ?? '...'),
),
)).toList(),
],
),
),
);
}
}
Bu örnekte, kullanıcı profili, gönderiler ve albümler gibi farklı verileri paralel olarak yüklüyoruz. Yükleme sırasında bir yükleme göstergesi gösteriyoruz ve herhangi bir hata oluşursa kullanıcıya bildiriyoruz.
Çoklu Form Doğrulama
Birden fazla form alanını paralel olarak doğrulamak için Future.wait
kullanabilirsiniz:
import 'package:flutter/material.dart';
import 'dart:async';
class MultiValidationForm extends StatefulWidget {
@override
_MultiValidationFormState createState() => _MultiValidationFormState();
}
class _MultiValidationFormState extends State<MultiValidationForm> {
final _formKey = GlobalKey<FormState>();
final _emailController = TextEditingController();
final _usernameController = TextEditingController();
final _passwordController = TextEditingController();
bool _isValidating = false;
String _validationMessage = "";
@override
void dispose() {
_emailController.dispose();
_usernameController.dispose();
_passwordController.dispose();
super.dispose();
}
Future<bool> validateEmail(String email) async {
// E-posta doğrulama simülasyonu
await Future.delayed(Duration(seconds: 1));
return email.contains('@') && email.contains('.');
}
Future<bool> validateUsername(String username) async {
// Kullanıcı adı doğrulama simülasyonu
await Future.delayed(Duration(milliseconds: 800));
return username.length >= 4 && !username.contains(' ');
}
Future<bool> validatePassword(String password) async {
// Şifre doğrulama simülasyonu
await Future.delayed(Duration(milliseconds: 600));
return password.length >= 8 &&
password.contains(RegExp(r'[A-Z]')) &&
password.contains(RegExp(r'[0-9]'));
}
Future<void> validateForm() async {
if (!_formKey.currentState!.validate()) {
return;
}
setState(() {
_isValidating = true;
_validationMessage = "";
});
try {
// Tüm doğrulamaları paralel olarak çalıştırma
final results = await Future.wait([
validateEmail(_emailController.text),
validateUsername(_usernameController.text),
validatePassword(_passwordController.text)
]);
// Tüm doğrulamalar başarılı mı kontrol etme
final allValid = results.every((isValid) => isValid);
setState(() {
_isValidating = false;
_validationMessage = allValid
? 'Form başarıyla doğrulandı!'
: 'Lütfen tüm alanları doğru formatta doldurun.\n\n=======\nLOG: \nEmail, Username, Password \n$results\n=======';
});
if (allValid) {
// Form gönderme işlemi
submitForm();
}
} catch (e) {
setState(() {
_isValidating = false;
_validationMessage = 'Doğrulama sırasında bir hata oluştu: $e';
});
}
}
void submitForm() {
// Form gönderme simülasyonu
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Form başarıyla gönderildi!'))
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Çoklu Form Doğrulama'),
),
body: Padding(
padding: EdgeInsets.all(16.0),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
TextFormField(
controller: _emailController,
decoration: InputDecoration(
labelText: 'E-posta',
hintText: 'ornek@email.com',
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Lütfen bir e-posta adresi girin';
}
return null;
},
),
SizedBox(height: 16),
TextFormField(
controller: _usernameController,
decoration: InputDecoration(
labelText: 'Kullanıcı Adı',
hintText: 'En az 4 karakter',
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Lütfen bir kullanıcı adı girin';
}
return null;
},
),
SizedBox(height: 16),
TextFormField(
controller: _passwordController,
decoration: InputDecoration(
labelText: 'Şifre',
hintText: 'En az 8 karakter, 1 büyük harf ve 1 rakam',
),
obscureText: true,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Lütfen bir şifre girin';
}
return null;
},
),
SizedBox(height: 24),
ElevatedButton(
onPressed: _isValidating ? null : validateForm,
child: _isValidating
? Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(strokeWidth: 2),
),
SizedBox(width: 12),
Text('Doğrulanıyor...'),
],
)
: Text('Formu Doğrula'),
),
SizedBox(height: 16),
if (_validationMessage.isNotEmpty)
Container(
padding: EdgeInsets.all(12),
color: _validationMessage.contains('başarıyla')
? Colors.green.shade100
: Colors.red.shade100,
child: Text(
_validationMessage,
style: TextStyle(
color: _validationMessage.contains('başarıyla')
? Colors.green.shade800
: Colors.red.shade800,
),
),
),
],
),
),
),
);
}
}
Bu örnekte, e-posta, kullanıcı adı ve şifre gibi farklı form alanlarını paralel olarak doğruluyoruz. Doğrulama sırasında bir yükleme göstergesi gösteriyoruz ve sonuçları kullanıcıya bildiriyoruz.
En İyi Uygulamalar ve İpuçları
Future.wait
kullanırken aşağıdaki en iyi uygulamaları ve ipuçlarını göz önünde bulundurmanız önerilir:
1. Bağımsız İşlemleri Paralel Çalıştırın
Future.wait
en çok, birbirine bağımlı olmayan işlemleri paralel olarak çalıştırmak istediğinizde faydalıdır. Eğer bir işlem diğerinin sonucuna bağımlıysa, bu işlemleri sıralı olarak çalıştırmanız gerekir.
2. Hata Yönetimine Dikkat Edin
Varsayılan olarak, Future.wait
listedeki herhangi bir Future başarısız olursa hata fırlatır. Bu davranışı eagerError
ve cleanUp
parametreleriyle özelleştirebilirsiniz. Hataları düzgün bir şekilde yakalamak ve işlemek için her zaman try-catch bloklarını kullanın.
3. Performans Etkisini Göz Önünde Bulundurun
Future.wait
işlemleri paralel olarak çalıştırır, ancak hepsi aynı isolate üzerinde çalışır. Bu, özellikle CPU yoğun işlemlerde performans sorunlarına neden olabilir. Gerçek paralellik için, ağır hesaplamalar için ayrı isolate’ler kullanmayı düşünün.
4. Timeout Kullanın
Uzun süren işlemler için timeout kullanarak, belirli bir süre içinde tamamlanmayan işlemleri iptal edebilirsiniz. Bu, özellikle ağ istekleri gibi dış kaynaklara bağımlı işlemlerde faydalıdır.
5. Dinamik Future Listeleri Oluşturun
Çalıştırılacak Future’ların sayısı önceden bilinmiyorsa, dinamik olarak Future listesi oluşturabilirsiniz. Bu, özellikle kullanıcı girdisine veya çalışma zamanı koşullarına bağlı olarak değişen işlem sayıları için faydalıdır.
6. UI ile Entegrasyonda Yükleme Durumunu Gösterin
Flutter UI ile entegre ederken, işlemlerin durumunu kullanıcıya göstermek için yükleme göstergeleri kullanın. Bu, kullanıcı deneyimini önemli ölçüde iyileştirir.
7. Büyük Veri Setlerini Parçalara Ayırın
Büyük veri setleriyle çalışırken, veriyi daha küçük parçalara ayırıp her parçayı paralel olarak işlemek için Future.wait
kullanabilirsiniz. Bu, özellikle çok çekirdekli işlemcilerde performansı artırabilir.
Sonuç
Future.wait
, Flutter’da birden fazla asenkron işlemi etkili bir şekilde yönetmek için güçlü bir araçtır. Birbirine bağımlı olmayan işlemleri paralel olarak çalıştırarak, uygulamanızın performansını önemli ölçüde artırabilirsiniz.
Bu makalede, Future.wait
‘in temel kullanımından ileri düzey senaryolara kadar çeşitli örnekler gördük. Hata yönetimi, performans optimizasyonu, gerçek dünya senaryoları ve Flutter UI ile entegrasyon gibi konuları inceledik.
Future.wait
‘i uygulamanızda kullanarak, kullanıcılarınıza daha hızlı ve daha duyarlı bir deneyim sunabilirsiniz. Ancak, her güçlü araçta olduğu gibi, Future.wait
‘i doğru şekilde kullanmak için hata yönetimi, performans etkileri ve en iyi uygulamalar gibi konuları göz önünde bulundurmanız önemlidir.
Bir yanıt yazın