Ön Hazırlık ve Gereksinimler
Uygulamanıza başlamadan önce Flutter SDK'sının güncel sürümünün (3.x veya üzeri) yüklü olduğundan emin olmalısınız. Projenizin bağımlılıklarını yönetmek için pubspec.yaml dosyanızın temiz olduğundan emin olun. Bu rehberde herhangi bir üçüncü parti kütüphane kullanmadan, Flutter'ın kendi sunduğu TextField ve setState mekanizmasını kullanarak performanslı bir çözüm geliştireceğiz.
- Flutter SDK 3.22+ yüklü olmalıdır.
- Dart dilinde temel koleksiyon işlemleri (List, Map) bilgisi gereklidir.
- VS Code veya Android Studio gibi bir IDE kurulumu gereklidir.
Adım 1: Veri Modelini ve Liste Yapısını Oluşturma
İlk adımda, filtreleme yapacağımız veri setini tanımlamamız gerekiyor. Basit bir kullanıcı listesi üzerinden ilerleyeceğiz. Verilerimizi bir List içerisinde tutacağız.
// Kullanıcı listesi verisi
final List tumKullanicilar = [
"Ahmet Yılmaz",
"Ayşe Demir",
"Mehmet Kaya",
"Fatma Çelik",
"Canan Aydın"
];
Burada tumKullanicilar değişkeni, filtrelenmemiş ana veri kaynağımızdır. Arama yapıldığında bu listeyi bozmamak için her zaman bir kopya üzerinde işlem yapacağız.
Adım 2: Filtrelenmiş Listeyi Yönetmek
Arama sonuçlarını göstermek için ana listemizden bağımsız, ekranda o an gösterilecek olan ikinci bir listeye ihtiyacımız var. Bu liste, arama terimi değiştikçe güncellenecektir.
// State sınıfı içerisinde tanımlanacak değişkenler
List gorunenListe = [];
@override
void initState() {
super.initState();
// Başlangıçta tüm veriyi göster
gorunenListe = tumKullanicilar;
}
initState metodu, widget ilk oluşturulduğunda çalışır ve gorunenListe değişkenini ana listemizle doldurur. Böylece uygulama ilk açıldığında boş bir ekranla karşılaşmayız.
Adım 3: Arama Fonksiyonunu Yazma
Kullanıcı TextField içerisine bir değer girdiğinde, bu değeri tumKullanicilar listesinde arayacak ve eşleşenleri gorunenListe içerisine aktaracak bir fonksiyon yazmalıyız.
void filtrele(String arananDeger) {
List sonuclar = [];
if (arananDeger.isEmpty) {
sonuclar = tumKullanicilar;
} else {
sonuclar = tumKullanicilar
.where((kullanici) => kullanici.toLowerCase().contains(arananDeger.toLowerCase()))
.toList();
}
setState(() {
gorunenListe = sonuclar;
});
}
Bu fonksiyon, toLowerCase() kullanarak büyük/küçük harf duyarlılığını ortadan kaldırır. contains() metodu, girilen metnin listedeki öğenin içinde geçip geçmediğini kontrol eder.
Adım 4: Arayüzü (UI) Tasarlama
Şimdi arama çubuğunu ve listeyi ekrana çizelim. TextField bileşeninin onChanged özelliğini kullanarak her tuş vuruşunda filtrele fonksiyonunu tetikleyeceğiz.
Column(
children: [
TextField(
onChanged: (deger) => filtrele(deger),
decoration: InputDecoration(
labelText: 'Ara',
prefixIcon: Icon(Icons.search),
),
),
Expanded(
child: ListView.builder(
itemCount: gorunenListe.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(gorunenListe[index]),
);
},
),
),
],
)
ListView.builder kullanımı, listeniz çok uzun olduğunda performans optimizasyonu sağlar. Sadece ekranda görünen öğeler render edilir.
Adım 5: Performans ve Güvenlik Uyarıları
Liste filtreleme işlemlerinde büyük veri setleri (10.000+ öğe) ile çalışıyorsanız, arama işlemini ana thread üzerinde yapmak arayüzde donmalara (jank) neden olabilir. Bu durumda compute fonksiyonu ile işlemi arka plana atmanız önerilir.
Kritik Uyarı: Kullanıcı girişlerini doğrudan veritabanı sorgularında kullanıyorsanız, mutlaka "input sanitization" (giriş temizleme) yaparak SQL injection veya XSS saldırılarına karşı önlem alın. Ancak bu rehberdeki gibi yerel listelerle yapılan aramalarda, Dart'ın tip güvenliği sayesinde bu tür riskler minimize edilmiştir.
Yöntemlerin Karşılaştırılması
| Yöntem | Avantajı | Dezavantajı |
|---|---|---|
| setState ile Filtreleme | Hızlı kurulum, basit yapı | Büyük verilerde performans düşebilir |
| Debounce ile Filtreleme | Gereksiz işlem yükünü azaltır | Ek kütüphane veya zamanlayıcı gerektirir |
| BLoC/Provider | Karmaşık projelerde ölçeklenebilir | Daha fazla kod yazımı gerektirir |
Sıkça Sorulan Sorular
Arama yaparken büyük/küçük harf duyarlılığını nasıl kapatırım?
Hem aranan metni hem de listedeki öğeleri toLowerCase() veya toUpperCase() metodları ile aynı formata getirerek karşılaştırma yapmalısınız.
Liste boşaldığında "Sonuç bulunamadı" mesajını nasıl gösteririm?
gorunenListe.isEmpty kontrolünü yaparak, listenin boş olduğu durumda bir Text veya Center widget'ı ile kullanıcıya bildirim verebilirsiniz.
Arama çubuğu her harfte API isteği atıyor, bunu nasıl engellerim?
Bu duruma "Debouncing" denir. Kullanıcı yazmayı bıraktıktan sonra belli bir süre (örneğin 500ms) bekleyip fonksiyonu tetiklemek için bir Timer kullanmanız gerekir.
Performans için en iyi yöntem nedir?
Listeniz çok uzunsa ListView.builder kullanmak ve arama mantığını compute fonksiyonu ile izole bir isolate üzerinde çalıştırmak en iyi performansı verir.
Filtreleme sırasında animasyon ekleyebilir miyim?
Evet, AnimatedList bileşenini kullanarak öğelerin eklenip çıkarılmasını daha şık bir şekilde yönetebilirsiniz.
Debounce Mekanizması ile API İsteklerini Optimize Etme
Kullanıcı arama çubuğuna her karakter girdiğinde API isteği tetiklemek, hem sunucu maliyetlerini artırır hem de uygulamanızda gereksiz ağ trafiğine yol açar. Bu durumu engellemek için Debounce (gecikmeli tetikleme) yöntemini kullanmalıyız. Bu yöntem, kullanıcı yazmayı bıraktıktan belirli bir süre sonra (örneğin 500ms) arama fonksiyonunu çalıştırır.
Bu işlem için dart:async kütüphanesindeki Timer sınıfından faydalanabiliriz:
import 'dart:async';
Timer? _debounce;
void onSearchChanged(String query) {
if (_debounce?.isActive ?? false) _debounce!.cancel();
_debounce = Timer(const Duration(milliseconds: 500), () {
// API isteği veya filtreleme fonksiyonu buraya gelecek
performSearch(query);
});
}
@override
void dispose() {
_debounce?.cancel();
super.dispose();
}
Filtreleme İşlemlerinde "Debounce" ve "Throttle" Farkı
Arama işlemlerinde genellikle Debounce tercih edilse de, bazı durumlarda Throttle kullanımı daha uygun olabilir. Aşağıdaki tablo, bu iki yaklaşımın farklarını anlamanıza yardımcı olacaktır:
| Özellik | Debounce | Throttle |
|---|---|---|
| Çalışma Prensibi | İşlem, son etkileşimden sonra belirlenen sürede tetiklenir. | İşlem, belirlenen sürede sadece bir kez tetiklenir. |
| Kullanım Alanı | Arama kutuları, form doğrulamaları. | Kaydırma (scroll) olayları, yeniden boyutlandırma. |
| Avantajı | Gereksiz istekleri tamamen engeller. | Sürekli bir akışta işlem sıklığını kontrol eder. |
Büyük Veri Setlerinde "Isolate" Kullanımı
Eğer binlerce öğeden oluşan bir listeyi filtreliyorsanız, ana iş parçacığı (Main Thread) donabilir ve uygulama "kilitlenmiş" gibi görünebilir. Bu tür ağır işlemleri ana iş parçacığından ayırmak için Isolate yapısını kullanmalısınız. Flutter'da compute fonksiyonu, bu işlemi kolaylaştırmak için tasarlanmıştır.
import 'package:flutter/foundation.dart';
// Filtreleme fonksiyonunu ayrı bir iş parçacığında çalıştırın
Future filterListInBackground(List data, String query) async {
return await compute(_filterTask, {'data': data, 'query': query});
}
List _filterTask(Map params) {
final data = params['data'] as List;
final query = params['query'] as String;
return data.where((item) => item.toLowerCase().contains(query.toLowerCase())).toList();
}
Bu yaklaşım, özellikle düşük donanımlı cihazlarda kullanıcı deneyimini (UX) ciddi oranda iyileştirir. Filtreleme işlemi arka planda tamamlandığında, sonuçları setState ile arayüze yansıtarak uygulamanın akıcılığını koruyabilirsiniz.
Sonuç
Flutter ile liste arama filtreleme, uygulamanızın kullanılabilirliğini artıran temel bir yetenektir. Bu rehberde öğrendiğiniz setState ve where metodları, küçük ve orta ölçekli projeler için oldukça yeterlidir. Bir sonraki adım olarak, bu filtreleme mantığını bir Provider veya Riverpod yapısına taşıyarak, uygulamanızın diğer sayfalarından da bu arama verisine erişmeyi deneyebilirsiniz.
Güvenlik Sorumluluk Reddi: Bu makalede yer alan kod örnekleri eğitim amaçlıdır. Üretim ortamındaki uygulamalarınızda, kullanıcı verilerinin gizliliği ve uygulama güvenliği için gerekli tüm testleri yapmalı ve güncel güvenlik standartlarına uymalısınız.


Yorumlar (0)
Yorum Yaz