Flutter: LayoutBuilder ile Ekran Boyutuna Göre Tasarım
Flutter her ne kadar iOS ve Android'de çalışabiliyor olsa da, varsayılan olarak dinamik değildir. Farklı ekran boyutlarına uyum sağlamak için LayoutBuilder gibi araçlar kullanarak responsive tasarımlar oluşturmalıyız.
Çoklu Ekran Desteği Neden Önemli?
Flutter uygulamaları şu cihazlarda çalışabilir:
- Küçük ekranlı telefonlar (iPhone SE)
- Büyük ekranlı telefonlar (iPhone Pro Max)
- Tabletler (iPad)
- Masaüstü bilgisayarlar
- Web tarayıcıları
Her ekran boyutunda aynı sabit değerleri kullanırsanız, bazı cihazlarda tasarımınız bozuk görünebilir.
Canlı Demo: LayoutBuilder Responsive Tasarım
LayoutBuilder ile responsive tasarım örneklerini dikey çizgiden genişleterek deneyin:
LayoutBuilder Nedir?
LayoutBuilder, parent widget'ın boyut kısıtlamalarını (constraints) sağlayan bir widget'tır. Bu bilgi ile ekran boyutuna göre farklı tasarımlar oluşturabilirsiniz.
Temel Kullanım
LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
return Container(
width: constraints.maxWidth,
height: constraints.maxHeight,
child: Text('Mevcut genişlik: ${constraints.maxWidth}'),
);
},
)BoxConstraints Özellikleri
maxWidth: Mevcut maksimum genişlikmaxHeight: Mevcut maksimum yükseklikminWidth: Minimum genişlikminHeight: Minimum yükseklik
1. Yöntem: Her Ekrana Göre Tasarım Yapmak
Ekran boyutunu kontrol ederek farklı layout'lar gösterebilirsiniz.
Breakpoint'ler Tanımlama
class ScreenSize {
static bool isSmall(double width) => width < 600;
static bool isMedium(double width) => width >= 600 && width < 900;
static bool isLarge(double width) => width >= 900;
}Responsive Widget Örneği
class ResponsiveLayout extends StatelessWidget {
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth < 600) {
// Küçük ekran (Mobil)
return _buildMobileLayout();
} else if (constraints.maxWidth < 900) {
// Orta ekran (Tablet)
return _buildTabletLayout();
} else {
// Büyük ekran (Desktop)
return _buildDesktopLayout();
}
},
);
}
Widget _buildMobileLayout() {
return Column(
children: [
AppBar(title: Text('Mobil Görünüm')),
Expanded(
child: ListView(
children: [
_buildCard('Öğe 1'),
_buildCard('Öğe 2'),
_buildCard('Öğe 3'),
],
),
),
],
);
}
Widget _buildTabletLayout() {
return Row(
children: [
// Yan panel
Container(
width: 200,
color: Colors.grey[200],
child: ListView(
children: [
ListTile(title: Text('Menü 1')),
ListTile(title: Text('Menü 2')),
],
),
),
// Ana içerik
Expanded(
child: Column(
children: [
AppBar(title: Text('Tablet Görünüm')),
Expanded(
child: GridView.count(
crossAxisCount: 2,
children: [
_buildCard('Öğe 1'),
_buildCard('Öğe 2'),
_buildCard('Öğe 3'),
_buildCard('Öğe 4'),
],
),
),
],
),
),
],
);
}
Widget _buildDesktopLayout() {
return Row(
children: [
// Sol panel
Container(
width: 250,
color: Colors.grey[200],
child: ListView(
children: [
ListTile(title: Text('Menü 1')),
ListTile(title: Text('Menü 2')),
ListTile(title: Text('Menü 3')),
],
),
),
// Ana içerik
Expanded(
child: Column(
children: [
AppBar(title: Text('Desktop Görünüm')),
Expanded(
child: GridView.count(
crossAxisCount: 3,
children: [
_buildCard('Öğe 1'),
_buildCard('Öğe 2'),
_buildCard('Öğe 3'),
_buildCard('Öğe 4'),
_buildCard('Öğe 5'),
_buildCard('Öğe 6'),
],
),
),
],
),
),
// Sağ panel
Container(
width: 250,
color: Colors.grey[100],
child: Column(
children: [
Padding(
padding: EdgeInsets.all(16),
child: Text('Yan Panel'),
),
],
),
),
],
);
}
Widget _buildCard(String title) {
return Card(
margin: EdgeInsets.all(8),
child: Padding(
padding: EdgeInsets.all(16),
child: Center(child: Text(title)),
),
);
}
}Resim Çözünürlüğü Seçimi
Ekran boyutuna göre farklı çözünürlükteki resimleri kullanabilirsiniz:
Resimleri İsimlendirme
assets/
└── images/
├── banner_low.jpg // Küçük ekranlar için
├── banner_medium.jpg // Orta ekranlar için
└── banner_high.jpg // Büyük ekranlar içinKullanımı
LayoutBuilder(
builder: (context, constraints) {
String imagePath;
if (constraints.maxWidth < 600) {
imagePath = 'assets/images/banner_low.jpg';
} else if (constraints.maxWidth < 1200) {
imagePath = 'assets/images/banner_medium.jpg';
} else {
imagePath = 'assets/images/banner_high.jpg';
}
return Image.asset(
imagePath,
width: constraints.maxWidth,
fit: BoxFit.cover,
);
},
)Fayda: Küçük ekranlarda küçük resim yükleyerek performans artışı sağlarsınız.
Column Count Değiştirme
GridView'da ekran genişliğine göre sütun sayısını ayarlama:
LayoutBuilder(
builder: (context, constraints) {
int crossAxisCount;
if (constraints.maxWidth < 600) {
crossAxisCount = 2; // Mobilde 2 sütun
} else if (constraints.maxWidth < 900) {
crossAxisCount = 3; // Tablette 3 sütun
} else {
crossAxisCount = 4; // Desktop'ta 4 sütun
}
return GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: crossAxisCount,
crossAxisSpacing: 8,
mainAxisSpacing: 8,
),
itemCount: 20,
itemBuilder: (context, index) {
return Card(
child: Center(child: Text('Öğe ${index + 1}')),
);
},
);
},
)2. Yöntem: Tasarımı Ekrana Göre Oranlama
Sabit değerler yerine yüzdelik oranlar kullanarak responsive tasarım:
LayoutBuilder(
builder: (context, constraints) {
final screenWidth = constraints.maxWidth;
final screenHeight = constraints.maxHeight;
return Container(
width: screenWidth * 0.8, // Ekranın %80'i genişliğinde
height: screenHeight * 0.5, // Ekranın %50'si yüksekliğinde
color: Colors.blue,
child: Center(
child: Text(
'Ekranın %80 x %50\'si',
style: TextStyle(color: Colors.white),
),
),
);
},
)Padding'i Oranlama
LayoutBuilder(
builder: (context, constraints) {
final padding = constraints.maxWidth * 0.05; // Ekranın %5'i
return Padding(
padding: EdgeInsets.all(padding),
child: Text('Oransal padding'),
);
},
)Font Boyutunu Oranlama
LayoutBuilder(
builder: (context, constraints) {
final fontSize = constraints.maxWidth * 0.05; // Genişliğin %5'i
return Text(
'Responsive metin',
style: TextStyle(fontSize: fontSize),
);
},
)MediaQuery ile Birlikte Kullanım
LayoutBuilder yerel constraints verir, MediaQuery tüm ekran bilgisini verir:
LayoutBuilder(
builder: (context, constraints) {
final mediaQuery = MediaQuery.of(context);
final screenWidth = mediaQuery.size.width;
final screenHeight = mediaQuery.size.height;
final localWidth = constraints.maxWidth;
return Column(
children: [
Text('Ekran genişliği: $screenWidth'),
Text('Local genişlik: $localWidth'),
Text('Ekran yüksekliği: $screenHeight'),
],
);
},
)Ne zaman hangisi?
- LayoutBuilder: Parent widget'a göre boyutlandırma
- MediaQuery: Tüm ekrana göre boyutlandırma
Orientation Kontrolü
Dikey (portrait) veya yatay (landscape) modda farklı tasarımlar:
LayoutBuilder(
builder: (context, constraints) {
final isPortrait = constraints.maxHeight > constraints.maxWidth;
if (isPortrait) {
return Column(
children: [
Text('Dikey Mod'),
Expanded(child: Container(color: Colors.blue)),
],
);
} else {
return Row(
children: [
Text('Yatay Mod'),
Expanded(child: Container(color: Colors.green)),
],
);
}
},
)Responsive Widget Helper
Kendi responsive widget'ınızı oluşturabilirsiniz:
class ResponsiveBuilder extends StatelessWidget {
final Widget Function(BuildContext context, ScreenType screenType) builder;
const ResponsiveBuilder({Key? key, required this.builder}) : super(key: key);
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
ScreenType screenType;
if (constraints.maxWidth < 600) {
screenType = ScreenType.mobile;
} else if (constraints.maxWidth < 900) {
screenType = ScreenType.tablet;
} else {
screenType = ScreenType.desktop;
}
return builder(context, screenType);
},
);
}
}
enum ScreenType { mobile, tablet, desktop }Kullanımı
ResponsiveBuilder(
builder: (context, screenType) {
switch (screenType) {
case ScreenType.mobile:
return Text('Mobil Görünüm');
case ScreenType.tablet:
return Text('Tablet Görünüm');
case ScreenType.desktop:
return Text('Desktop Görünüm');
}
},
)Performans İpuçları
Gereksiz rebuild'lerden kaçının: LayoutBuilder her boyut değişiminde rebuild eder.
Const kullanın: Değişmeyen widget'ları const yapın.
Heavy computation'ı önleyin: Builder içinde ağır hesaplamalardan kaçının.
Cache kullanın: Hesaplanan değerleri cache'leyin.
Özet
LayoutBuilder ile responsive tasarım:
- Ekran boyutunu algılama:
constraints.maxWidthveconstraints.maxHeight - Breakpoint'lerle tasarım: Mobil, tablet, desktop
- Oransal boyutlandırma: Yüzdelik değerler kullanma
- Farklı resim çözünürlükleri: Performans optimizasyonu
- Dinamik layout: Column count, padding, font size
Best Practices:
- Standart breakpoint'ler kullanın (600px, 900px)
- Oransal değerler tercih edin
- Her cihazda test edin
- Performansa dikkat edin
Alternatif Paketler:
responsive_framework: Hazır responsive çözümlerflutter_screenutil: Ekran adaptasyonusizer: Responsive boyutlandırma
LayoutBuilder, Flutter'da profesyonel ve her cihazda mükemmel görünen uygulamalar yapmanın temelidir!