Flutter: MediaQuery ile Ekrana Göre Oranlama
MediaQuery, Flutter'da cihazın ekran özellikleri hakkında detaylı bilgi veren güçlü bir araçtır. Ekran boyutu, yoğunluğu, yönelimi ve daha fazlası hakkında bilgi alarak responsive tasarımlar oluşturabilirsiniz.
MediaQuery Nedir?
MediaQuery, BuildContext'ten erişilen ve cihazın medya özelliklerini sağlayan bir sınıftır. Ekran genişliği, yüksekliği, pixel yoğunluğu, safe area gibi bilgileri sunar.
Temel Kullanım
Widget build(BuildContext context) {
final mediaQuery = MediaQuery.of(context);
final screenWidth = mediaQuery.size.width;
final screenHeight = mediaQuery.size.height;
return Container(
width: screenWidth * 0.8, // Ekranın %80'i
height: screenHeight * 0.5, // Ekranın %50'si
color: Colors.blue,
child: Center(
child: Text('$screenWidth x $screenHeight'),
),
);
}MediaQuery Özellikleri
size (Ekran Boyutu)
final size = MediaQuery.of(context).size;
final width = size.width; // Ekran genişliği (dp/logical pixels)
final height = size.height; // Ekran yüksekliği (dp/logical pixels)
print('Ekran: ${width}x${height}');devicePixelRatio (Piksel Yoğunluğu)
final pixelRatio = MediaQuery.of(context).devicePixelRatio;
// Fiziksel piksel sayısı
final physicalWidth = size.width * pixelRatio;
final physicalHeight = size.height * pixelRatio;
print('Piksel yoğunluğu: $pixelRatio');
print('Fiziksel boyut: ${physicalWidth}x${physicalHeight}');orientation (Yönelim)
final orientation = MediaQuery.of(context).orientation;
if (orientation == Orientation.portrait) {
print('Dikey mod');
} else {
print('Yatay mod');
}padding (Safe Area)
final padding = MediaQuery.of(context).padding;
print('Üst padding: ${padding.top}'); // Notch, status bar
print('Alt padding: ${padding.bottom}'); // Home indicator
print('Sol padding: ${padding.left}');
print('Sağ padding: ${padding.right}');viewInsets (Klavye)
final viewInsets = MediaQuery.of(context).viewInsets;
final keyboardHeight = viewInsets.bottom;
if (keyboardHeight > 0) {
print('Klavye açık: $keyboardHeight px');
} else {
print('Klavye kapalı');
}platformBrightness (Tema)
final brightness = MediaQuery.of(context).platformBrightness;
if (brightness == Brightness.dark) {
print('Dark mode');
} else {
print('Light mode');
}Oransal Boyutlandırma Örnekleri
Genişlik Oranı
Widget build(BuildContext context) {
final screenWidth = MediaQuery.of(context).size.width;
return Container(
width: screenWidth * 0.9, // Ekranın %90'ı
padding: EdgeInsets.all(16),
child: Text('İçerik'),
);
}Yükseklik Oranı
Widget build(BuildContext context) {
final screenHeight = MediaQuery.of(context).size.height;
return Container(
height: screenHeight * 0.3, // Ekranın %30'u
color: Colors.blue,
child: Center(child: Text('Banner')),
);
}Padding Oranı
Widget build(BuildContext context) {
final screenWidth = MediaQuery.of(context).size.width;
final padding = screenWidth * 0.05; // Ekranın %5'i
return Padding(
padding: EdgeInsets.all(padding),
child: Text('Oransal padding'),
);
}Font Boyutu Oranı
Widget build(BuildContext context) {
final screenWidth = MediaQuery.of(context).size.width;
final fontSize = screenWidth * 0.06; // Genişliğin %6'sı
return Text(
'Responsive Başlık',
style: TextStyle(fontSize: fontSize),
);
}Pratik Kullanım Örnekleri
Örnek 1: Tam Ekran Container
Widget build(BuildContext context) {
final size = MediaQuery.of(context).size;
return Container(
width: size.width,
height: size.height,
color: Colors.blue,
child: Center(
child: Text(
'Tam Ekran',
style: TextStyle(color: Colors.white, fontSize: 24),
),
),
);
}Örnek 2: Klavye Açıkken Scroll
Widget build(BuildContext context) {
final keyboardHeight = MediaQuery.of(context).viewInsets.bottom;
return Scaffold(
body: SingleChildScrollView(
padding: EdgeInsets.only(bottom: keyboardHeight),
child: Column(
children: [
TextField(decoration: InputDecoration(hintText: 'İsim')),
SizedBox(height: 16),
TextField(decoration: InputDecoration(hintText: 'Email')),
SizedBox(height: 16),
ElevatedButton(
onPressed: () {},
child: Text('Gönder'),
),
],
),
),
);
}Örnek 3: Safe Area Uyumlu Layout
Widget build(BuildContext context) {
final padding = MediaQuery.of(context).padding;
final safeHeight = MediaQuery.of(context).size.height -
padding.top - padding.bottom;
return Column(
children: [
Container(
height: padding.top,
color: Colors.blue,
),
Container(
height: safeHeight,
child: Center(child: Text('İçerik')),
),
Container(
height: padding.bottom,
color: Colors.blue,
),
],
);
}Örnek 4: Responsive Grid
Widget build(BuildContext context) {
final screenWidth = MediaQuery.of(context).size.width;
int crossAxisCount;
if (screenWidth < 600) {
crossAxisCount = 2; // Mobil
} else if (screenWidth < 900) {
crossAxisCount = 3; // Tablet
} else {
crossAxisCount = 4; // Desktop
}
return GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: crossAxisCount,
crossAxisSpacing: 8,
mainAxisSpacing: 8,
),
itemCount: 20,
itemBuilder: (context, index) {
return Card(child: Center(child: Text('${index + 1}')));
},
);
}Örnek 5: Dinamik Card Boyutu
Widget build(BuildContext context) {
final screenWidth = MediaQuery.of(context).size.width;
final cardWidth = screenWidth > 600 ? 400.0 : screenWidth * 0.9;
return Center(
child: Card(
child: Container(
width: cardWidth,
padding: EdgeInsets.all(16),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text('Başlık', style: TextStyle(fontSize: 20)),
SizedBox(height: 8),
Text('İçerik metni...'),
],
),
),
),
);
}Orientation'a Göre Layout
Widget build(BuildContext context) {
final orientation = MediaQuery.of(context).orientation;
if (orientation == Orientation.portrait) {
// Dikey mod
return Column(
children: [
Container(height: 200, color: Colors.blue),
Expanded(child: Container(color: Colors.green)),
],
);
} else {
// Yatay mod
return Row(
children: [
Container(width: 200, color: Colors.blue),
Expanded(child: Container(color: Colors.green)),
],
);
}
}Platform Brightness'a Göre Tema
Widget build(BuildContext context) {
final brightness = MediaQuery.of(context).platformBrightness;
final isDark = brightness == Brightness.dark;
return Container(
color: isDark ? Colors.black : Colors.white,
child: Text(
'Otomatik tema',
style: TextStyle(
color: isDark ? Colors.white : Colors.black,
),
),
);
}Responsive Helper Fonksiyonları
Kendi yardımcı fonksiyonlarınızı oluşturabilirsiniz:
class ScreenUtil {
static double screenWidth(BuildContext context) {
return MediaQuery.of(context).size.width;
}
static double screenHeight(BuildContext context) {
return MediaQuery.of(context).size.height;
}
static double widthPercent(BuildContext context, double percent) {
return screenWidth(context) * (percent / 100);
}
static double heightPercent(BuildContext context, double percent) {
return screenHeight(context) * (percent / 100);
}
static bool isSmallScreen(BuildContext context) {
return screenWidth(context) < 600;
}
static bool isMediumScreen(BuildContext context) {
final width = screenWidth(context);
return width >= 600 && width < 900;
}
static bool isLargeScreen(BuildContext context) {
return screenWidth(context) >= 900;
}
}Kullanımı
Widget build(BuildContext context) {
final width = ScreenUtil.widthPercent(context, 80); // %80
final height = ScreenUtil.heightPercent(context, 30); // %30
return Container(
width: width,
height: height,
color: Colors.blue,
child: Center(child: Text('Helper kullanımı')),
);
}MediaQuery vs LayoutBuilder
MediaQuery
// Tüm ekran bilgisi
final screenWidth = MediaQuery.of(context).size.width;- Cihazın tam ekran boyutunu verir
- Her yerden erişilebilir
- BuildContext gerektirir
LayoutBuilder
// Parent widget'ın boyutu
LayoutBuilder(
builder: (context, constraints) {
final localWidth = constraints.maxWidth;
return Container(width: localWidth);
},
)- Parent widget'ın boyutunu verir
- Widget ağacında yerel bilgi
- Daha spesifik kontrol
Ne zaman hangisi?
- MediaQuery: Ekran genişliğine/yüksekliğine göre genel kararlar
- LayoutBuilder: Parent container'a göre özel boyutlandırma
textScaleFactor (Metin Ölçeği)
Kullanıcının sistem ayarlarındaki metin boyutu çarpanı:
final textScaleFactor = MediaQuery.of(context).textScaleFactor;
// Örnek: Kullanıcı sistemde metni %150 büyüttüyse
// textScaleFactor = 1.5Metin boyutlarınızı buna göre ayarlayın:
Text(
'Responsive metin',
style: TextStyle(
fontSize: 16 * MediaQuery.of(context).textScaleFactor,
),
)Performans İpuçları
- MediaQuery.of() çağrısını minimize edin:
// ❌ Kötü
Widget build(BuildContext context) {
return Column(
children: [
Container(width: MediaQuery.of(context).size.width * 0.5),
Container(width: MediaQuery.of(context).size.width * 0.3),
Container(width: MediaQuery.of(context).size.width * 0.2),
],
);
}
// ✅ İyi
Widget build(BuildContext context) {
final screenWidth = MediaQuery.of(context).size.width;
return Column(
children: [
Container(width: screenWidth * 0.5),
Container(width: screenWidth * 0.3),
Container(width: screenWidth * 0.2),
],
);
}Gereksiz rebuild'lerden kaçının: MediaQuery değiştiğinde widget rebuild olur.
Cache değerleri: Hesaplanan değerleri değişkende saklayın.
Özet
MediaQuery:
- Ekran boyutlarını verir (
size.width,size.height) - Safe area bilgisi sağlar (
padding) - Klavye durumunu algılar (
viewInsets) - Yönelimi kontrol eder (
orientation) - Tema bilgisi verir (
platformBrightness) - Piksel yoğunluğunu sağlar (
devicePixelRatio)
Oransal Tasarım İpuçları:
- Genişlik için:
screenWidth * 0.x - Yükseklik için:
screenHeight * 0.x - Standart oranlar: 0.1 (%10), 0.5 (%50), 0.9 (%90)
- Padding: Ekranın %3-5'i
- Font: Genişliğin %4-6'sı (başlıklar için)
Best Practices:
- Helper fonksiyonlar oluşturun
- Değerleri cache'leyin
- Breakpoint'lerle birlikte kullanın
- Her cihazda test edin
MediaQuery, Flutter'da cihaz özelliklerine uygun responsive uygulamalar yapmanın temel taşıdır!