Flutter: FutureBuilder ile Asenkron Listeleme
personAhmet Balaman
calendar_today
FlutterFutureBuilderAsyncAwaitWidgetAPI
FutureBuilder, Flutter'da asenkron işlemleri widget'lar içinde kullanmak için tasarlanmış özel bir yapıdır. Widget'lar doğrudan async özelliğine sahip olmadığından, asenkron fonksiyonları UI'da kullanmak için FutureBuilder gereklidir.
Neden FutureBuilder?
- async özelliği olan fonksiyonları kullanırken await ile sadece yapması gereken işlemi bitirene kadar çalışmasını sağlarız
- Fakat await kullanmak için async özelliği olan fonksiyon içinde olmamız gereklidir
- async özelliği olan fonksiyonu widget içinde kullanmak istediğimizde async özelliği olması gerekmektedir
- Widget'larda bu özellik yoktur!
- Widget içinde async özelliğini kullanmak için FutureBuilder yapısı gereklidir
Canlı Demo
Aşağıdaki interaktif örnekte bu widget'ı deneyebilirsiniz:
💡 Eğer yukarıdaki örnek açılmazsa, DartPad linkine tıklayarak yeni sekmede çalıştırabilirsiniz.
Temel Kullanım
FutureBuilder<String>(
future: fetchData(), // async fonksiyon
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator();
} else if (snapshot.hasError) {
return Text('Hata: ${snapshot.error}');
} else if (snapshot.hasData) {
return Text('Veri: ${snapshot.data}');
}
return Text('Veri bulunamadı');
},
)
// Async fonksiyon
Future<String> fetchData() async {
await Future.delayed(Duration(seconds: 2));
return 'Merhaba Flutter!';
}Önemli Özellikler
| Özellik | Açıklama |
|---|---|
future |
Beklenecek Future nesnesi |
builder |
UI oluşturan fonksiyon |
initialData |
Başlangıç verisi |
ConnectionState Durumları
| Durum | Açıklama |
|---|---|
none |
Future henüz atanmadı |
waiting |
Future çalışıyor, bekleniyor |
active |
Stream için aktif veri akışı |
done |
Future tamamlandı |
ListView ile FutureBuilder
API'den veri çekip liste oluşturma:
class MyListPage extends StatelessWidget {
Future<List<String>> fetchItems() async {
// API çağrısı simülasyonu
await Future.delayed(Duration(seconds: 2));
return ['Flutter', 'Dart', 'Firebase', 'Android', 'iOS'];
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Liste')),
body: FutureBuilder<List<String>>(
future: fetchItems(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
}
if (snapshot.hasError) {
return Center(child: Text('Hata: ${snapshot.error}'));
}
if (!snapshot.hasData || snapshot.data!.isEmpty) {
return Center(child: Text('Veri bulunamadı'));
}
final items = snapshot.data!;
return ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return ListTile(
leading: CircleAvatar(child: Text('${index + 1}')),
title: Text(items[index]),
);
},
);
},
),
);
}
}GridView ile FutureBuilder
Aynı çalışmayı GridView.builder içinde de kullanabiliriz:
class MyGridPage extends StatelessWidget {
Future<List<Map<String, dynamic>>> fetchProducts() async {
await Future.delayed(Duration(seconds: 2));
return [
{'name': 'Ürün 1', 'price': 99, 'color': Colors.red},
{'name': 'Ürün 2', 'price': 149, 'color': Colors.blue},
{'name': 'Ürün 3', 'price': 199, 'color': Colors.green},
{'name': 'Ürün 4', 'price': 249, 'color': Colors.orange},
{'name': 'Ürün 5', 'price': 79, 'color': Colors.purple},
{'name': 'Ürün 6', 'price': 129, 'color': Colors.teal},
];
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Ürünler')),
body: FutureBuilder<List<Map<String, dynamic>>>(
future: fetchProducts(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
}
if (snapshot.hasError) {
return Center(child: Text('Hata: ${snapshot.error}'));
}
if (!snapshot.hasData || snapshot.data!.isEmpty) {
return Center(child: Text('Ürün bulunamadı'));
}
final products = snapshot.data!;
return GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
mainAxisSpacing: 8,
crossAxisSpacing: 8,
),
padding: EdgeInsets.all(8),
itemCount: products.length,
itemBuilder: (context, index) {
final product = products[index];
return Card(
color: product['color'],
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
product['name'],
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 8),
Text(
'₺${product['price']}',
style: TextStyle(color: Colors.white),
),
],
),
),
);
},
);
},
),
);
}
}API'den Gerçek Veri Çekme
import 'dart:convert';
import 'package:http/http.dart' as http;
class User {
final int id;
final String name;
final String email;
User({required this.id, required this.name, required this.email});
factory User.fromJson(Map<String, dynamic> json) {
return User(
id: json['id'],
name: json['name'],
email: json['email'],
);
}
}
class UsersPage extends StatelessWidget {
Future<List<User>> fetchUsers() async {
final response = await http.get(
Uri.parse('https://jsonplaceholder.typicode.com/users'),
);
if (response.statusCode == 200) {
final List<dynamic> data = json.decode(response.body);
return data.map((json) => User.fromJson(json)).toList();
} else {
throw Exception('Kullanıcılar yüklenemedi');
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Kullanıcılar')),
body: FutureBuilder<List<User>>(
future: fetchUsers(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
}
if (snapshot.hasError) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.error, size: 64, color: Colors.red),
SizedBox(height: 16),
Text('Hata: ${snapshot.error}'),
],
),
);
}
final users = snapshot.data!;
return ListView.builder(
itemCount: users.length,
itemBuilder: (context, index) {
final user = users[index];
return Card(
margin: EdgeInsets.symmetric(horizontal: 16, vertical: 4),
child: ListTile(
leading: CircleAvatar(
child: Text(user.name[0]),
),
title: Text(user.name),
subtitle: Text(user.email),
trailing: Icon(Icons.chevron_right),
),
);
},
);
},
),
);
}
}FutureBuilder Best Practices
1. Future'ı State'te Saklama
class MyPage extends StatefulWidget {
@override
_MyPageState createState() => _MyPageState();
}
class _MyPageState extends State<MyPage> {
late Future<List<String>> _futureData;
@override
void initState() {
super.initState();
_futureData = fetchData(); // Future'ı bir kez oluştur
}
Future<List<String>> fetchData() async {
await Future.delayed(Duration(seconds: 2));
return ['Veri 1', 'Veri 2', 'Veri 3'];
}
@override
Widget build(BuildContext context) {
return FutureBuilder<List<String>>(
future: _futureData, // Aynı Future'ı kullan
builder: (context, snapshot) {
// ...
},
);
}
}2. Yenileme Özelliği
class _MyPageState extends State<MyPage> {
late Future<List<String>> _futureData;
@override
void initState() {
super.initState();
_loadData();
}
void _loadData() {
setState(() {
_futureData = fetchData();
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Veri'),
actions: [
IconButton(
icon: Icon(Icons.refresh),
onPressed: _loadData, // Yenile
),
],
),
body: FutureBuilder<List<String>>(
future: _futureData,
builder: (context, snapshot) {
// ...
},
),
);
}
}Özet
- FutureBuilder: Widget'larda async kullanımı için
- ConnectionState: Bekleme, hata, tamamlanma durumları
- snapshot.hasData: Veri kontrolü
- snapshot.hasError: Hata kontrolü
- ListView.builder + FutureBuilder: Dinamik asenkron liste
- GridView.builder + FutureBuilder: Dinamik asenkron grid
FutureBuilder, API çağrıları ve veritabanı işlemleri gibi asenkron operasyonları Flutter UI'da kullanmak için vazgeçilmez bir yapıdır.