Flutter: Expanded Widget ile Alan Genişletme
Son güncelleme:
5 dk okuma

Expanded, bir Row veya Column'un içindeki çocuğu, diğer çocuklardan arta kalan alanın tamamını kaplayacak şekilde büyütür. Bir metnin ikonla buton arasında kalan yeri doldurması, bir listenin alt bardan geriye kalan yüksekliği alması, iki panelin ekranı 2:1 paylaşması hep bu widget'ın işidir. Bu yazı Expanded'ın kendisine odaklanıyor: flex hesabı, iç içe kullanım, neden yalnızca Row, Column ve Flex içinde çalıştığı ve konsolda en sık gördüğünüz üç hata. Hangi durumda Expanded yerine Flexible ya da Spacer seçileceğini ve üçünü birlikte kullanan düzen tariflerini Expanded, Flexible ve Spacer karşılaştırmasında ayrıca anlattım; burada yalnızca Expanded'ın kendi davranışı var.
Canlı Demo: Expanded Widget Örnekleri
Expanded ve flex kullanımlarını interaktif olarak deneyin:
💡 Eğer yukarıdaki örnek açılmazsa, DartPad linkine tıklayarak yeni sekmede çalıştırabilirsiniz.
Temel Kullanım
Row içinde Expanded yatay eksende, Column içinde dikey eksende çalışır. Sabit boyutlu çocuklar önce ölçülür, geriye ne kaldıysa Expanded'a verilir:
Row(
children: [
Container(width: 50, color: Colors.red),
Expanded(
child: Container(color: Colors.blue, child: const Text('Genişledim')),
),
Container(width: 50, color: Colors.green),
],
)Mavi container, kırmızı ve yeşilden sonra kalan tüm genişliği alır. Aynı kodu Column'a taşıyıp width yerine height yazdığınızda mavi alan kalan yüksekliği doldurur. Expanded çapraz eksene karışmaz: Row içindeki Expanded'ın yüksekliği Row'un crossAxisAlignment kuralına göre belirlenir.
Flex Hesabı
Birden fazla Expanded varsa kalan alan flex değerlerine oranla bölünür. Hesap iki adımdır: önce sabit çocuklar düşülür, sonra kalan alan flex toplamına bölünüp her çocuğa payı kadar verilir.
Row(
children: [
const SizedBox(width: 100, child: Text('Sabit')),
Expanded(
flex: 1,
child: Container(color: Colors.red, child: const Text('1 pay')),
),
Expanded(
flex: 3,
child: Container(color: Colors.blue, child: const Text('3 pay')),
),
],
)Ekran 500 piksel genişliğindeyse önce 100 piksel sabit çıkar, kalan 400 piksel 1+3 = 4 paya bölünür: kırmızı 100, mavi 300 piksel olur. Sık yapılan bir yanılgı flex: 1 ve flex: 3 değerlerini toplam genişliğin dörtte biri ve dörtte üçü sanmaktır; oran her zaman kalan alan üzerinden hesaplanır. flex yazılmazsa değer 1'dir, yani iki çıplak Expanded alanı yarı yarıya paylaşır.
Flex değerleri birbirine göre anlamlıdır: flex: 2 ve flex: 4 ile flex: 1 ve flex: 2 aynı sonucu verir. Okunabilirlik için en küçük tam sayıları kullanın.
Expanded Aslında Nedir?
Expanded, Flexible(fit: FlexFit.tight) widget'ının kısa yazımıdır. tight demek, çocuğa "tam olarak bu kadar ol" emri vermektir; çocuk daha küçük olmak istese de kalan alanın tamamını kaplar. Flexible'ın varsayılanı olan FlexFit.loose ise "en fazla bu kadar ol" der. Bu ayrım, Expanded'a width: 100 verilmiş bir Container koyduğunuzda genişliğin neden yok sayıldığını da açıklar: sıkı kısıt Container'ın isteğini ezer.
Bir başka sonuç: Expanded, Row'a mainAxisSize: MainAxisSize.min verilmiş olsa bile Row'u tam genişliğe çıkarır, çünkü kalan alanın tamamını istemektedir. Bu etkileşimi mainAxisSize yazısında ayrıca ele aldım.
İç İçe Expanded
Expanded'ın çocuğu bir Row veya Column olabilir ve onun da içinde yeni Expanded'lar bulunabilir. Her Expanded yalnızca doğrudan üstündeki Flex widget'ına bakar:
Row(
children: [
Expanded(
flex: 2,
child: Column(
children: [
Expanded(child: Container(color: Colors.red)),
Expanded(child: Container(color: Colors.blue)),
],
),
),
Expanded(
child: Container(color: Colors.green),
),
],
)Dıştaki Row genişliği 2:1 paylaştırır; soldaki Column kendi payının yüksekliğini iki eşit parçaya böler. İçteki Expanded'ların dıştaki Row ile ilgisi yoktur. Bu yapı çalışır çünkü dıştaki Row ekrandan sınırlı bir yükseklik alır ve bu yükseklik Column'a aktarılır. Yükseklik sınırsız olsaydı içteki Expanded'lar hata verirdi; aşağıda bu var.
Ne Zaman Kullanılır, Ne Zaman Kullanılmaz?
Expanded'ı bir Row veya Column içinde, bir çocuğun "geriye ne kalırsa" kuralıyla boyutlanması gerektiğinde kullanın: uzun metnin ikon ile buton arasını doldurması, içerik alanının alt barın üstündeki tüm yüksekliği alması, sütunların ekranı oranla paylaşması.
Şu durumlarda başka bir araç gerekir:
- Çocuğun kendi boyutunda kalması ama sığmadığında küçülmesi isteniyorsa
Flexible(varsayılanloose). Sohbet balonu gibi içeriğe sarılan düzenlerde Expanded metni gereksiz yere tam genişliğe çeker. - Yalnızca boşluk istiyorsanız
Spacer. İçi boş birExpanded(child: SizedBox())yazmak yerineSpacer(flex: 2)aynı işi okunaklı yapar; ayrıntısı Spacer yazısında. - Row veya Column'un dışında tam genişlik istiyorsanız
SizedBox(width: double.infinity)ya da Column'acrossAxisAlignment: CrossAxisAlignment.stretch. Expanded, Flex olmayan bir üst widget'ın içinde hata verir. - Üst widget'ın belirli bir yüzdesi gerekiyorsa
FractionallySizedBox(widthFactor: 0.6). Expanded oranı yalnızca kardeşlerine göre kurar. - Kaydırılabilir bir alanda ana eksende sınır yoktur; orada Expanded çalışmaz. Sabit yükseklik verin ya da listeyi doğrudan kaydırılabilir yapın.
Sık Yapılan Hatalar
1. ScrollView içindeki Column'da Expanded
SingleChildScrollView(
child: Column(
children: [
const Text('Başlık'),
Expanded(child: ListView(children: const [Text('Satır')])), // Hata
],
),
)Belirti: RenderFlex children have non-zero flex but incoming height constraints are unbounded. ScrollView, Column'a sonsuz yükseklik sunar ve sonsuzun "kalanı" hesaplanamaz. Üç çıkış yolu var: Expanded'ı kaldırıp içteki listeye SizedBox(height: 300) gibi sabit bir yükseklik vermek; içteki listeye shrinkWrap: true ve physics: NeverScrollableScrollPhysics() verip dıştaki kaydırmayı kullanmak; ya da Column'u ScrollView'dan çıkarıp yalnızca listeyi kaydırılabilir yapmak. Uzun listelerde üçüncüsü en verimlisidir; kaydırma seçenekleri için ScrollView ve liste widget'ları yazısına bakabilirsiniz.
2. Expanded ile Row arasına başka widget koymak
Row(
children: [
Padding(
padding: const EdgeInsets.all(8),
child: Expanded(child: Text('Metin')), // Hata
),
],
)Belirti: Incorrect use of ParentDataWidget. ile başlayan uzun bir hata. Expanded bir "parent data" widget'ıdır; flex bilgisini doğrudan üstündeki Row'a yazar. Arada Padding, Container veya Center varsa bilgi yanlış yere gider. Sırayı ters çevirin: önce Expanded, onun içinde Padding.
3. Expanded içinde sabit genişlik vermek
Belirti: Expanded(child: Container(width: 120, ...)) yazdınız, container yine kalan alanın tamamına yayıldı. tight kısıt Container'ın genişlik isteğini geçersiz kılar. Sabit genişlik istiyorsanız Expanded'ı kaldırın; "en fazla 120 olsun" istiyorsanız Flexible ile ConstrainedBox(constraints: BoxConstraints(maxWidth: 120)) kullanın.
4. Uzun metinde Expanded'ı unutmak
Belirti: Sağ kenarda sarı-siyah şerit ve A RenderFlex overflowed by ... pixels on the right. Row, metnin ne kadar yer isteyeceğini sınırlamaz. Metni Expanded ile sarıp overflow: TextOverflow.ellipsis ekleyin:
Row(
children: [
const Icon(Icons.label),
const SizedBox(width: 8),
Expanded(
child: Text(
'Satıra sığmayacak kadar uzun bir etiket metni',
overflow: TextOverflow.ellipsis,
),
),
],
)Mini Senaryo: Ödeme Ekranı
Bir ödeme ekranı düşünün: üstte kaydırılabilir form alanları, altta her zaman görünen "Geri" ve "Ödemeyi tamamla" butonları. Column'daki Expanded formu kalan yüksekliğe yayar; Row'daki iki Expanded butonları 1:2 oranında paylaştırır:
class CheckoutScreen extends StatelessWidget {
const CheckoutScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Ödeme')),
body: Column(
children: [
Expanded(
child: ListView(
padding: const EdgeInsets.all(16),
children: const [
TextField(decoration: InputDecoration(labelText: 'Kart numarası')),
SizedBox(height: 12),
TextField(decoration: InputDecoration(labelText: 'Son kullanma')),
SizedBox(height: 12),
TextField(decoration: InputDecoration(labelText: 'CVC')),
],
),
),
SafeArea(
top: false,
child: Padding(
padding: const EdgeInsets.all(16),
child: Row(
children: [
Expanded(
child: OutlinedButton(
onPressed: () => Navigator.pop(context),
child: const Text('Geri'),
),
),
const SizedBox(width: 12),
Expanded(
flex: 2,
child: FilledButton(
onPressed: () {},
child: const Text('Ödemeyi tamamla'),
),
),
],
),
),
),
],
),
);
}
}Bu yapı çalışır çünkü Scaffold gövdeye sınırlı bir yükseklik verir; Column önce alt butonların yüksekliğini ölçer, kalanı Expanded'a bırakır. Klavye açıldığında gövde küçülür, Expanded'ın alanı da otomatik daralır ve liste kaydırılabilir kalır. Aynı listeyi bir SingleChildScrollView içine koysaydınız birinci hatayı alırdınız. Buton satırında SizedBox(width: 12) sabit olduğu için hesaptan önce düşülür; kalan genişlik üçe bölünür ve "Ödemeyi tamamla" iki payı alır.
Sık Sorulan Sorular
Expanded ile Flexible arasındaki fark nedir?
Expanded, Flexible(fit: FlexFit.tight) ile aynıdır: çocuk kalan alanın tamamını kaplamak zorundadır. Flexible'ın varsayılan loose değeri ise çocuğun daha küçük kalmasına izin verir.
Expanded neden Container içinde çalışmıyor?
Expanded yalnızca Row, Column ve Flex'in doğrudan çocuğu olabilir. Başka bir widget'ın içinde Incorrect use of ParentDataWidget hatası alırsınız; tam genişlik için SizedBox(width: double.infinity) kullanın.
"incoming height constraints are unbounded" hatası nasıl çözülür?
Expanded, ana eksende sınırsız kısıt sunan bir üst widget'ın (genellikle SingleChildScrollView veya ListView) içindeki Column'dadır. Expanded'ı kaldırıp sabit yükseklik verin ya da Column'u kaydırma alanından çıkarın.
flex değeri toplam alanın yüzdesi midir?
Hayır. Sabit boyutlu kardeşler önce düşülür, kalan alan flex toplamına bölünür. Row'da 100 piksellik sabit bir çocuk varsa flex: 1 ve flex: 1 toplamın değil, kalanın yarısını alır.
İlgili Yazılar
Flutter: Spacer Widget ile Esnek Boşluklar
Spacer'ın içinde ne olduğu, flex ile boşluk oranlama, SizedBox ve mainAxisAlignment ile farkı, kaydırılabilir alanlarda neden hata verdiği ve çözümü.
Flutter: mainAxisSize ile Alan ve Hizalama Kontrolü
mainAxisSize'ın max ve min farkı, kısıtlarla ilişkisi, mainAxisAlignment'ın neden etkisiz kaldığı ve dialog, bottom sheet, buton grubu tarifleri.
Flutter: Expanded, Flexible ve Spacer Farkı ve Tarifler
Expanded, Flexible ve Spacer arasındaki fark, FlexFit.loose ile tight ayrımı ve üçünü birlikte kullanan dört tarif: araç çubuğu, kart, form, bölünmüş ekran.