كيف تجعل واجهات تطبيقاتك في Flutter متجاوبة مع جميع الشاشات باستخدام MediaQuery و GridView
إذا كنت مطور تطبيقات باستخدام Flutter، فلا بد أنك واجهت مشكلة توافق التصميم مع أحجام الشاشات المختلفة. فالهاتف الصغير قد يعرض الواجهة بشكل مختلف تماماً عن الجهاز اللوحي أو شاشة الكمبيوتر. هنا تظهر أهمية كتابة أكواد مرنة تستجيب لحجم الشاشة وتتكيف مع جميع الأجهزة.
في هذا المقال سنتناول بالشرح والتحليل كود عملي مكتوب بلغة Dart داخل إطار عمل Flutter، يوضح كيفية حساب أبعاد الشاشة ديناميكياً باستخدام MediaQuery، بالإضافة إلى إنشاء شبكة (GridView) متجاوبة تتحكم في عدد الأعمدة وفقاً لحجم الشاشة.
كلاس Dimensions لحساب الأبعاد ديناميكياً
الكود التالي يُعتبر أداة مساعدة (Utility Class) تسهّل على المطور التعامل مع MediaQuery دون الحاجة لكتابته مراراً وتكراراً
Dimensions.dart
class Dimensions {
static double screenHeight(BuildContext context) =>
MediaQuery.of(context).size.height;
static double screenWidth(BuildContext context) =>
MediaQuery.of(context).size.width;
static double heightPercentage(BuildContext context, double percentage) =>
screenHeight(context) * percentage / 100;
static double widthPercentage(BuildContext context, double percentage) =>
screenWidth(context) * percentage / 100;
static double fontSize(BuildContext context, double percentage) =>
screenHeight(context) * percentage / 100;
static double radius(BuildContext context, double percentage) =>
screenHeight(context) * percentage / 100;
}ما الذي يقوم به هذا الكود؟
screenHeight / screenWidth: يعيدان الطول والعرض الكلي للشاشة.
heightPercentage / widthPercentage: تسمح لك بالحصول على نسبة مئوية من الطول أو العرض. على سبيل المثال: إذا أردت زر يأخذ 10% من طول الشاشة.
fontSize: يمكنك استخدامه لجعل حجم الخطوط متجاوباً مع حجم الشاشة.
radius: مفيد لتحديد نصف قطر الحواف (border radius) بشكل نسبي للشاشة.
لماذا هذا مهم؟
لأن التصميم الثابت (Static Design) قد يبدو جيداً على جهاز واحد، لكنه ينهار على شاشات أخرى. أما التصميم النسبي (Responsive Design) فيجعل التجربة موحدة واحترافية عبر مختلف الأجهزة.
حساب عدد الأعمدة تلقائياً داخل GridView
الكود التالي يوضح كيف يمكننا التحكم في عدد الأعمدة (CrossAxisCount) داخل شبكة GridView اعتماداً على عرض الشاشة
@override
Widget build(BuildContext context) {
int calculateCrossAxisCount(BuildContext context) {
final screenWidth = Dimensions.screenWidth(context);
if (screenWidth > 1200) {
return 4;
} else if (screenWidth > 768) {
return 3;
} else {
return 2;
}
}
List<model> myModel = [];
return Scaffold(
appBar: AppBar(
title: const Text('Attendance Management'),
centerTitle: true,
),
body: Padding(
padding: EdgeInsets.all(Dimensions.widthPercentage(context, 5)),
child: GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: calculateCrossAxisCount(context),
mainAxisSpacing: Dimensions.heightPercentage(context, 1),
crossAxisSpacing: Dimensions.widthPercentage(context, 1),
childAspectRatio: 0.7,
),
itemCount: myModel.length,
itemBuilder: (context, index) {
final item = myModel[index];
return AttendanceCard(
title: item.title,
icon: item.icon,
description: item.description,
widget: item.widget,
);
},
),
),
);
}شرح الكود:
calculateCrossAxisCount: إذا كان عرض الشاشة أكبر من 1200 بكسل ⇒ 4 أعمدة. إذا كان العرض بين 768 و1200 ⇒ 3 أعمدة. إذا كان أقل من 768 ⇒ عمودان فقط.
Padding: يتم حساب الهوامش (Margins) بشكل نسبي باستخدام Dimensions.widthPercentage. GridView.builder: ينشئ شبكة ديناميكية تعتمد على عدد العناصر في المصفوفة myModel. SliverGridDelegateWithFixedCrossAxisCount: تتحكم في تخطيط العناصر داخل الشبكة (Spacing + AspectRatio).
المميزات العملية لهذا النهج مرونة كاملة: واجهتك تعمل بشكل ممتاز على الموبايل، التابلت، وحتى شاشات سطح المكتب. قابلية إعادة الاستخدام: يمكن استدعاء Dimensions في أي مكان داخل التطبيق دون تكرار الكود. تحسين تجربة المستخدم (UX): لأن المحتوى يتوزع بشكل منظم مهما كان الجهاز.
إن استخدام MediaQuery بشكل مباشر في كل مكان داخل تطبيقك قد يكون مرهقاً وغير منظم. لذلك يُعتبر إنشاء كلاس مساعد مثل Dimensions خطوة احترافية تجعل مشروعك أكثر نظافة (Clean Code) وأكثر مرونة. ومع الجمع بينه وبين GridView المتجاوبة، يمكنك بناء تطبيقات Flutter بمستوى عالٍ من الجودة ينافس التطبيقات العالمية.


